让我们从我们正在实现的类型开始:
(<*>) :: Monad f => f (a -> b) -> f a -> f b
(正常类型<*>
当然有一个Applicative
约束,但这里我们尝试使用Monad
实施Applicative
)
So in fs <*> as = _
, fs
是“函数 f”(f (a -> b)
), and as
是一个“f”a
s".
我们将从绑定开始fs
:
(<*>) :: Monad f => f ( a -> b) -> f a -> f b
fs <*> as
= fs >>= _
如果你真的编译它,GHC 会告诉我们这个洞的类型(_
) has:
foo.hs:4:12: warning: [-Wtyped-holes]
• Found hole: _ :: (a -> b) -> f b
Where: ‘a’, ‘f’, ‘b’ are rigid type variables bound by
the type signature for:
(Main.<*>) :: forall (f :: * -> *) a b.
Monad f =>
f (a -> b) -> f a -> f b
at foo.hs:2:1-45
这就说得通了。莫纳德的>>=
需要一个f a
左边还有一个函数a -> f b
在右边,所以通过绑定f (a -> b)
左边的函数在右边得到一个(a -> b)
函数“提取”自fs
。假设我们可以编写一个函数,可以使用它来返回f b
,那么整个绑定表达式将返回f b
我们需要满足类型签名<*>
.
所以它看起来像:
(<*>) :: Monad f => f ( a -> b) -> f a -> f b
fs <*> as
= fs >>= (\f -> _)
我们在那里能做什么?我们有f :: a -> b
,我们还有as :: f a
,我们需要做一个f b
。如果你习惯了Functor
这是显而易见的;只是fmap f as
. Monad
暗示Functor
,所以这实际上有效:
(<*>) :: Monad f => f ( a -> b) -> f a -> f b
fs <*> as
= fs >>= (\f -> fmap f as)
我认为这也是一个much更容易理解的方式Applicative
可以一般使用以下设施来实现Monad
.
So why is your example written using another >>=
and pure
instead of just fmap
? It's kind of harkening back to the days when Monad
did not have Applicative
and Functor
as superclasses. Monad
always "morally" implied both of these (since you can implement Applicative
and Functor
using only the features of Monad
), but Haskell didn't always require there to be these instances, which leads to books, tutorials, blog posts, etc explaining how to implement these using only Monad
. The example line given is simply inlining the definition of fmap
in terms of >>=
and pure
(return
)1.
我会继续打开包装,就好像我们没有一样fmap
,这样它就会导致您感到困惑的版本。
如果我们不打算使用fmap
结合f :: a -> b
and as :: f a
,那么我们需要绑定as
这样我们就有了一个类型的表达式a
申请f
to:
(<*>) :: Monad f => f ( a -> b) -> f a -> f b
fs <*> as
= fs >>= (\f -> as >>= (\a -> _))
在那个洞里我们需要做一个f b
,我们有f :: a -> b
and a :: a
. f a
给我们一个b
,所以我们需要调用pure
把它变成一个f b
:
(<*>) :: Monad f => f ( a -> b) -> f a -> f b
fs <*> as
= fs >>= (\f -> as >>= (\a -> pure (f a)))
这就是这条线正在做的事情。
- Binding
fs :: f (a -> b)
访问f :: a -> b
- 在可以访问的函数内部
f
它具有约束力as
访问a :: a
- 在可以访问的函数内部
a
(它仍在可以访问的函数内部f
以及),打电话f a
做一个b
,并致电pure
使其成为f b
1 You can implement fmap
using >>=
and pure
as fmap f xs = xs >>= (\x -> pure (f x))
, which is also fmap f xs = xs >>= pure . f
. Hopefully you can see that the inner bind of your example is simply inlining the first version.