了解您的Haskell 第11章介绍了以下定义:
instance Applicative ((->) r) where
pure x = (\_ -> x)
f <*> g = \x -> f x (g x)
在这里,作者参与了一些不寻常的挥手(“&lt; *&gt;的实例实现有点神秘,所以如果我们只是[在没有解释的情况下展示它的话]它是最好的”)。我希望这里的某个人可以帮我解决这个问题。
根据应用类定义,(<*>) :: f (a -> b) -> f a -> f b
在实例中,用((->)r)
代替f
:r->(a->b)->(r->a)->(r->b)
所以第一个问题是,我如何从该类型转到f <*> g = \x -> f x (g x)
?
但即使我认为最后一个公式是理所当然的,我也很难同意我给GHCi的例子。例如:
Prelude Control.Applicative> (pure (+5)) <*> (*3) $ 4
17
此表达式显示与f <*> g = \x -> f (g x)
一致(请注意,此版本x
不会出现在f
之后。
我意识到这很麻烦,所以感谢与我的关系。
答案 0 :(得分:33)
首先,请记住如何为应用程序定义fmap
:
fmap f x = pure f <*> x
这意味着您的示例与(fmap (+ 5) (* 3)) 4
相同。函数的fmap
函数只是合成,因此您的确切表达式与((+ 5) . (* 3)) 4
相同。
现在,让我们考虑为什么实例的编写方式。 <*>
所做的基本上是将函数中的函数应用于函子中的值。专门用于(->) r
,这意味着它将函数返回的函数r
应用于r
函数返回的值。返回函数的函数只是两个参数的函数。所以真正的问题是:如何将两个参数的函数(r
和a
,返回b
)应用于a
函数返回的值r
{1}}?
首先要注意的是,您必须返回(->) r
类型的值,这意味着结果也必须是来自r
的函数。作为参考,这是<*>
函数:
f <*> g = \x -> f x (g x)
因为我们想要返回一个类型r
,x :: r
的函数。我们返回的函数必须具有类型r -> b
。我们如何获得b
类型的值?好吧,我们有一个函数f :: r -> a -> b
。由于r
将成为结果函数的参数,我们可以免费获得。所以现在我们有a -> b
的函数。因此,只要我们有一些a
类型的值,我们就可以获得类型为b
的值。但是我们如何获得a
类型的值?好吧,我们有另一个函数g :: r -> a
。因此,我们可以使用r
类型的值(参数x
)并使用它来获取类型a
的值。
所以最后的想法很简单:我们使用参数首先通过将a
插入g
来获取r
类型的值。参数的类型为g
,r -> a
的类型为a
,因此我们有一个f
。然后,我们将参数和新值都插入f
。我们需要两者,因为r -> a -> b
的类型为r
。在我们同时插入a
和b1
之后,我们就会有r -> b
。由于参数是lambda,结果的类型为{{1}},这就是我们想要的。
答案 1 :(得分:20)
通过你原来的问题,我认为你可能错过了一个微妙但非常关键的观点。使用LYAH的原始示例:
(+) <$> (+3) <*> (*100) $ 5
这与:
相同pure (+) <*> (+3) <*> (*100) $ 5
此处的关键是pure
之前的(+)
,其效果是将(+)
作为一个应用来装箱。如果你看看如何定义pure
,你可以看到要取消它,你需要提供一个额外的参数,可以是任何东西。将<*>
应用于(+) <$> (+3)
,即可获得
\x -> (pure (+)) x ((+3) x)
请注意(pure (+)) x
,我们将x
应用于pure
至unbox (+)
。所以我们现在有了
\x -> (+) ((+3) x)
添加(*100)
以获取(+) <$> (+3) <*> (*100)
并再次应用<*>
,我们
\y -> (\x -> (+) ((+3) x)) y ((*100) y) {Since f <*> g = f x (g x)}
5 -> (\x -> (+) ((+3) x)) 5 ((*100) 5)
(\x -> (+) ((+3) x)) 5 (500)
5 -> (+) ((+3) 5) (500)
(+) 8 500
508
总而言之,x
之后的f
不是我们的二元运算符的第一个参数,它用于UNBOX pure
内的运算符。
答案 2 :(得分:14)
“在实例中,用
((->)r)
代替f
:r->(a->b)->(r->a)->(r->b)
”
为什么,这不对。它实际上是(r->(a->b)) -> (r->a) -> (r->b)
,与(r->a->b) -> (r->a) -> r -> b
相同。即,我们映射一个中缀和一个函数,该函数将中缀的右手参数返回给一个只接受中缀'LHS并返回其结果的函数。例如,
Prelude Control.Applicative> (:) <*> (\x -> [x]) $ 2
[2,2]
答案 3 :(得分:0)
Function as Functor
和Function as Applicative
?首先,如何理解函子的功能?
我们可以将仿函数视为一个空盒子,例如:
instance Functor Maybe where
fmap :: (a -> b) -> f a -> f b
fmap f (Just x) = Just (f x)
fmap f Nothing = Nothing
那里的Maybe
类型可以看作是一个带有一个插槽的空盒子,该类型采用一种类型来生成具体的类型Maybe a
。在fmap
函数中:
Maybe
,所以 fa 是Maybe a
)。 当我们实现函数函子时,因为函数函子必须具有两个参数才能形成类型a -> b
,如果我们希望函数函子只有一个插槽,则应首先填充一个插槽,因此函数的类型构造函数函子是((->)r):
instance Functor ((->) r) where
fmap f g = (\x -> f (g x))
与fmap
函数中的Maybe
函数一样,我们应该将第二个参数 g 视为由生成的具体类型的值f ( f 等于(->) r
),所以 fa 是(->) r a
,可以看作r -> a
。最后,不难理解fmap
函数中的 gx 不能被视为r -> x
,它只是一个函数应用程序,可以被视为{{1 }},也(r -> a) x
。
最后,不难理解应用功能(x -> a)
中的<*>功能可以实现如下:
(->) r
gr 的将 r 映射到 a , fra 将 r,a映射到 b ,因此整个lambda函数可以看作<*> :: f (a -> b) -> f a -> f b
<*> :: (r -> a -> b) -> (r -> a) -> (r -> b)
<&> :: (a -> b) -> (r -> a) -> (r -> b)
f <*> g = \r -> f r (g r)
,也可以看作r -> b
。例如:
f b
结果是5 +(5 + 3)= 13。
((+) <*> (+3)) 5
= 508?我们知道(+) <$> (+3) <*> (*100) $ 5
的类型为:(+)
;
我们也知道Num a, a -> a -> a
和(+3)
的类型为:(*100)
;
Num r, a, r -> a
等于(+) <$> (+3)
,其中pure (+) <*> (+3)
等于:t pure (+)
换句话说,Num _, a, _ -> a -> a -> a
只需接受一个pure (+)
参数,然后返回_
运算符,参数+
对最终返回值没有影响。 _
还将函数pure (+)
的返回值映射到一个函数。现在
(+3)
我们可以应用运算符并获得:
f <*> g = \r -> f r (g r)
其类型为pure (+) <*> (+3) =
\r -> f r (gr) =
\r -> + (gr) =
\r -> + (r + 3) =
\r x -> x + (r + 3)
。然后,使用<*>的定义计算r -> x -> a
,并得到:
pure (+) <*> (+3) <*> (*100)
然后我们将此函数与参数5一起应用,我们得到:
pure (+) <*> (+3) <*> (*100) =
\r -> f r (gr) =
\r -> (r + 3) + (gr)
\r -> (r + 3) + (r * 100)
我们可以简单地认为这种应用样式是首先计算(5 + 3) + (5 * 100) = 508
之后的值,并将它们与<$>
之前的运算符求和。在最后一个示例中,该运算符是等于<$>
的二进制运算符,我们可以用三元运算符(+)
代替它,因此以下等式成立:
(\x y z -> [x,y,z])