HomeVideos

Haskell for Imperative Programmers #17 - Monads

Now Playing

Haskell for Imperative Programmers #17 - Monads

Transcript

303 segments

0:00

today we are going to close the book on

0:02

the theoretical aspects of Haskell

0:05

because today we are going to talk about

0:07

monads now monads for a lot of people

0:12

are this magical thing that nobody can

0:15

explain right and nobody really

0:17

understands but that really isn't the

0:19

case as we can see here when we look at

0:21

the info for monads and if we look at

0:25

the common saying what the minimal

0:27

function is that we need to have for a

0:29

Monat we see that there is only one

0:32

function that we actually need in order

0:35

to have a monad so this means that if we

0:38

understand this one function we should

0:40

be able to understand monads but

0:43

actually we are going to look at three

0:46

functions here we are going to look at

0:48

return the greater greater and the

0:50

greater greater equals but let's make

0:52

another observation right now maybe and

0:56

IO the types and the IO actions that we

0:59

have seen before are monads so this is

1:04

really interesting because we have

1:05

worked with monads before we just didn't

1:08

know okay so let's look at the very

1:13

important thing this greater greater

1:15

equals operator it is also called bind

1:19

and it works like this we get a monad of

1:23

type a and a function a to monnet B and

1:27

then we get a monad B so this is

1:30

interesting because as we can see here

1:32

we get the internal type of the monad so

1:37

in the case of a maybe read a just one

1:41

has the internal value one and an i/o

1:45

action for example also has some

1:48

internal value getline has some internal

1:52

string so this bind operator seems to be

1:55

able to extract that value the question

1:59

is how does it do it well let's look at

2:01

two examples with the maybes a just one

2:05

is just what we expect we have the

2:08

internal value one and then we have this

2:11

anonymous function here that

2:13

gets this one as its ex argument and

2:16

then just puts it back in to adjust why

2:18

not

2:19

but then the interesting thing is a

2:21

nothing with this bind operator gets us

2:25

a nothing now

2:27

this seems to be weird because why is

2:31

that the case shouldn't we get a value

2:32

well no because a nothing doesn't have

2:35

an internal value and of course it

2:38

depends on how you define the spined

2:40

operator but since a nothing encapsulate

2:45

some error state very often you know it

2:48

encapsulates that you have nothing in

2:51

your hands then it shouldn't return

2:54

anything but a nothing okay so now that

2:59

we know that why not write a function

3:02

with this which we will call maybe add

3:04

which takes a maybe of X and a value Y

3:09

and then adds them together the

3:11

important thing is that we still have to

3:14

return a maybe after this right

3:16

otherwise we are not don't have a sound

3:19

type because the bind operator has to

3:21

return a monad so we just return this

3:25

just of the sum of those two values so

3:29

as we can see here if we do some adding

3:32

with a nothing we actually get a nothing

3:34

which is what we want because we cannot

3:36

add to nothing so the error if some

3:40

error happened is propagated and if we

3:43

have a just of one for example and we

3:45

add a 1 to it we get a just 2 so this

3:48

works just as expected from this we can

3:52

build something even crazier where we do

3:54

this with two maybes now we have 2

3:57

maybes that we use the bind operator on

4:00

in order to get the internal values and

4:02

then we sum them together and throw them

4:05

into the just constructor again even

4:08

though I don't have a example here if

4:10

the second argument is nothing we get

4:12

nothing if the first argument is nothing

4:15

we get nothing we only get a just of any

4:18

value if the two values we throw in here

4:22

are just

4:24

okay so now we've seen that we can do it

4:29

like this but remember there was this

4:32

one function in the monads which was

4:37

called return and return should take a

4:40

value and then return the monad of that

4:44

value so maybe also has to have a return

4:48

of course because otherwise maybe

4:50

wouldn't be a monad so we can use return

4:53

here and that is true but now let's look

4:55

at something interesting because the

4:57

type actually changes so the most

5:01

general type that we have now is a monad

5:04

of B's - a monad of B's - a monad of

5:06

