1 / 23

# Odds and Ends in Haskell: Folding, I/O, and Functors - PowerPoint PPT Presentation

Odds and Ends in Haskell: Folding, I/O, and Functors. Adapted from material by Miran Lipovaca. The foldl function. We’ve seen a particular pattern quite often with lists: - base case on empty list - some operation with the head, plus a recursive call on the tail

I am the owner, or an agent authorized to act on behalf of the owner, of the copyrighted work described.

Odds and Ends in Haskell: Folding, I/O, and Functors

Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author.While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server.

- - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - -

#### Presentation Transcript

Odds and Ends

Folding, I/O, and Functors

Adapted from material by Miran Lipovaca

### The foldl function

We’ve seen a particular pattern quite often with lists:

- base case on empty list

- some 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”

Example: implementing the sum function

sum'::(Numa)=>[a]->a

sum'xs=foldl(\accx-> acc +x)0 xs

The binary function is applied to the accumulator and the first element (in foldl), and produces a new accumulator. Then called again with the new accumulator and the new first element of the list, until the rest of the list is empty.

ghci>sum'[3,5,2,1]

11

In fact, we can write this function in an even shorter way, since functions can be returned as parameters:

sum'::(Numa)=>[a]->a

sum'=foldl(+)0

The lambda function on the previous slide is really the same as (+), and we can omit xs because the function written above will just return a function that takes a list as input.

Another example: elem

Returns True if the variable is present in the list

elem'::(Eqa)=>a->[a]->Bool

elem'yys=foldl

(\accx>ifx==ythenTrueelseacc)

Falseys

• - Starting value and accumulator are booleans.

• Start (and default) is False, which makes sense.

• Check if current element is what we want. If so,

• done (so return True). Otherwise, accumulator is unchanged, and it continues on with the tail.

4

Other functions:

- Foldr is the same, except starts with the end of the list (and accumulator is the second input to the function).

- Scanl and scanr work just the same, but return all intermediate accumulator values in a list.

- foldl1 and foldr1 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.

### File I/O

So far, we’ve worked mainly at the prompt, and done very little true input or output. This is logical in a functional language, since nothing has side effects!

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.

A simple example: save the following file as helloword.hs

main=putStrLn"hello,world"

Now we actually compile a program:

\$ghc--makehelloworld

[1of1]CompilingMain

(helloworld.hs,helloworld.o)

\$./helloworld

hello,world

7

What are these functions?

ghci>:tputStrLn

putStrLn::String->IO()

ghci>:tputStrLn"hello,world"

putStrLn"hello,world"::IO()

So putStrLn takes a string and returns an I/O action (which has a result type of (), the empty tuple).

In Haskell, an I/O action is one with a side effect - usually either reading or printing. Usually some kind of a return value, where () is a dummy value for no return.

8

An I/O action will only be performed when you give it the name “main” and then run the program.

A more interesting example:

main=do

putStrLn"Hello,what'syourname?”

name<-getLine

putStrLn("Hey"++name++",

yourock!")

Notice the do statement - more imperative style.

Each step is an I/O action, and these glue together.

9

More on getLine:

ghci>:tgetLine

getLine::IOString

This is the first I/O we’ve seen that doesn’t have an empty tuple type - it has a String.

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

Notice this is the first non-functional action we’ve seen, since this function will NOT have the same value every time it is run! This is called “impure” code.

10

An invalid example:

nameTag="Hello,mynameis"++getLine

What’s the problem? Well, ++ requires both parameters to have the same type.

What is the return type of getLine?

Another word of warning: what does the following do?

name=getLine

11

• Just remember that I/O actions are only performed in a few possible places:

• 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).

• At the ghci prompt:

ghci>putStrLn"HEEY"

HEEY

12

You can use let statements inside do blocks, to call other functions (and with no “in” part required):

importData.Char

main=do

putStrLn"What'syourfirstname?"

firstName<-getLine

putStrLn"What'syourlastname?"

lastName<-getLine

letbigFirstName=maptoUpperfirstName

bigLastName=maptoUpperlastName

putStrLn\$"hey"++bigFirstName++""++

bigLastName++",howareyou?"

Note that <- is for I/O, and let for expressions.

13

Return in haskell: NOT like other languages.

main=do

line<-getLine

ifnullline

thenreturn()

elsedo

putStrLn\$reverseWordslinemain

reverseWords::String->String

reverseWords=unwords

mapreverse.words

14

What is return?

Does NOT signal the end of execution! Return instead makes an I/O action out of a pure value.

main=do

a<-return"hell"

b<-return"yeah!"

putStrLn\$a++""++b

In essence, return is the opposite of <-. Instead of “unwrapping” I/O Strings, it wraps them.

15

• Other I/O functions:

• print (works on any type in show, but calls show first)

• putStr - And as putStrLn, but no newline

• putChar and getChar

main=doprintTrue

print2

print"haha"

print3.2

print[3,4,3]

main=do

c<-getChar

ifc/=''

thendo

putCharc

main

elsereturn()

16

importData.Char

main=forever\$do

putStr"Givemesomeinput:"

l<-getLine

putStrLn\$maptoUpperl

(Will indefinitely ask for input and print it back out capitalized.)

17

### Functors

Functors are a typeclass, just like Ord, Eq, Show, and all the others. This one is designed to hold things that can be mapped over; for example, lists are part of this typeclass.

classFunctorfwhere

fmap::(a->b)->fa->fb

This type is interesting - not like previous exmaples, like in EQ, where (==) :: (Eq a) => a -> a -> Bool. Here, f is NOT a concrete type, but a type constructor that takes one parameter.

18

Compare fmap to map:

fmap::(a->b)->fa->fb

map :: (a -> b) -> [a] -> [b]

So map is a lot like a functor! Here, map takes a function and a list of type a, and returns a list of type b.

In fact, can define map in terms of fmap:

instanceFunctor[]where

fmap=map

19

Notice what we wrote:

instanceFunctor[]where

fmap=map

We did NOT write “instance Functor [a] where…”, since f has to be a type constructor that takes one type.

Here, [a] is already a concrete type, while [] is a type constructor that takes one type and can produce many types, like [Int], [String], [[Int]], etc.

20

Another example:

instanceFunctorMaybewhere

fmapf(Justx)=Just(fx)

fmapfNothing=Nothing

Again, we did NOT write “instance Functor (Maybe m) where…”, since functor wants a type constructor.

Mentally replace the f’s with Maybe, so fmap acts like (a -> b) -> Maybe a -> Maybe b.

If we put (Maybe m), would have (a -> b) -> (Maybe m) a -> (Maybe m) b, which looks wrong.

21

Using it:

ghci>fmap(++"HEYGUYSIMINSIDETHE

JUST")(Just"Somethingserious.")

Just"Somethingserious.HEYGUYSIMINSIDETHEJUST"

ghci>fmap(++"HEYGUYSIMINSIDETHE

JUST")Nothing

Nothing

ghci>fmap(*2)(Just200)

Just400

ghci>fmap(*2)Nothing

Nothing

22