Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Understanding scoping with haskell monads

I'm trying to understand how the scope works in do blocks.

If I have the following code:

l = [1, 2, 3]
m = [1, 2]

then this works fine

res = do
    a <- l
    b <- m
    return (a, b)

And return the Cartesian product of m and l.

To understand the scope I tried to rewrite this in a different form (without do blocks)

I know that do blocks are just syntactic sugar over monadic operations so I tried to "unsugar" it and by using this and came up with this:

res = l >>= (\a -> m) >>= (\b -> return (a, b))

Strangely I get this error Not in scope: ‘a’.

Can anyone tell me where I did it wrong and possibly, how the scoping works because it really looks like magic that the return in the do block is able to access a ?

Thank you very much

like image 941
DARK_DUCK Avatar asked Nov 30 '22 16:11

DARK_DUCK


2 Answers

The issue is that the scope of the lambda in your code is not quite right. It should extend all the way to the end of the expression, not just around the small computation. Your code should desugar to

 l >>= (\a -> m >>= (\b -> return (a, b))

You can drop the parentheses by the way, which makes it a little more pleasant.

 l >>= \a -> m >>= \b -> return (a, b)

but this kind of obscures the meaning. If you want to be painfully explicit, we can convert to prefix notation and say

 bind a f = a >>= f
 bind l (\a -> bind m (\b -> return (a, b))

Perhaps stripping away some of the operator sugar helps.

Note how the >>='s nest inside the lambda, not around it. This ensures that a remains in scope. In fact, this nesting is a little bit of a pain to write out by hand, that's part of the impetus for do-notation :)

like image 124
Daniel Gratzer Avatar answered Dec 03 '22 04:12

Daniel Gratzer


The problem is with your parentheses. You write

res = l >>= (\a -> m) >>= (\b -> return (a, b))

but what you need is

res = l >>= (\a -> m >>= (\b -> return (a, b)))

which can also be written

res = l >>= \a -> m >>= \b -> return (a, b)

You ended the lambda expression binding a prematurely, and then tried to use a.

like image 30
dfeuer Avatar answered Dec 03 '22 04:12

dfeuer