beasts now this still works with maybes

5:09

right because instead of a monad you can

5:12

write a maybe because maybe it's a monad

5:14

but now you could also use this on IO

5:18

ends and you can use this on any monad

5:23

that has the internal internal type that

5:26

is in this num type class so you could

5:30

use anything basically you could use a

5:32

reader you could use some some network

5:35

sockets if you get a number out of the

5:38

monad you can use it with this maybe add

5:41

function and now we can think about

5:43

renaming this function leading up to the

5:46

best joke of this whole series we will

5:49

call this function monad with two DS

5:53

because it's a magnetic ad right that

5:57

was worth it okay so let's look at this

6:02

function again and when looking at this

6:04

function again we maybe see that okay if

6:06

we want to now use even more monads if

6:10

we want to have even more arguments this

6:12

syntax becomes really convoluted and a

6:15

bit ugly because we are using this

6:18

operator the spined operator all the

6:20

time with an anonymous function

6:22

definition this is not really the way to

6:24

go is it and no it isn't which is why

6:27

there is an alternative syntax that we

6:30

have already seen and it's the du

6:33

notation because if you have a bar

6:37

where you say well the Monad em gets

6:39

bound to this anonymous function with

6:41

the argument X this is the same in the

6:44

dew notation as saying well X with this

6:47

left arrow M which says nothing but well

6:53

take the value of the internal value of

6:56

M and put it into X and then do

6:58

something else but again remember if

7:01

there is a fault he state in our monad

7:04

so for example if the maybe is nothing

7:06

or even IO has some internal exception

7:10

then we actually because the dew

7:14

notation is nothing but the bind

7:16

operator then we actually propagate this

7:18

error through so we don't have to think

7:21

about errors in this case we always

7:24

think about getting a value but we can

7:27

be sure that if there's an error if for

7:29

example and nothing is returned then

7:31

this is just propagated through so this

7:34

is great because this lets us build pure

7:37

functions that can still handle errors

7:41

and exceptions that happen on the side

7:45

right and using do notation we can

7:48

actually rewrite this a monad function

7:50

and it looks like this and let's just go

7:54

through it we have the Monad x'

7:55

x and y the monads MX and my of course

8:00

and we get those values with x left

8:03

arrow m x and y left arrow and y and

8:06

then we return the sum of those two

8:09

great so maybe let's maybe look at

8:15

something interesting the actual

8:18

implementation of a monad for them maybe

8:21

because it's actually really easy so we

8:25

have this bind operator here and we do a

8:28

matching I don't think I have mentioned

8:31

this in this series but this is also a

8:32

way of doing pattern matching and here

8:36

we match the M to nothing and in this

8:40

case we just return nothing and if we

8:43

have a just of X we apply the function

8:45

to it right this is exactly what binds

8:48

should do and a return

8:50

of any value is just a wealth just of

8:53

that value great so this is how a monad

8:57

can be instantiated in and if you have

9:00

your own type for example for a random

9:02

number generator or for something that

9:04

has to hold a state you can use it just

9:06

like this okay

9:09

so we've talked about the most important

9:11

thing the bind let's talk about this one

9:14

fail what does that do well fail is

9:18

often not implemented and you don't have

9:21

to implement it if you don't want to it

9:23

takes a string and then returns a monad

9:26

now the funny thing is that the default

9:29

implementation is that fail passes the

9:33

string to the function error and error

9:36

doesn't return a monad it actually just

9:38

ends your program right there with an

9:40

exception so yeah fail is used in order

9:45

to have some well error in your program

9:47

pop up for example let's say you do some

9:50

network code and some exception

9:52

shouldn't happen like a socket gets

9:55

closed prematurely for example then you

9:57

can just call fail for example and if

10:00

your monad can handle that if your Monat

10:02

can handle the error code and then

10:04

somehow encode it in its monad that's

10:07

great because then you get a monad but

10:10

if it doesn't implement the fail

10:11

function it will just pass it to error

10:13

and just end the program right there

10:15

okay so that's fail we've talked about

10:18

return and bind let's talk about the

