Home > Net >  iteration with the list monad
iteration with the list monad

Time:03-20

I'm having trouble understanding how the iterative behavior of the list monad can be derived from its definition.

instance Monad [] where
  m >>= f  = concatMap f m
  return x = [x]
  fail s   = []

Discussions I've read seem to pass over the question of how >>= creates a control structure, as shown most clearly with do notation:

allEvenOdds :: Int -> [(Int,Int)]
allEvenOdds n = do
  evenValue <- [2,4 .. n]               
  oddValue <- [1,3 .. n]                
  return (evenValue,oddValue)

Is this built in to Haskell, the way I assume the IO monad's interface to actual i/o is?

CodePudding user response:

There's nothing built-in, everything is a simple consequence of the Monad instance you quoted (and, since this example uses do notation, how that desugars to uses of the >>= operator):

allEvenOdds n = do
  evenValue <- [2,4 .. n]               
  oddValue <- [1,3 .. n]                
  return (evenValue,oddValue)

-- desugaring the do notation
allEvenOdds n =
  [2,4 .. n] >>= \evenValue ->
  [1,3 .. n] >>= \oddValue ->
  return (evenValue, oddValue)

-- using the list instance of Monad to replace >>= and return
allEvenOdds n =
  concatMap (\evenValue ->
    concatMap (\oddValue -> [(evenvalue, oddValue)]) [1,3 .. n])
  ) [2,4 .. n]

which you can hopefully easily see "iterates over" both lists and results in a list of all pairs of (even, odd) with values taken from both lists.

At a high level, we can say that the list monad results in iteration simply because concatMap, like map, executes the given function for each element of the list, so it implicitly iterates over the list.

CodePudding user response:

The list instance of the Monad typeclass models nondeterminism: you can see each var <- someList as a for loop like in Python.

The do notation is desugared to [2,4 .. n] >>= (\evenValue -> [1, 3 .. n] >>= (\oddValue -> return (evenValue, oddValue))), so this is equivalent to something in Python like:

    result = []
    for evenValue in range(2, n, 2):
        for oddValue in range(1, n, 2):
            result.append((evenValue, oddValue))

or with list comprehension:

    result = [
        (evenValue, oddValue)
        for evenValue in range(2, n, 2)
        for oddValue in range(1, n, 2)
    ]

This works since for instance Monad [], the expression [2,4 .. n] >>= (\evenValue -> [1, 3 .. n] >>= (\oddValue -> return (evenValue, oddValue))) is thus equivalent to:

concatMap (\evenValue -> [1, 3 .. n] >>= (\oddValue -> return (evenValue, oddValue))) [2,4 .. n]

and thus:

concatMap (\evenValue -> concatMap (\oddValue -> [(evenValue, oddValue)]) [1, 3 .. n]) [2,4 .. n]

But do notation is not "hardwired" to IO: IO is just an instance of Monad that is implemented in such way that one IO action will run before the second one. For lists, it thus is implemented in an equivalent way as spanning Python for loops.

  • Related