pla*_*ian 36 haskell applicative
学习Haskell的第11章介绍了以下定义:
instance Applicative ((->) r) where
pure x = (\_ -> x)
f <*> g = \x -> f x (g x)
Run Code Online (Sandbox Code Playgroud)
在这里,作者参与了一些不寻常的挥手("<*>的实例实现有点神秘,所以如果我们只是[在没有解释它的情况下显示它的话]它是最好的").我希望这里的某个人可以帮助我搞清楚.
根据应用类定义, (<*>) :: 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
Run Code Online (Sandbox Code Playgroud)
这个表达式看起来与之相符f <*> g = \x -> f (g x)(请注意,此版本x之后不会出现f.
我意识到这很混乱,所以感谢与我的关系.
Tik*_*vis 35
首先,请记住如何fmap为应用程序定义:
fmap f x = pure f <*> x
Run Code Online (Sandbox Code Playgroud)
这意味着您的示例与之相同(fmap (+ 5) (* 3)) 4.在fmap对功能的功能仅仅是组成,所以你确切的表达是一样的((+ 5) . (* 3)) 4.
现在,让我们考虑为什么实例的编写方式.什么<*>基本上将函数中的函数应用于仿函数中的值.专门用于(->) r,这意味着它将函数返回的函数应用于函数返回r的值r.返回函数的函数只是两个参数的函数.所以,真正的问题是:你会如何运用的两个参数(函数r和a,返回b)的值a由函数返回r?
首先要注意的是你必须返回一个类型的值,(->) r这意味着结果也必须是一个函数r.供参考,这是<*>功能:
f <*> g = \x -> f x (g x)
Run Code Online (Sandbox Code Playgroud)
因为我们想要返回一个取值类型的函数r,x :: r.我们返回的函数必须有一个类型r -> b.我们如何获得类型的值b?好吧,我们有一个功能f :: r -> a -> b.由于r它将成为结果函数的参数,我们可以免费获得.所以现在我们有了一个函数a -> b.所以,只要我们有一些类型的值a,我们就可以得到一个类型的值b.但是我们如何获得类型的价值a?好吧,我们有另一个功能g :: r -> a.因此我们可以获取type r(参数x)的值,并使用它来获取type的值a.
所以最后的想法很简单:我们使用参数首先a通过插入来获取类型的值g.参数有类型r,g有类型r -> a,所以我们有一个a.然后,我们将参数和新值插入f.我们需要两个因为f有类型r -> a -> b.一旦我们插入一个r和一个a,我们有一个b1.由于参数是lambda,结果有一个类型r -> b,这就是我们想要的.
小智 22
通过你原来的问题,我认为你可能错过了一个微妙但非常关键的观点.使用LYAH的原始示例:
(+) <$> (+3) <*> (*100) $ 5
Run Code Online (Sandbox Code Playgroud)
这与:
pure (+) <*> (+3) <*> (*100) $ 5
Run Code Online (Sandbox Code Playgroud)
这里的关键是purebefore (+),它具有拳击(+)作为应用的效果.如果你看看如何pure定义,你可以看到要取消它,你需要提供一个额外的参数,可以是任何东西.应用<*>到(+) <$> (+3),我们得到
\x -> (pure (+)) x ((+3) x)
Run Code Online (Sandbox Code Playgroud)
注意到(pure (+)) x,我们正在申请x到pure拆箱(+).所以我们现在有
\x -> (+) ((+3) x)
Run Code Online (Sandbox Code Playgroud)
添加(*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
Run Code Online (Sandbox Code Playgroud)
所以总的来说,xafter f不是我们的二元运算符的第一个参数,它用于UNBOX里面的运算符pure.
lef*_*out 15
"在实例中,取代
((->)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]
Run Code Online (Sandbox Code Playgroud)