作为应用函子(Haskell / LYAH) [英] functions as applicative functors (Haskell / LYAH)
问题描述
纯x =(\ - > x)
f< g = \ x - > fx(gx)
在这里,作者参与了一些不寻常的手势( < *>有点神秘,所以最好如果我们只是[在没有解释的情况下展示它在行动中])。
根据适用的类定义,(<>):: f(a - > b) - > f a - > fb
在实例中,将(( - >)r)
替换为(a→b)→(r→a)→(r→b)→(c→c) b)
所以第一个问题是,我该如何从该类型获得 f <*> g = \ x - > fx(gx)
?
但即使我认为最后的公式是理所当然的,我也很难让它与我给GHCi的例子一致。例如:
Prelude Control.Applicative> (纯(+5))* (* 3)$ 4
17
这个表达式看起来与<$ c $一致c> f * g = \ x - > f(gx)(请注意,在此版本中 x
不出现在 f
。
我意识到这是混乱的,所以感谢你的支持。
首先,请记住为应用程序定义了 fmap
: fmap fx =纯f <*> x
这意味着您的示例是与(fmap(+ 5)(* 3))4
相同。函数的 fmap
所以你的确切表达与((+ 5)。(* 3))4
。
相同,让我们考虑为什么实例是按照它的方式编写的。什么<>
does本质上是将functor中的函数应用到functor中的值。到( - >)r
,这意味着它将函数从 r
返回的函数应用于返回的值由 r
中的函数返回一个函数on只是两个参数的函数。所以真正的问题是:如何应用两个参数( r
和 a
)的函数,返回 b
)从一个函数返回的值 a
首先要注意的是,您必须返回( - >)r
类型的值意味着结果也必须是来自 r
的函数。作为参考,这里是<>
函数:
f * g = \ x - > fx(gx)
因为我们想要返回一个函数,它的类型为 r
, x :: r
。我们返回的函数必须有一个类型 r - > B'/ code>。我们如何获得类型
b
的值?那么,我们有一个函数 f :: r - > a - > B'/ code>。由于
r
将成为结果函数的参数,因此我们可以免费获得它。所以现在我们有一个函数来自 a - > B'/ code>。因此,只要我们具有
a
类型的值,我们就可以得到 b
类型的值。但是,我们如何获得类型 a
的值?那么,我们有另外一个函数 g :: r - >一个
。因此,我们可以将 r
(参数 x
)类型的值用于获取类型 A
。所以最后的想法很简单:我们使用这个参数首先通过插入它来获取类型 a
的值成 g
。参数的类型为 r
, g
的类型为 r - >一个
,所以我们有一个 a
。然后,我们将参数和新值都插入到 f
中。我们需要两者,因为 f
有一个类型 r - > a - > B'/ code>。一旦我们同时插入
r
和 a
,我们就有一个 b1
。由于该参数在lambda表达式中,因此结果的类型为 r - > b
,这就是我们想要的。
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.
According to the applicative class definition, (<*>) :: f (a -> b) -> f a -> f b
In the instance, substituting ((->)r)
for f
: r->(a->b)->(r->a)->(r->b)
So the first question, is how do I get from that type to f <*> g = \x -> f x (g x)
?
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
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.
First of all, remember how fmap
is defined for applicatives:
fmap f x = pure f <*> x
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
.
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
?
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)
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
.
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屋!