作为应用函子的功能(Haskell/LYAH) [英] functions as applicative functors (Haskell / LYAH)
问题描述
Learn You a Haskell 的第 11 章介绍了以下定义:
Chapter 11 of Learn You a Haskell introduces the following definition:
instance Applicative ((->) r) where
pure x = (\_ -> x)
f <*> g = x -> f x (g x)
在这里,作者进行了一些不寻常的挥手(<*> 的实例实现有点神秘,所以最好我们只是 [在行动中展示它而不解释它]").我希望这里有人能帮我弄清楚.
Here, the author engages in some uncharacteristic hand-waving ("The instance implementation for <*> is a bit cryptic, so it's best if we just [show it in action without explaining it]"). I'm hoping someone here might help me figure it out.
根据应用类定义,(<*>)::f(a -> b) ->f a ->f b
在这个例子中,用((->)r)
代替f
:r->(a->b)->(r->a)->(r->b)
In the instance, substituting ((->)r)
for f
: r->(a->b)->(r->a)->(r->b)
所以第一个问题是我如何从那种类型得到 f <*>g = x ->f x (g x)
?
So the first question, is how do I get from that type to f <*> g = x -> f x (g x)
?
但即使我认为最后一个公式是理所当然的,我也很难让它与我提供给 GHCi 的示例一致.例如:
But even if I take that last formula for granted, I have trouble making it agree with examples I give to GHCi. For example:
Prelude Control.Applicative> (pure (+5)) <*> (*3) $ 4
17
这个表达式看起来与 f <*> 一致.g = x ->f(g x)
(注意在这个版本中x
不会出现在f
之后.
This expression instead appears consistent with f <*> g = x -> f (g x)
(note that in this version x
doesn't appear after f
.
我意识到这很乱,所以感谢您的耐心等待.
I realize this is messy, so thanks for bearing with me.
推荐答案
首先,记住 fmap
是如何为应用程序定义的:
First of all, remember how fmap
is defined for applicatives:
fmap f x = pure f <*> x
这意味着您的示例与 (fmap (+ 5) (* 3)) 4
相同.函数的 fmap
函数只是组合,所以你的确切表达式与 ((+ 5) . (* 3)) 4
.
This means that your example is the same as (fmap (+ 5) (* 3)) 4
. The fmap
function for functions is just composition, so your exact expression is the same as ((+ 5) . (* 3)) 4
.
现在,让我们想想为什么实例是这样写的.<*>
所做的实质上是将函子中的函数应用于函子中的值.专门针对 (->) r
,这意味着它将 r
中的函数返回的函数应用于 r
中的函数返回的值>.返回一个函数的函数只是一个有两个参数的函数.所以真正的问题是:你如何将一个有两个参数(r
和 a
,返回 b
)的函数应用到一个值 >a
由 r
的函数返回?
Now, let's think about why the instance is written the way it is. What <*>
does is essentially apply a function in the functor to a value in the functor. Specializing to (->) r
, this means it applies a function returned by a function from r
to a value returned by a function from r
. A function that returns a function is just a function of two arguments. So the real question is this: how would you apply a function of two arguments (r
and a
, returning b
) to a value a
returned by a function from r
?
首先要注意的是,你必须返回一个 (->) r
类型的值,这意味着结果也必须是一个来自 r
的函数.作为参考,这里是 <*>
函数:
The first thing to note is that you have to return a value of type (->) r
which means the result also has to be a function from r
. For reference, here is the <*>
function:
f <*> g = x -> f x (g x)
因为我们想要返回一个函数,它的值类型为 r
,x :: r
.我们返回的函数必须有一个类型 r ->b
.我们如何获得 b
类型的值?好吧,我们有一个函数 f :: r ->->b
.由于 r
将成为结果函数的参数,我们免费获得它.所以现在我们有一个来自 a -> 的函数.b
.所以,只要我们有一个 a
类型的值,我们就可以得到一个 b
类型的值.但是我们如何获得 a
类型的值呢?好吧,我们还有另一个函数 g :: r ->一个
.因此,我们可以获取 r
类型的值(参数 x
)并使用它来获取 a
类型的值.
Since we want to return a function taking a value of type r
, x :: r
. The function we return has to have a type r -> b
. How can we get a value of type b
? Well, we have a function f :: r -> a -> b
. Since r
is going to be the argument of the result function, we get that for free. So now we have a function from a -> b
. So, as long as we have some value of type a
, we can get a value of type b
. But how do we get a value of type a
? Well, we have another function g :: r -> a
. So we can take our value of type r
(the parameter x
) and use it to get a value of type a
.
所以最后的想法很简单:我们使用参数首先通过将其插入g
来获取a
类型的值.参数的类型为 r
,g
的类型为 r ->a
,所以我们有一个 a
.然后,我们将参数和新值都插入到 f
中.我们需要两者,因为 f
有一个类型 r ->->b
.一旦我们同时插入r
和a
,我们就有了一个b1
.由于参数在 lambda 中,因此结果的类型为 r ->b
,这就是我们想要的.
So the final idea is simple: we use the parameter to first get a value of type a
by plugging it into g
. The parameter has type r
, g
has type r -> a
, so we have an a
. Then, we plug both the parameter and the new value into f
. We need both because f
has a type r -> a -> b
. Once we plug both an r
and an a
in, we have a b1
. Since the parameter is in a lambda, the result has a type r -> b
, which is what we want.
这篇关于作为应用函子的功能(Haskell/LYAH)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!