我正在尝试Cont monad,并发现了以下问题。
代码如下所示:
let inff = map (return :: a -> Cont r a) [0..]
let seqf = sequence inff
runCont seqf head
这是Haskell中Cont monad实现的限制吗? 如果是这样,我们如何改进呢?
答案 0 :(得分:13)
原因是即使sequence someList
的head元素的值仅取决于someList
的第一个元素,效果 sequence someList
的{{1}}通常可以取决于someList
的所有效果(对大多数monad来说都是如此)。因此,如果我们想要评估head元素,我们仍然需要评估所有效果。
例如,如果我们有一个Maybe
值列表,则只有sequence someList
的所有元素都为Just
时,someList
的结果才为Just
。因此,如果我们尝试sequence
无限列表,我们需要检查其无限数量的元素,如果它们都是Just
。
同样适用于Cont
。
在continuation monad中,我们可以随时从计算中转义并返回与到目前为止计算的结果不同的结果。
请考虑以下示例:
test :: (Num a, Enum a) => a
test = flip runCont head $
callCC $ \esc -> do
sequence (map return [0..100] ++ [esc [-1]])
或直接使用cont
代替callCC
:
test' :: (Num a, Enum a) => a
test' = flip runCont head $
sequence (map return [0..100] ++ [cont (const (-1))])
test
的结果只是-1
。在处理前100个元素之后,最终元素可以决定逃避所有这些并返回-1
。因此,为了查看head
中sequence someList
的{{1}}元素是什么,我们再次需要计算它们。
答案 1 :(得分:6)
这不是Cont
monad与sequence
一样的缺陷。您可以为Either
获得类似的结果,例如:
import Control.Monad.Instances ()
xs :: [Either a Int]
xs = map Right [0..] -- Note: return = Right, for Either
ys :: Either a [Int]
ys = sequence xs
在计算整个列表之前,您无法检索ys
的任何元素,这将永远不会发生。
另请注意:sequence (map f xs) = mapM f xs
,因此我们可以将此示例简化为:
>>> import Control.Monad.Instances
>>> mapM Right [0..]
<Hangs forever>
有一些monad mapM
将在无限的值列表上工作,特别是懒惰的StateT
monad和Identity
,但它们是规则的例外。
通常,mapM
/ sequence
/ replicateM
(没有尾随下划线)是反模式,正确的解决方案是使用pipes
,这可以让您构建有效的不尝试预先计算所有结果的流。 The beginning of the pipes
tutorial描述了如何更详细地解决这个问题,但一般的经验法则是,只要你写下这样的内容:
example1 = mapM f xs
example2 = sequence xs
只需将其转换为:
,即可将其转换为惰性Producer
example1' = each xs >-> Pipes.Prelude.mapM f
example2' = each xs >-> Pipes.Prelude.sequence
将上述示例与Either
一起使用,您可以写:
>>> import Pipes
>>> let xs = each [0..] >-> mapM Right :: Producer Int (Either a) ()
然后你可以懒惰地处理流而不生成所有元素:
>>> Pipes.Prelude.any (> 10) xs
Right True