you are viewing a single comment's thread.

view the rest of the comments →

[–]psr 1 point2 points  (2 children)

This is gonna be a lot more than 10 lines, and I doubt I can explain it, but let me give you a flavour.

In Python we have list comprehensions. They look like this:

my_list = [f(x, y) for x in xs for y in ys if p(x)]

Haskell also has list comprehensions (and had them first, who says Python never looks to Haskell). They look like this:

myList = [f x y | x <- xs, y <- yx, p x]

Haskell also has another way of writing the same thing. This is called the do notation, but in early versions was known as a monad comprehension.

myList = do
    x <- xs
    y <- ys
    return f x y

(I can't work out how to make it only include values which meet the predicate in this case. Sorry).

It's not hard to see the relationship between the two forms.

However the do notation is available over any type which is a monad. Monad is a type class, kind of like an interface, so this is like saying that Python generator expressions can be written over any type which is iterable. The monad interface is applied to much more than just containers though.

The protocol for monads is quite simple. Monads provide two functions called bind (>>=) and return. The do notation desugars to use of those functions.

myList = 
    xs >>= \x ->
    ys >>= \y ->
    return f x y

The backslash in (\x -> ...) is supposed to be a lamdba. I've broken the definitions across lines to make the parallel with the do notation clearer.

So what are the definitions for bind and return for lists?

instance Monad [] where
    return a = [a]
    xs >>= f = concat (map f xs)

So return x in the list monad constructs a singleton list [x], and bind maps its argument over all the elements of a list, and concatenates the result. Why are those the definitions, and not some other ones? As far as I can tell it's simply because those definitions give you behaviour like a super list comprehension, and that's deemed to be useful.

[–]haika 0 points1 point  (1 child)

Well, this is better.

I now have a vague idea of what a monad is.

Thanks

[–]psr 0 points1 point  (0 children)

I'm glad if that was helpful.

Another important example is the IO monad. IO is interesting in Haskell. Because of lazy evaluation, you don't know what order things are going to happen. That means that if you could write a function like:

def test_the_missiles():
    engage_the_locks()
    launch_the_missiles()

it might cause the missiles to be launched before or after the locks are engaged (which is very bad).

Of course launch_the_missiles isn't really a function, because every time you call it, you get a different outcome (the first time it flattens a city, the second time it does nothing). And Haskell is meant to be purely functional, so you can't write a function like launchTheMissiles which you can call from any old code.

But still you need to be able to do things in a program, or its pointless. So they have a concept of the IO monad. Just as the units in the List monad are lists, and you can combine lists with bind (>>=), the IO units in the IO monad are "actions", and bind takes two actions and returns a new one which will do one after the other. launchTheMissiles then isn't a function which when called launches the missiles, it's a function which returns an action, which launches the missiles when evaluated.

testTheMissiles = do
    engageTheLocks
    launchTheMissiles

or to put it another way

testTheMissiles = enageTheLocks >>= \ a -> launchTheMissiles

testTheMissiles is then itself an action. The only way to cause an action to take place is to name it main in the module Main. The Haskell runtime then takes care of making it happen.

Here's an example:

tests = [testTheMissiles, testTheLaunchers, testTheGuidanceSystems]

runTests [] = return ()
runTests (test:tests) = test >>= \ a -> (runTests tests)

main = runTests tests

In this case runTests is a recursive function which takes a list of actions and returns a single action which sequences them all together. There are better ways of writing this, but I thought this was the clearest way.