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
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 :)
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
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With