[英](r ->) applicative functor
I am having some trouble understanding how the function instance (->) r
of Applicative works in Haskell. 我在理解Applicative的函数实例(->) r
如何在Haskell中工作时遇到了一些麻烦。
For example if I have 例如,如果我有
(+) <$> (+3) <*> (*100) $ 5
I know you get the result 508, I sort of understand that you take the result of (5 + 3)
and (5 * 100)
and you apply the (+)
function to both of these. 我知道你得到了结果508,我知道你得到(5 + 3)
和(5 * 100)
并且你将(+)
函数应用于这两个。
However I do not quite understand what is going on. 但是我不太明白发生了什么。 I assume that the expression is parenthesized as follows: 我假设表达式括号如下:
((+) <$> (+3)) <*> (*100)
From my understanding what is happening is that your mapping (+)
over the eventual result of (+3)
and then you are using the <*>
operator to apply that function to the eventual result of (*100)
根据我的理解,发生的事情是你在(+3)
的最终结果上的映射(+)
,然后你使用<*>
运算符将该函数应用于(*100)
的最终结果
However I do not understand the implementation of <*>
for the (->) r
instance and why I cannot write: 但是我不理解(->) r
实例的<*>
的实现以及为什么我不能写:
(+3) <*> (*100)
How does the <*>
, <$>
operator work when it comes to (->) r
? <*>
, <$>
运算符在(->) r
时是如何工作的?
<$>
is just another name for fmap
and its definition for (->) r
is (.)
(the composition operator): <$>
只是fmap
另一个名称, fmap
(->) r
定义是(.)
(组合运算符):
intance Functor ((->) r) where
fmap f g = f . g
You can basically work out the implementation for <*>
just by looking at the types: 通过查看类型,您基本上可以计算<*>
的实现:
instance Applicative ((->) r) where
(<*>) :: (r -> a -> b) -> (r -> a) -> (r -> b)
f <*> g = \x -> f x (g x)
You have a function from r
to a
to b
and a function from r
to a
. 你有一个从r
到a
到b
的函数和一个从r
到a
的函数。 You want a funtion from r
to b
as a result. 你想要一个从r
到b
的功能。 First thing you know is you return a function: 你知道的第一件事是你返回一个函数:
\x ->
Now you want to apply f
since it is the only item which may return a b
: 现在你想要应用f
因为它是唯一可以返回b
:
\x -> f _ _
Now the arguments for f
are of type r
and a
. 现在f
的参数是r
和a
的类型。 r
is simply x
(since it alrady is of type r
and you can get an a
by applying g
to x
: r
只是x
(因为它的alrady是r
类型,你可以通过将g
应用于x
得到a
:
\x -> f x (g x)
Aaand you're done. Aaand你已经完成了。 Here's a link to the implementation in Haskell's Prelude . 这是Haskell Prelude中实现的链接 。
Consider the type signature of <*>
: 考虑<*>
的类型签名:
(<*>) :: Applicative f => f (a -> b) -> f a -> f b
Compare this to the type signature for ordinary function application, $
: 将此与普通函数应用程序的类型签名进行比较, $
:
($) :: (a -> b) -> a -> b
Notice that they are extremely similar! 请注意,它们非常相似! Indeed, the <*>
operator effectively generalizes application so that it can be overloaded based on the types involved. 实际上, <*>
运算符有效地推广了应用程序,使其可以根据所涉及的类型进行过载 。 This is easy to see when using the simplest Applicative
, Identity
: 使用最简单的Applicative
, Identity
时很容易看到:
ghci> Identity (+) <*> Identity 1 <*> Identity 2
Identity 3
This can also be seen with slightly more complicated applicative functors, such as Maybe
: 这也可以通过稍微复杂的应用函数来看到,例如Maybe
:
ghci> Just (+) <*> Just 1 <*> Just 2
Just 3
ghci> Just (+) <*> Nothing <*> Just 2
Nothing
For (->) r
, the Applicative
instance performs a sort of function composition, which produces a new function that accepts a sort of “context” and threads it to all of the values to produce the function and its arguments: 对于(->) r
, Applicative
实例执行一种函数组合,它生成一个新函数,它接受一种“上下文”并将其线程化为所有值以生成函数及其参数:
ghci> ((\_ -> (+)) <*> (+ 3) <*> (* 100)) 5
508
In the above example, I have only used <*>
, so I've explicitly written out the first argument as ignoring its argument and always producing (+)
. 在上面的例子中,我只使用了<*>
,所以我明确写出第一个参数是忽略它的参数并且总是产生(+)
。 However, Applicative
typeclass also includes the pure
function, which has the same purpose of “lifting” a pure value into an applicative functor: 但是, Applicative
类型类还包括pure
函数,它具有将纯值“提升”为applicative functor的相同目的:
ghci> (pure (+) <*> (+ 3) <*> (* 100)) 5
508
In practice, though, you will rarely see pure x <*> y
because it is precisely equivalent to x <$> y
by the Applicative
laws, since <$>
is just an infix synonym for fmap
. 但实际上,您很少会看到pure x <*> y
因为它与Applicative
定律完全等同于x <$> y
,因为<$>
只是fmap
的中缀同义词。 Therefore, we have the common idiom: 因此,我们有一个共同的习语:
ghci> ((+) <$> (+ 3) <*> (* 100)) 5
508
More generally, if you see any expression that looks like this: 更一般地说,如果您看到任何表达式如下所示:
f <$> a <*> b
…you can read it more or less like the ordinary function application fab
, except in the context of a particular Applicative
instance's idioms. ...除了在特定的Applicative
实例的习语的上下文中,您可以或多或少地像普通函数应用程序fab
一样阅读它。 In fact, an original formulation of Applicative
proposed the idea of “idiom brackets”, which would add the following as syntactic sugar for the above expression: 实际上, Applicative
一个原始提法提出了“成语括号”的概念,它将为以上表达式添加以下语法糖:
(| f a b |)
However, Haskellers seem to be satisfied enough with the infix operators that the benefits of adding the additional syntax has not been deemed worth the cost, so <$>
and <*>
remain necessary. 但是,Haskellers似乎对中缀运算符感到满意,认为添加额外语法的好处并不值得花费,因此<$>
和<*>
仍然是必要的。
As a Haskell newbie myself, i'll try to explain the best way i can 作为一个Haskell新手,我会尝试解释我能做到的最佳方式
The <$>
operator is the same as mapping a function on to another function. <$>
运算符与将函数映射到另一个函数相同。
When you do this: 当你这样做:
(+) <$> (+3)
You are basically doing this: 你基本上是这样做的:
fmap (+) (+3)
The above will call the Functor implementation of (->) r which is the following: 以上将调用( - >)r的Functor实现,如下所示:
fmap f g = (\x -> f (g x))
So the result of fmap (+) (+3)
is (\\x -> (+) (x + 3))
所以fmap (+) (+3)
是(\\x -> (+) (x + 3))
Note that the result of this expression has a type of a -> (a -> a)
请注意,此表达式的结果类型为a -> (a -> a)
Which is an applicative! 哪个适用! That is why you can pass the result of (+) <$> (+3)
to the <*>
operator! 这就是为什么你可以将(+) <$> (+3)
的结果传递给<*>
运算符!
Why is it an applicative you might ask? 为什么这是一个你可能会问的应用? Lets look the at the <*>
definition: 让我们看看<*>
定义:
f (a -> b) -> f a -> f b
Notice that the first argument matches our returned function definition a -> (a -> a)
请注意,第一个参数匹配我们返回的函数定义a -> (a -> a)
Now if we look at the <*>
operator implementation, it looks like this: 现在,如果我们查看<*>
运算符实现,它看起来像这样:
f <*> g = (\x -> f x (g x))
So when we put all those pieces together, we get this: 因此,当我们将所有这些部分组合在一起时,我们得到了
(+) <$> (+3) <*> (+5)
(\x -> (+) (x + 3)) <*> (+5)
(\y -> (\x -> (+) (x + 3)) y (y + 5))
(\y -> (+) (y + 3) (y + 5))
Let's take a look at the types of these functions (and the definitions that we automatically get along with them): 让我们来看看这些函数的类型(以及我们自动与之相关的定义):
(<$>) :: (a -> b) -> (r -> a) -> r -> b
f <$> g = \x -> f (g x)
(<*>) :: (r -> a -> b) -> (r -> a) -> r -> b
f <*> g = \x -> f x (g x)
In the first case, <$>
, is really just function composition. 在第一种情况下, <$>
,实际上只是功能组合。 A simpler definition would be (<$>) = (.)
. 更简单的定义是(<$>) = (.)
。
The second case is a little more confusing. 第二种情况更令人困惑。 Our first input is a function f :: r -> a -> b
, and we need to get an output of type b
. 我们的第一个输入是函数f :: r -> a -> b
,我们需要得到类型为b
的输出。 We can provide x :: r
as the first argument to f
, but the only way we can get something of type a
for the second argument is by applying g :: r -> a
to x :: r
. 我们可以提供x :: r
作为f
的第一个参数,但是我们可以为第二个参数获得类型a
的唯一方法是将g :: r -> a
应用于x :: r
。
As an interesting aside, <*>
is really the S
function from SKI combinatory calculus , whereas pure
for (-> r)
is the K :: a -> b -> a
(constant) function. 有趣的是, <*>
实际上是SKI组合微积分的S
函数,而(-> r)
pure
(-> r)
是K :: a -> b -> a
(常数)函数。
The (->) e
Functor
and Applicative
instances tend to be a bit confusing. (->) e
Functor
和Applicative
实例往往有点令人困惑。 It may help to view (->) e
as an "undressed" version of Reader e
. 将(->) e
视为Reader e
的“脱衣服”版本可能会有所帮助。
newtype Reader e a = Reader
{ runReader :: e -> a }
The name e
is supposed to suggest the word "environment". 名称e
应该用来表示“环境”这个词。 The type Reader ea
should be read as "a computation that produces a value of type a
given an environment of type e
". 该类型Reader ea
应该被解读为“产生类型的值计算a
给定类型的环境e
”。
Given a computation of type Reader ea
, you can modify its output: 给定Reader ea
类型的计算,您可以修改其输出:
instance Functor (Reader e) where
fmap f r = Reader $ \e -> f (runReader r e)
That is, first run the computation in the given environment, then apply the mapping function. 也就是说,首先在给定环境中运行计算,然后应用映射函数。
instance Applicative (Reader e) where
-- Produce a value without using the environment
pure a = Reader $ \ _e -> a
-- Produce a function and a value using the same environment;
-- apply the function to the value
rf <*> rx = Reader $ \e -> (runReader rf e) (runReader rx e)
You can use the usual Applicative
reasoning for this as any other applicative functor. 您可以使用通常的Applicative
推理作为任何其他应用程序仿函数。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.