为什么这个haskell代码不会终止

时间:2015-07-09 04:32:04

标签: haskell lazy-evaluation state-monad

import Control.Monad.State.Lazy

type Queue a = [a]

push :: a -> State (Queue a) ()
push x = state (\xs -> ((),xs++[x]))

pop :: State (Queue a) a
pop = state (\(x:xs) -> (x,xs))

queueManip :: State (Queue Int) Int
queueManip =
  do
    mapM_ push [1..]
    a <- pop
    return a

main :: IO()
main = do
  let (a,_) = runState queueManip []
  print a

mapM_不应该懒惰吗?除了实现队列之外,复杂性不应该是O(1)吗?

因为追加(++)本身就是懒惰......

2 个答案:

答案 0 :(得分:5)

如果我是邪恶的并且使用

怎么办?
push' :: Int -> State (Queue Int) ()
push' 1052602983 = state $ \_ -> ((), []) -- "Muarhar!"
push' x = state $ \xs -> ((),xs++[x])

然后mapM push' [1..]应该清楚地将状态呈现为[1052602983, 1052602984 ..]pop产生1是错误的。但是mapM如果没有首先评估十亿个其他数字,就不可能知道这一点。实际上将它们推向状态与此无关,push可能完全懒惰也无关紧要:mapM至少必须给它一个机会来检查在交付monadic程序流程之前,任何给定的数字。

答案 1 :(得分:1)

请注意do mapM_ push [1..3]; something与:

相同
do push 1; push 2; push 3; something

这应该解释为什么

do mapM_ push [1..]; something

永远无法执行something

如果你看一下州monad的定义:

type State s a = s -> (a, s)

instance Monad (State s) where
  return a = \s -> (a,s)
  m >>= g = wpAB
    where
      wpAB = \s1 -> let (v2, s2) = m s1
                        (v3, s3) = g v2 s2
                    in (v3, s3)

-- (newtypes and such have been removed to declutter the code)

您发现m >>= g的值始终取决于g。与Maybe monad的定义形成对比:

instance Monad Maybe where
    return x = Just x
    (>>=) m g = case m of
                  Nothing -> Nothing
                  Just x  -> g x

其中m >>= g可以独立于g,这解释了Maybe monad如何使do-chain短路。