4

I just started learning haskell a few weeks ago and I saw this:

moves = do f <- [(+), subtract] g <- [(+), subtract] (x, y) <- [(1, 2), (2, 1)] [f x *** g y] 

I haven't seen a do block ending in a list before, it was part of a solution to the knights tour problem.. Can somebody explain how it works?

2
  • 3
    This code is equivalent to moves = [f x *** g y | f <- [(+), subtract], g <- [(+), subtract], (x, y) <- [(1, 2), (2, 1)]] Commented Oct 30, 2014 at 17:39
  • 1
    In the list monad [x] = return x. Commented Oct 30, 2014 at 18:38

1 Answer 1

6

You have to desugar the notation. First start by writing down the types:

import Control.Arrow moves :: [(Integer, Integer) -> (Integer, Integer)] moves = do f <- [(+), subtract] g <- [(+), subtract] (x, y) <- [(1, 2), (2, 1)] [f x *** g y] 

So we are in the [] (list) monad.

Lookup the definition of Monad []:

instance Monad [] where m >>= k = foldr ((++) . k) [] m m >> k = foldr ((++) . (\ _ -> k)) [] m return x = [x] 

And translate the do notation to bind and return:

moves = [(+), subtract] >>= \f -> [(+), subtract] >>= \g -> [(1, 2), (2, 1)] >>= \(x,y) -> [f x *** g y] 

Then, finally, rewrite the binds in terms of their definitions:

By return on list

moves = [(+), subtract] >>= \f -> [(+), subtract] >>= \g -> [(1, 2), (2, 1)] >>= \(x,y) -> return (f x *** g y) 

Definition of >>=

moves = foldr ((++) . (\f -> [(+), subtract] >>= \g -> [(1, 2), (2, 1)] >>= \(x,y) -> return (f x *** g y) )) [] [(+), subtract] 

Definition of >>=

moves = foldr ((++) . (\f -> foldr ((++) . (\g -> [(1, 2), (2, 1)] >>= \(x,y) -> return (f x *** g y)) ) [] [(+), subtract] )) [] [(+), subtract] 

Definition of >>=

moves = foldr ((++) . (\f -> foldr ((++) . (\g -> foldr ((++) . (\(x,y) -> return (f x *** g y)) ) [] [(1, 2), (2, 1)] )) [] [(+), subtract] )) [] [(+), subtract] 

Undo the return:

moves = foldr ((++) . (\f -> foldr ((++) . (\g -> foldr ((++) . (\(x,y) -> [f x *** g y]) ) [] [(1, 2), (2, 1)] )) [] [(+), subtract] )) [] [(+), subtract] 

So you see its a nested fold over two element lists. Unfolding the folds is left as an exercise for the reader :)

Sign up to request clarification or add additional context in comments.

3 Comments

I already know what's going on in a list-monad do-comprehension, but I find the desugared version with foldr impossible to follow. I don't see how it's supposed to help someone who doesn't already know what's going on.
I wouldn't expect a beginner to understand (++) . (\f -> ...), either. At best, that would arise the question "why doesn't this trigger a type error?".
It would probably be easier to follow if you use concatMap f instead of foldr ((++) . f) []

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.