如何懒惰地在 monad 中构建 Haskell 列表?
How to build a Haskell list inside a monad lazily?
考虑以下两个几乎相同的函数:
buildList 0 = []
buildList n = n : buildList (n - 1)
buildListM 0 = return []
buildListM n = buildListM (n - 1) >>= return . (n :)
Haskell 的惰性方面允许 buildList
生成列表而无需太多内存开销,因为它生成列表的头部,然后构建尾部。
但是 monadic 版本 buildListM
似乎在 n
变大时使用更多内存,因为它必须先构建尾部然后再添加头部。
这是在 monad 中构建列表的好方法,还是有更有效的方法?
许多Monads
(例如IO
、严格的ST s
、严格的State s
)是"strict",因为>>=
是严格的在它的左操作数中。其他人,最值得注意的是 Identity
,还有 (->) a
、懒惰的 State s
、懒惰的 Writer q
、懒惰的 ST s
,在某种意义上是 "lazy" >>=
的左操作数并不严格。考虑在 Identity
monad 中应用 toListM
:
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
实际上与 buildList
在 [=56= 时相同] 在 Identity
单子中。使事情变得严格的是特定的 monad,而不是 monadic 性质。
您有时可以通过以下两种方式之一在 "strict" monad 中做一些懒惰的事情:
使用unsafeInterleaveIO
或unsafeInterleaveST
作弊。
使用更强大的 MonadFix
抽象,它可以让您在计算执行之前掌握计算结果,但是如果您在计算执行之前访问这样的结果,您将失败可用。
考虑以下两个几乎相同的函数:
buildList 0 = []
buildList n = n : buildList (n - 1)
buildListM 0 = return []
buildListM n = buildListM (n - 1) >>= return . (n :)
Haskell 的惰性方面允许 buildList
生成列表而无需太多内存开销,因为它生成列表的头部,然后构建尾部。
但是 monadic 版本 buildListM
似乎在 n
变大时使用更多内存,因为它必须先构建尾部然后再添加头部。
这是在 monad 中构建列表的好方法,还是有更有效的方法?
许多Monads
(例如IO
、严格的ST s
、严格的State s
)是"strict",因为>>=
是严格的在它的左操作数中。其他人,最值得注意的是 Identity
,还有 (->) a
、懒惰的 State s
、懒惰的 Writer q
、懒惰的 ST s
,在某种意义上是 "lazy" >>=
的左操作数并不严格。考虑在 Identity
monad 中应用 toListM
:
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
实际上与 buildList
在 [=56= 时相同] 在 Identity
单子中。使事情变得严格的是特定的 monad,而不是 monadic 性质。
您有时可以通过以下两种方式之一在 "strict" monad 中做一些懒惰的事情:
使用
unsafeInterleaveIO
或unsafeInterleaveST
作弊。使用更强大的
MonadFix
抽象,它可以让您在计算执行之前掌握计算结果,但是如果您在计算执行之前访问这样的结果,您将失败可用。