Odds and Ends in Haskell Folding IO and

  • Slides: 23
Download presentation
Odds and Ends in Haskell: Folding, I/O, and Functors Adapted from material by Miran

Odds and Ends in Haskell: Folding, I/O, and Functors Adapted from material by Miran Lipovaca 0

lists: - base case on empty list The function - somefoldl operation with the

lists: - base case on empty list The function - somefoldl operation with the head, plus a recursive call on the tail This is such a common pattern that there is a higher-order function to handle it. Inputs: a function, a initial starting value (which we’ll call the accumulator, although it can have any name) and a list to “fold up” 1

applied to the accumulator Example: implementing the sum function and the first element (in

applied to the accumulator Example: implementing the sum function and the first element (in sum' : : (Num a) => [a] -> a foldl), and sum' xs = foldl (acc x -> acc + x) 0 xs produces a new accumulator. Then called again with the new accumulator and the new ghci> sum' [3, 5, 2, 1] first 11 element of the list, until the rest 2

The lambda In fact, we can write this function in an even shorter function

The lambda In fact, we can write this function in an even shorter function onfunctions can be returned as parameters: way, since the previous slide is really sum' : : (Num a) => [a] -> a the same as sum' = foldl (+) 0 (+), and we can omit xs because the function written above will just return a function that takes a list as input. 3

- Starting value and accumulator are booleans. - Start (and default) is False, which

- Starting value and accumulator are booleans. - Start (and default) is False, which makes sense. Another example: elem - Check if current element is what we want. If so, Returns True if the variable is present in the list done (so return True). Otherwise, accumulator is elem' : : (Eq a) => a -> [a] -> Bool unchanged, and it continues on with the tail. elem' y ys = foldl (acc x > if x == y then True else acc) False ys 4

function). - Scanl and scanr work just the same, but return all intermediate accumulator

function). - Scanl and scanr work just the same, but return all intermediate accumulator values in a list. - foldl 1 and foldr 1 work just the same as foldl and foldr, but don’t need to provide a starting value they assume first (or last) element of the list is the starting value. 5

done very little true input or output. This is logical in a functional language,

