Is Haskell's mapM not lazy?

前端 未结 5 780
情歌与酒
情歌与酒 2020-12-01 23:36

UPDATE: Okay this question becomes potentially very straightforward.

q <- mapM return [1..]

Why does this never return

5条回答
  •  春和景丽
    2020-12-02 00:16

    Here's an attempt at a proof that mapM return [1..] doesn't terminate. Let's assume for the moment that we're in the Identity monad (the argument will apply to any other monad just as well):

    mapM return [1..] -- initial expression
    sequence (map return [1 ..]) -- unfold mapM
    let k m m' = m >>= \x ->
                 m' >>= \xs ->
                 return (x : xs)
    in foldr k (return []) (map return [1..]) -- unfold sequence
    

    So far so good...

    -- unfold foldr
    let k m m' = m >>= \x ->
                 m' >>= \xs ->
                 return (x : xs)
        go [] = return []
        go (y:ys) = k y (go ys)
    in go (map return [1..])
    
    -- unfold map so we have enough of a list to pattern-match go:
    go (return 1 : map return [2..])
    -- unfold go:
    k (return 1) (go (map return [2..])
    -- unfold k:
    (return 1) >>= \x -> go (map return [2..]) >>= \xs -> return (x:xs)
    

    Recall that return a = Identity a in the Identity monad, and (Identity a) >>= f = f a in the Identity monad. Continuing:

    -- unfold >>= :
    (\x -> go (map return [2..]) >>= \xs -> return (x:xs)) 1
    -- apply 1 to \x -> ... :
    go (map return [2..]) >>= \xs -> return (1:xs)
    -- unfold >>= :
    (\xs -> return (1:xs)) (go (map return [2..]))
    

    Note that at this point we'd love to apply to \xs, but we can't yet! We have to instead continue unfolding until we have a value to apply:

    -- unfold map for go:
    (\xs -> return (1:xs)) (go (return 2 : map return [3..]))
    -- unfold go:
    (\xs -> return (1:xs)) (k (return 2) (go (map return [3..])))
    -- unfold k:
    (\xs -> return (1:xs)) ((return 2) >>= \x2 ->
                             (go (map return [3..])) >>= \xs2 ->
                             return (x2:xs2))
    -- unfold >>= :
    (\xs -> return (1:xs)) ((\x2 -> (go (map return [3...])) >>= \xs2 ->
                            return (x2:xs2)) 2)
    

    At this point, we still can't apply to \xs, but we can apply to \x2. Continuing:

    -- apply 2 to \x2 :
    (\xs -> return (1:xs)) ((go (map return [3...])) >>= \xs2 ->
                             return (2:xs2))
    -- unfold >>= :
    (\xs -> return (1:xs)) (\xs2 -> return (2:xs2)) (go (map return [3..]))
    

    Now we've gotten to a point where neither \xs nor \xs2 can be reduced yet! Our only choice is:

    -- unfold map for go, and so on...
    (\xs -> return (1:xs))
      (\xs2 -> return (2:xs2))
        (go ((return 3) : (map return [4..])))
    

    So you can see that, because of foldr, we're building up a series of functions to apply, starting from the end of the list and working our way back up. Because at each step the input list is infinite, this unfolding will never terminate and we will never get an answer.

    This makes sense if you look at this example (borrowed from another StackOverflow thread, I can't find which one at the moment). In the following list of monads:

    mebs = [Just 3, Just 4, Nothing]
    

    we would expect sequence to catch the Nothing and return a failure for the whole thing:

    sequence mebs = Nothing
    

    However, for this list:

    mebs2 = [Just 3, Just 4]
    

    we would expect sequence to give us:

    sequence mebs = Just [3, 4]
    

    In other words, sequence has to see the whole list of monadic computations, string them together, and run them all in order to come up with the right answer. There's no way sequence can give an answer without seeing the whole list.

    Note: The previous version of this answer asserted that foldr computes starting from the back of the list, and wouldn't work at all on infinite lists, but that's incorrect! If the operator you pass to foldr is lazy on its second argument and produces output with a lazy data constructor like a list, foldr will happily work with an infinite list. See foldr (\x xs -> (replicate x x) ++ xs) [] [1...] for an example. But that's not the case with our operator k.

提交回复
热议问题