10:20

last one I don't think this has a

10:22

special name at least I didn't find one

10:24

the greater greater so what is the

10:28

greater greater well I will call this

10:30

the anonymous bind or the unbind I don't

10:35

know well let's look at its

10:37

implementation its default

10:38

implementation of never changes it's the

10:41

following m2 n is nothing but binding m

10:46

to an anonymous function where we drop

10:49

the the name for this argument so we

10:54

just ignore the value that we get and

10:56

just continue with whatever we wanted to

10:59

do the important thing is that let's say

11:02

a fault he stayed happened

11:04

M then this faulty state is propagated

11:06

through right so then we don't even go

11:09

into this anonymous function but if

11:11

everything was alright we just ignore

11:14

its value so we can see here that if

11:17

something went wrong right so nothing

11:20

just means something went wrong now if

11:23

something went wrong right at the

11:25

beginning we don't return just one we

11:27

return nothing but if we have something

11:31

like this where like the second case

11:32

where we have a just one so something

11:35

went right and then adjust to then we

11:38

return just two and of course if we have

11:40

just one and we want to return nothing

11:41

we've returned on nothing so what is

11:45

this used for why do we need it well an

11:47

act with the anonymous bind to some

11:51

expression is the same as doing it in

11:54

the du notation with just no regard for

11:57

the value we get back where do we need

12:00

this well for example put string Ln and

12:02

put char in all of those output

12:05

functions that's a typical use case

12:08

where we don't care what the return is

12:11

but we still want to do some error

12:14

checking right because this put string

12:17

Ln could fail and in this case we should

12:20

propagate this error through and this

12:23

function does just that but it's just

12:25

not as messy with putting a name on

12:29

every value that we get because for

12:31

example in the case of put string Ln we

12:33

have an io of the empty tuple and we

12:36

already know what that value is it will

12:38

be the empty tuple so that's a case

12:40

where we just want to ignore that value

12:43

and that's how we do it with this

12:45

operator and now you don't have to

12:47

define this operator again it's just

12:49

automatically defined with the default

12:51

implementation that we have right here

12:54

okay so now we are almost done let's

12:58

talk about the monad loss because there

13:01

are some laws that if you have a monad

13:04

your monad should abide by these laws so

13:09

let's look at this the left identity is

13:11

the following returning a and then a

13:15

bind to K should be the

13:17

as ka because return a should return

13:22

just that the a right it should return a

13:26

monad with the encapsulated value a and

13:29

throw that to the function K so that

13:32

would be the same as just throwing that

13:34

value to the function K right it should

13:37

be the same okay so let's look at the

13:40

right identity where we have some monad

13:43

which we bind to return well of course

13:46

we get the value from that M and then we

13:50

put it into return which in that case

13:52

should just return the monad the

13:55

important thing is that this has to be

13:58

the same M right internal States

14:00

shouldn't change this identity should be

14:05

kept alive so and lastly monads should

14:08

be associative or the bind operator

14:11

should be associative it shouldn't

14:13

matter whether you first find m to some

14:16

function k and then bind that to H or if

14:20

you do it the other way around where you

14:22

say well M is bound to this function

14:25

that takes this argument then you know

14:28

does the

14:28

there's does the actual binding operator

14:31

and this just shouldn't matter the the

14:34

way of of doing this evaluation should

14:39

be irrelevant okay

Interactive Summary

Ask follow-up questions or revisit key timestamps.

The video explains the concept of monads in Haskell, focusing on their theoretical aspects and practical applications. It highlights that understanding the 'bind' operator (>>=) is key to understanding monads. The video illustrates this with examples using the 'Maybe' type, showing how 'bind' handles both 'Just' and 'Nothing' values, and how it can be used to propagate errors. It also introduces other monadic functions like 'return' and the less commonly used 'fail' and '>>'. The 'do' notation is presented as a more readable alternative to chaining bind operations. Finally, the video touches upon the monadic laws (left identity, right identity, and associativity) that any monad should satisfy.

Suggested questions

5 ready-made prompts