考虑以下两个几乎相同的功能:
buildList 0 = []
buildList n = n : buildList (n - 1)
buildListM 0 = return []
buildListM n = buildListM (n - 1) >>= return . (n :)
Haskell的懒惰方面允许buildList
生成列表而内存中没有太多开销,因为它生成列表的头部然后构建尾部。
但是monadic版本buildListM
似乎使用了更多的内存,因为n
变得更大,因为它必须首先构建尾部然后在头部前面。
这是在monad中构建列表的好方法,还是有更有效的方法?
答案 0 :(得分:8)
许多Monads
(例如,IO
,严格ST s
,严格State s
)是“严格的”,因为>>=
的左操作数是严格的。其他人,尤其是Identity
但(->) a
,懒惰State s
,懒惰Writer q
,懒惰ST s
,在{{1}的意义上是“懒惰的”它的左操作数不严格。考虑在>>=
monad:
toListM
Identity
此处buildListM 0 = return []
buildListM n = buildListM (n - 1) >>= return . (n :)
和return = Identity
,所以
Identity m >>= f = f m
由于buildListM 0 = Identity []
buildListM n = Identity . (n :) $ runIdentity (buildListM (n - 1))
= Identity (n : runIdentity (buildListM (n - 1)))
和Identity
在运行时是完整的无操作,因此runIdentity
在buildListM
monad中运行时实际上与buildList
相同。这是特别的单子,而不是单子性,使事情变得严格。
你有时可以用两种方式之一在“严格”的monad中做懒惰的事情:
使用Identity
或unsafeInterleaveIO
作弊。
使用更强大的unsafeInterleaveST
抽象,它可以让你在执行计算之前掌握计算结果,但是如果你在可用之前访问这样的结果,那将会失败。