done very little true input or output. This is logical in a functional language, since nothing has side File effects!I/O However, this is a problem with I/O, since the whole point is to take input (and hence change some value) and then output something (which requires changing the state of the screen or other I/O device. Luckily, Haskell offers work-arounds that separate the more imperative I/O. 6

A simple example: save the following file as Now we actually compile a program:

A simple example: save the following file as Now we actually compile a program: helloword. hs main = put. Str. Ln "hello , world" $ ghc --make helloworld [1 of 1] Compiling Main ( helloworld. hs, helloworld. o ) Linking helloworld. . . $. /helloworld hello, world 7

(which has a result type of (), the empty tuple). In Haskell, an I/O

(which has a result type of (), the empty tuple). In Haskell, an I/O action is one with a side effect What are these functions? usually ghci> : t put. Str. Ln either reading or printing. Usually some kind put. Str. Ln : : String of a return value, where () is a dummy value for no return. > IO () ghci> : t put. Str. Ln "hel lo, world" put. Str. Ln "hello, world " : : IO () 8

An I/O action will only be performed when you give Notice the do statement

An I/O action will only be performed when you give Notice the do statement - more style. it the name “main” and then runimperative the program. main = do Each step is an I/O action, and these glue together. put. Str. Ln "Hello, w A more hat's your name? ” interesting example: name <- get. Line put. Str. Ln ("Hey " + + name ++ ", you rock!") 9

Once the string is returned, we use the <- to bind the result to

Once the string is returned, we use the <- to bind the result to the specified identifire. More on get. Line: Notice this is the first non-functional action we’ve seen, since this function will NOT have the same ghci> : t get. Line value every time it is run! This is called “impure” get. Line : : IO String code. 10

parameters to have the same type. What is theexample: return type of get. Line?

parameters to have the same type. What is theexample: return type of get. Line? An invalid Another word of warning: what does the following do? name. Tag = "Hello, my n name = get. Line ame is " ++ get. Line 11

- A main function - inside a bigger I/O block that we have composed

- A main function - inside a bigger I/O block that we have composed with a do (and remember that the last action can’t be bound to a name, since that is the one that is the return type). ghci> put. Str. Ln "HEEY" -At the ghci prompt: HEEY 12

main = do put. Str. Ln "What's your You canfirst name? " use let

main = do put. Str. Ln "What's your You canfirst name? " use let statements inside do blocks, to call Note that <- is for I/O, and let for expressions. other functions (and with no “in” part required): first. Name <- get. Line put. Str. Ln "What's your last name? " last. Name <- get. Line let big. First. Name = map to. Upper first. Name big. Last. Name = map to. Upper last. Name put. Str. Ln $ "hey " ++ b ig. First. Name ++ " " ++ big. Last. Name ++ ", ho w are you? " 13

 if null line Return then return () in haskell: NOT like other languages.

if null line Return then return () in haskell: NOT like other languages. else do put. Str. Ln $ reverse. Words line main reverse. Words : : String -> String reverse. Words = unwords map reverse. words 14

What is return? In essence, return is the opposite of <-. Instead of “unwrapping”

What is return? In essence, return is the opposite of <-. Instead of “unwrapping” I/Othe Strings, wraps them. Return Does NOT signal end ofit execution! main = do instead a <makes an I/O action out of a pure value. return "hell" b < return "yeah!" put. Str. Ln $ a ++ " " ++ b 15

Other I/O functions: -print (works on any type in show, but calls show first)

Other I/O functions: -print (works on any type in show, but calls show first) -put. Str - And as put. Str. Ln, but no newline main = do -put. Char and get. Char c <- get. Char main = do print True if c /= ' ' print 2 then do print "haha" put. Char c print 3. 2 main print [3, 4, 3] else return () 16

(Will indefinitely More advanced functionality ask for inputisand available print itinback out import Control.

(Will indefinitely More advanced functionality ask for inputisand available print itinback out import Control. Monad: capitalized. ) import Data. Char main = forever $ do put. Str "Give me so me input: " l <- get. Line put. Str. Ln $ map to. U pper l 17

Functors This type are Functors is interesting a typeclass, - not justlikeprevious Ord, Eq,

Functors This type are Functors is interesting a typeclass, - not justlikeprevious Ord, Eq, exmaples, Show, and in like all. EQ, the where others. (==) This: : one (Eqisa)designed => a ->toa hold -> Bool. thingsfthat Here, is NOT canabe concrete mappedtype, over; but foraexample, type lists are part of this constructor thattypeclass. takes one parameter. class Functor f where fmap : : (a -> b) -> f a -> f b 18

So map is a lot like a functor! Here, map takes a function and

So map is a lot like a functor! Here, map takes a function and a list of type a, and returns a list of Compare fmap to map: type b. In fact, can define map in terms of fmap: fmap : : (a -> b) -> [a] -> [b] > f a -> f b instance Functor [] where fmap = map 19

since f has to be a type constructor that takes one type. Notice what

since f has to be a type constructor that takes one type. Notice what we wrote: Here, [a] is already a concrete type, while [] is a type constructor that takes one type and can instance Functor [] where produce many types, like [Int], [String], [[Int]], etc. fmap = map 20

Mentally replace the f’s with Maybe, so fmap acts Another example: like (a ->

Mentally replace the f’s with Maybe, so fmap acts Another example: like (a -> b) -> Maybe a -> Maybe b. instance Functor Maybe where fmap f (Just x) = Just (f x) If we put (Maybe m), would have (a -> b) -> fmap f Nothing = Nothing (Maybe m) a -> (Maybe m) b, which looks wrong. 21

Using it: ghci> fmap (++ " HEY GUYS IM INSIDE THE JUST") (Just "Something

Using it: ghci> fmap (++ " HEY GUYS IM INSIDE THE JUST") (Just "Something serious. ") Just "Something serious. HEY GUYS IM INSIDE THE JUST" ghci> fmap (++ " HEY GUYS IM INSIDE THE JUST") Nothing ghci> fmap (*2) (Just 200) Just 400 ghci> fmap (*2) Nothing 22