如何懒惰地在 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 = IdentityIdentity m >>= f = f m,所以

buildListM 0 = Identity []
buildListM n = Identity . (n :) $ runIdentity (buildListM (n - 1))
             = Identity (n : runIdentity (buildListM (n - 1)))

由于 IdentityrunIdentity 在 运行 时完全没有操作, buildListM 实际上与 buildList 在 [=56= 时相同] 在 Identity 单子中。使事情变得严格的是特定的 monad,而不是 monadic 性质。

您有时可以通过以下两种方式之一在 "strict" monad 中做一些懒惰的事情:

  1. 使用unsafeInterleaveIOunsafeInterleaveST作弊。

  2. 使用更强大的 MonadFix 抽象,它可以让您在计算执行之前掌握计算结果,但是如果您在计算执行之前访问这样的结果,您将失败可用。