折叠,函数组合,单子和惰性编程?

23
我感到困惑。我可以写出以下代码:
import Control.Monad

main = print $ head $ (foldr (.) id [f, g]) [3]
  where f = (1:)
        g = undefined

输出结果是1。这是有道理的,因为它简化为:

main = print $ head $ ((1:) . undefined . id) [3]
main = print $ head $ (1:) ((undefined . id) [3])
main = print $ head $ 1 : ((undefined . id) [3])
main = print $ 1

但是如果我使用类似的单子技术,它不会有相同的效果:

import Control.Monad

main = print $ (foldr (<=<) return [f, g]) 3
  where f = const Nothing
        g = undefined

代码中出现了 prelude.Undefined,这很奇怪,因为我原本期望它会被简化:

main = print $ ((const Nothing) <=< undefined <=< return) 3
main = print $ return 3 >>= undefined >>= (\_ -> Nothing)
main = print $ Nothing -- nope! instead, undefined makes this blow up

然而,反转组合的顺序:

import Control.Monad

main = print $ (foldr (>=>) return [f, g]) 3
  where f = const Nothing
        g = undefined

执行了预期的短路操作并生成了Nothing

main = print $ (const Nothing >=> undefined >=> return) 3
main = print $ (const Nothing 3) >>= undefined >>= return
main = print $ Nothing >>= undefined >>= return
main = print $ Nothing

我想比较这两种方法可能是在比较不同的东西,但你能解释一下它们之间的区别吗? 我认为f <=< g是单子范畴对应于f.g的模拟,但它们显然不像我想象的那么相似。 你能解释一下为什么吗?

2个回答

21

这取决于你正在使用哪个单子,以及它的 (>>=) 操作符的定义方式。

对于 Maybe 来说,(>>=) 在其第一个参数上是严格的,就像 Daniel Fischer 所解释的那样。

以下是其他几个单子的一些结果。

> :set -XNoMonomorphismRestriction
> let foo = (const (return 42) <=< undefined <=< return) 3
> :t foo
foo :: (Num t, Monad m) => m t

身份:懒人。

> Control.Monad.Identity.runIdentity foo
42

IO: 严格模式。

> foo :: IO Integer
*** Exception: Prelude.undefined

读者:懒惰。

> Control.Monad.Reader.runReader foo "bar"
42

作者:有懒惰和严格两个变体。

> Control.Monad.Writer.runWriter foo
(42,())
> Control.Monad.Writer.Strict.runWriter foo
*** Exception: Prelude.undefined

状态: 也有严格和惰性两个版本。

> Control.Monad.State.runState foo "bar"
(42,"*** Exception: Prelude.undefined
> Control.Monad.State.Strict.runState foo "bar"
*** Exception: Prelude.undefined

内容:严格。

> Control.Monad.Cont.runCont foo id
*** Exception: Prelude.undefined

19

Maybe 的绑定在第一个参数上是严格的。

Just v >>= f = f v
Nothing >>= f = Nothing

因此,当您尝试时

Just v >>= undefined >>= \_ -> Nothing

你击中了

undefined v >>= \_ -> Nothing

这个实现需要确定 undefined vNothing 还是 Just something,以便确定使用哪个 (>>=) 的方程。

另一方面,

Nothing >>= undefined

这确定结果而无需查看 (>>=) 的第二个参数。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接