折叠、函数组合、单子和惰性,天哪?

发布于 2024-12-18 00:31:20 字数 1446 浏览 2 评论 0原文

我很困惑。我可以这样写:

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,但它们显然不像我想象的那么相似。你能解释一下为什么吗?

I am puzzled. I can write this:

import Control.Monad

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

and the output is 1. That makes sense, because it reduces to:

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

But if I use a vaguely similar monadic technique, it doesn't work the same:

import Control.Monad

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

This hits prelude.Undefined. Which is odd, because I would expect it to reduce:

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

However, flipping the order of composition:

import Control.Monad

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

does accomplish the expected short-circuiting and produces Nothing.

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

I suppose comparing the two approaches might have been comparing apples and oranges, but can you explain the difference? I thought that f <=< g was the monadic analogue to f . g, but they are apparently not as analogous as I thought. Can you explain why?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

ぽ尐不点ル 2024-12-25 00:31:20

这取决于您正在使用哪个 monad,以及它的 (>>=) 运算符是如何定义的。

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

It depends on which monad you're working with, and how its (>>=) operator is defined.

In the case of Maybe, (>>=) is strict in its first argument, as Daniel Fischer explained.

Here are some results for a handful of other monads.

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

Identity: Lazy.

> Control.Monad.Identity.runIdentity foo
42

IO: Strict.

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

Reader: Lazy.

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

Writer: Has both a lazy and a strict variant.

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

State: Has also both a strict and a lazy version.

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

Cont: Strict.

> Control.Monad.Cont.runCont foo id
*** Exception: Prelude.undefined
九公里浅绿 2024-12-25 00:31:20

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

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

因此,当您尝试

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

点击

undefined v >>= \_ -> Nothing

时,实现需要找出 undefined vNothing 还是 Just Something 来查看 的等式(>>=) 使用。

另一方面,

Nothing >>= undefined

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

The bind for Maybe is strict in the first argument.

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

So when you try

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

you hit

undefined v >>= \_ -> Nothing

and the implementation needs to find out whether undefined v is Nothing or Just something to see which equation of (>>=) to use.

On the other hand,

Nothing >>= undefined

determines the result without looking at the second argument of (>>=).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文