简体   繁体   English

(r - >)applicative functor

[英](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 . 你有一个从rab的函数和一个从ra的函数。 You want a funtion from r to b as a result. 你想要一个从rb的功能。 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的参数是ra的类型。 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 : 使用最简单的ApplicativeIdentity时很容易看到:

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: 对于(->) rApplicative实例执行一种函数组合,它生成一个新函数,它接受一种“上下文”并将其线程化为所有值以生成函数及其参数:

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 FunctorApplicative实例往往有点令人困惑。 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM