Monad 比 Applicative 更强大?

2024-02-08

我在看过去的讨论 https://stackoverflow.com/questions/23342184/difference-between-monad-and-applicative-in-haskell但不明白为什么任何答案实际上都是正确的。

适用性

<*> :: f (a -> b) -> f a -> f b

Monad

(>>=) :: m a -> (a -> m b) -> m b

所以如果我猜对了,那么声明就是>>=不能仅通过假设存在来写<*>

好吧,假设我有<*>.

我想创造>>=.

所以我有f a.

I have f (a -> b).

现在当你看它的时候,f (a -> b)可以写成(a -> b)(如果某个东西是 x, y , z 的函数 - 那么它也是 x, y 的函数)。

所以从存在<*> we get (a -> b) -> f a -> f b这又可以写成((a -> b) -> f a) -> f b,可以写成(a -> f b).

所以我们有f a, 我们有(a -> f b),我们知道<*>结果是f b,所以我们得到:

f a -> (a -> f b) -> f b

这是一个单子。

实际上,用更直观的语言来说:在实现时<*>,我提取(a -> b) out of f(a -> b),我提取a out of f a,然后我申请(a -> b) on a并得到b我用它包裹f终于得到f b.

所以我几乎做同样的事情来创建>>=。申请后(a -> b) on a并得到b,再执行一步并将其包裹起来f,所以我返回f b,因此我知道我有一个函数(a -> f b).


现在当你看它的时候,f(a -> b)可以写成(a -> b)

不,不能。此时你的直觉(危险地)还很遥远。这就像说锤子非常适合拧入螺丝,因为它已经可以用来钉钉子*。你不能简单地放弃f在这里,它是类型**的一部分。

相反,让我们弄清楚事实。一个Applicative有三个相关的函数,计数Functor's fmap:

fmap  :: Functor f     =>   (a -> b) -> f a -> f b
pure  :: Applicative f =>                 a -> f a
(<*>) :: Applicative f => f (a -> b) -> f a -> f b

这是另一个事实:您可以定义绑定 ((>>=)) 按照join反之亦然:

join :: Monad m => m (m a) -> m a
join k = k >>= id

(>>=) :: Monad m => m a -> (a -> m b) -> m b
k >>= f = join (fmap f k)

您在此处提供的 join 和 bind 的实现是 Monad 定义的一部分,还是仅 join 和 bind 签名是 Monad 定义的一部分? [...] 所以现在我问自己他们为什么要打扰。

当然,这些不是官方定义,因为它们永远不会终止。你必须定义(>>=)对于你的类型,如果你想让它成为一个单子:

instance Monad YourType where
   k >>= f = ...

另外,您的连接定义使用的 id 不在 Monad 接口中,为什么它在数学上是合法的?

首先,id :: a -> a是为任何类型定义的。其次,单子的数学定义实际上是通过join。所以它“更”***合法。但最重要的是,我们可以根据以下方面来定义单子定律:join(锻炼)。

如果我们创建了join via Applicative,我们还可以创建绑定。如果我们无法创造join via Applicative方法,我们也无法得出bind. And join的类型实际上很明显我们不能从中导出它Applicative:

join :: Monad m => m (m a) -> m a
             --    ^  ^       ^

Join 可以删除其中之一m层。让我们检查一下是否可以在其他方法中执行相同的操作:

fmap  :: Functor f     =>   (a -> b) -> f a -> f b
                          ^                    ^
                        0 here              1 here
pure  :: Applicative f =>                 a -> f a
                                          ^  | ^
                                      0 here | 1 here
(<*>) :: Applicative f => f (a -> b) -> f a -> f b
                          ^                    ^
                       1 here                1 here

答案是否定的:我们没有提供任何工具Applicative使我们能够崩溃多个m变成了一个。这也是写在类型分类百科全书 https://wiki.haskell.org/Typeclassopedia#Monad就在另一个问题中引用的段落之后:

为了从不同的角度了解 Monad 增强的功能,让我们看看如果我们尝试实现会发生什么(>>=)按照fmap, pure, and (<*>)。我们被赋予了一个值x类型的m a,和一个函数k类型的a -> m b,所以我们唯一能做的就是申请k to x。当然,我们不能直接应用它;我们必须使用fmap将其举过m。但它的类型是什么fmap k?嗯,这是m a -> m (m b)。所以当我们将其应用到x,我们留下了一些类型m (m b)——但现在我们陷入困境;我们真正想要的是m b,但是没有办法从这里到达那里。我们可以添加m正在使用pure,但是我们没有办法折叠多个m合而为一。

注意join无法摆脱m完全地,这将是一个完全的提取,并且——取决于一些其他功能——一个特征comonad https://stackoverflow.com/q/34837150/1139697。不管怎样,请确保你的直觉不会误入歧途。信任并使用类型。

* That comparison is a little bit bad, because you could actually try to dive a screw in with a hammer into a piece of wood. So think of a plastic screw, a rubber hammer and a carbon steel plate you want to drive the nail in. Good luck.

** Well, you can drop it, but then the type changes drastically.

*** Given that (>>=) and join are equivalent of power and any (>>=) using formula can be transformed to one only using join, they are of course both mathematical sound.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Monad 比 Applicative 更强大? 的相关文章

随机推荐