简体   繁体   中英

Haskell: variable scope in list monad

I was reading an online Haskell book about list monad. In the book, the list monad was defined like this:

instance Monad [] where  
    return x = [x]  
    xs >>= f = concat (map f xs)  
    fail _ = []

And then there was an example of list monad usage like this:

Prelude> [1,2] >>= \n -> ['a','b'] >>= \ch -> return (n,ch)  
[(1,'a'),(1,'b'),(2,'a'),(2,'b')]

I'm new to Haskell and my question about the example is, how come the variable 'n' is available inside the lambda expression of return (n,ch) . n is defined in the other lambda expression and I don't understand why a variable defined in one lambda expression is available in subsequent lambda expressions. I tried converting the example like below according to the list monad definition:

Prelude> concat (map (\ch -> return (n, ch)) (concat (map (\n -> ['a', 'b']) [1, 2])))

<interactive>:32:29: error: Variable not in scope: n

But as you can see, I got an error saying that the variable n is not available in the scope of the other lambda expression. Maybe the book just presented a simplified version of list monad definition?

[1,2] >>= \n -> ['a','b'] >>= \ch -> return (n,ch)  

is NOT parsed as

[1,2] >>= (\n -> ['a','b']) >>= (\ch -> return (n,ch))

but as

[1,2] >>= \n -> (['a','b'] >>= (\ch -> return (n,ch)))

Your translation with concat / map reflects the "wrong" parsing. We can adapt that to the correct one.

The first >>= becomes

concat (map (\n -> ???) [1,2])

and now we can translate the inner >>= replacing ??? as needed:

??? = concat (map (\ch -> ???2) ['a','b'])
???2= return (n,ch)

Result:

concat (map (\n -> concat (map (\ch -> return (n,ch)) ['a','b'])) [1,2])

Because your expression:

[1,2] >>= \n -> ['a','b'] >>= \ch -> return (n,ch)

is equivalent to:

--                  body of the lambda expression
--                _________^_______________________
--               /                                 \
[1,2] >>= (\n -> (['a','b'] >>= \ch -> return (n,ch)))
--        \________________ _________________________/
--                         v
--              entire right operand

So on the right of the first >>= you have written a lambda expression .

The definition of the [] monad is:

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

So you have written:

[1,2] >>= (\n -> (['a','b'] >>= \ch -> return (n,ch)))
 -> concat (map (\n -> (['a','b'] >>= \ch -> return (n,ch)) [1,2])
 -> concat (map (\n -> concat (map (\ch -> return (n,ch)) ['a','b'])) [1,2])
 -> concat (map (\n -> concat (map (\ch -> [(n,ch)]) ['a','b'])) [1,2])

So n is in the scope of the \\ch -> [(n,ch)] expression. And with the last statement, we obtain:

Prelude> concat (map (\n -> concat (map (\ch -> [(n,ch)]) ['a','b'])) [1,2])
[(1,'a'),(1,'b'),(2,'a'),(2,'b')]

Well @chi and @Willem Van Onsem 's answers are perfectly at place yet just for variety i would like to mention that list comprehensions would just be a syntactical sugar for this monadic job as;

Prelude> [(x,y) | x <- [1,2], y <- ['a', 'b']]
[(1,'a'),(1,'b'),(2,'a'),(2,'b')]

However since lists are also instances of applicative functor class in Haskell another nice way of doing this job without touching monad instance would be;

Prelude> (,) <$> [1,2] <*> ['a','b']
[(1,'a'),(1,'b'),(2,'a'),(2,'b')]

I would like to elaborate the latter a little bit,

(,) is in fact a function which takes two arguments and with a type signature; a -> b -> (a,b) . Whereas <$> is the inline form of fmap . So when we apply (,) <$> [1,2] what we get is an applicative functor (a list of functions) like [(,) 1, (,) 2] . Now we can apply the applicative operator <*> which has the type signature Applicative f => f (a -> b) -> fa -> fb In the type signature f shouldn't be confused with function. It designates a functor which happens to be the [] (list) type here. Which says, <*> will unwrap the contained function(s) from the functor and apply it to the provided functor's contained value(s) to return the result(s) of these applications in the same functor type. So obviously since list applications are defined by all elements one to one the result turns out to be a list of tuples of all combinations.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM