【问题标题】:Deriving monad bind from applicative (<*>) confusion从应用 (<*>) 混淆派生 monad 绑定
【发布时间】:2019-01-22 17:57:52
【问题描述】:

阅读 Haskell Book 时,我的大脑正在思考以下示例。我真的不知道第 21 行的 flip 函数在做什么

1 class Functor f where
2   fmap :: (a -> b) -> f a -> f b
3
4 class Functor f => Applicative f where
5   pure :: a -> f a
6   (<*>) :: f (a -> b) -> f a -> f b
7
8 class Applicative f => Monad f where
9   return :: a -> f a
10   (>>=) :: f a -> (a -> f b) -> f b
11
12 instance Functor ((->) r) where
13   fmap = (.)
14
15 instance Applicative ((->) r) where
16   pure = const
17   f <*> a = \r -> f r (a r)
18
19 instance Monad ((->) r ) where
20   return = pure
21   ra >>= arb = flip arb <*> ra


-- flip :: (a -> b -> c) -> b -> a -> c

-- ra >>= arb = flip arb <*> ra

据我了解,flip 接受一个函数,该函数接受两个参数,然后是两个单独的参数,并返回一个值。在绑定示例中,我们是否将arb 作为(a -&gt; b -&gt; c) 传递,然后将&lt;*&gt; 作为b 传递给flip 的签名,最后将ra 作为a?我看不到。

我已经尝试使类型更具体到我的实际示例,以便您可以将&lt;*&gt; 重写为

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

我也可以为绑定做同样的事情

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

所以即使是像我这样的傻瓜也可以看到,如果我们可以交换&lt;*&gt; 我们可以像

一样排队
(<???>) :: (r -> a) -> (r -> a -> b) -> (r -> b)
(>>=) ::   (r -> a) -> (a -> r -> b) -> (r -> b)

但是看看那里的第二个参数,第一个想要一个 r 作为它的第一个参数,而 bind 想要一个 a

所以不知何故flip 是本书的示例为我们这样做,但我真的不明白如何。任何帮助将不胜感激。

谢谢!

【问题讨论】:

  • flip f = \arg1 arg2 -&gt; f arg2 arg1 是将a -&gt; r -&gt; b 转换为r -&gt; a -&gt; b 所需的全部内容。在 Haskell 中,函数都是一个值的一个参数(有时该值是另一个函数),这也相当于 flip f arg1 arg2 = f arg2 arg1

标签: haskell monads reader


【解决方案1】:

我认为最重要的混淆点是:flip 正在修改 arb,而不是像您似乎相信的那样修改 &lt;*&gt;。我们已经“修改”了&lt;*&gt;,使其具有“正确”的参数顺序,只需按照与我们得到它们相反的顺序给&lt;*&gt; 提供它的参数!

现在了解详情。正如您所说,我们有:

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

所以,既然我们在左边写了

ra >>= arb = ...

那么我们的作用域是:

ra :: r -> a
arb :: a -> r -> b

(注意如何选择名称以反映类型!)重写您为flip 提供的类型,我们有

flip :: (a -> b -> c) -> b -> a -> c -- original
flip :: (a -> r -> b) -> r -> a -> b -- rename variables

因此:

flip arb :: r -> a -> b

现在回想一下你写的(&lt;*&gt;)的类型:

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

所以对于(&lt;*&gt;) 的第一个参数,我们需要r -&gt; a -&gt; b 类型的东西。嘿! flip arb 有这种类型!对于第二个参数,我们需要r -&gt; a 类型的东西。而且我们又走运了,因为ra 有这种类型,所以...

flip arb <*> ra :: r -> b

(与中缀运算符一样,这​​是运算符(&lt;*&gt;) 参数flip arbra 的应用。)我们希望拥有什么类型?好吧,我们现在回到(&gt;&gt;=)的类型:

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

在接受两个参数后,这应该返回r -&gt; b 类型的东西。很酷,这就是我们构建的。

ra >>= arb = flip arb <*> ra

【讨论】:

  • flip 仅影响arb 而不会影响表达式的其余部分,这是因为函数应用程序与运算符的优先级吗? arb 应用于flip 而不是&lt;*&gt;ra,这是因为将参数应用于翻转以某种方式优先吗?
  • 换句话说,我们为什么不写(flip arb) &lt;*&gt; ra
  • 可能值得指出的是,类似 Reader 的 monad 是唯一可以仅根据应用运算符实现的。
  • @ShaneUnger (flip arb) &lt;*&gt; ra 也可以。是的,前缀函数应用程序比中缀函数应用程序绑定得更紧密,所以即使没有括号,它也意味着同样的事情。
猜你喜欢
  • 2011-12-19
  • 1970-01-01
  • 2014-01-25
  • 2014-03-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-27
  • 2017-10-18
相关资源
最近更新 更多