箭头操作符应用的直观概念

时间:2017-10-28 17:04:55

标签: haskell

今天我有几个小时的乐趣试图理解箭头操作符应用程序在Haskell中的作用。我现在正试图验证我的理解是否正确。简而言之,我发现对于箭头操作符应用

 (f <*> g <*> h <*> v) z = f z (g z) (h z) (v z)

在我继续之前,我知道this discussion,但发现它比我今天希望得到的更复杂,更复杂。

为了理解应用程序的作用,我从基础

中箭头应用的定义开始
instance Applicative ((->) a) where
    pure = const
    (<*>) f g x = f x (g x)

然后继续探索表达式

(f <*> g <*> h) z

(f <*> g <*> h <*> v) z

扩张后的产量。

从我们得到的定义

 f <*> g = \x -> f x (g x)

因为(<*>)是左关联的,所以它遵循

 f <*> g <*> h = (f <*> g) <*> h
               = (\x -> f x (g x)) <*> h
               = \y -> (\x -> f x (g x)) y (h y)

因此

 (f <*> g <*> h) z = (\y -> (\x -> f x (g x)) y (h y)) z
                   = (\x -> f x (g x)) z (h z)
                   = (f z (g z)) (h z)
                   = f z (g z) (h z)

最后一步是由于函数应用程序是左关联的。类似地

 (f <*> g <*> h <*> v) z = f z (g z) (h z) (v z)

对我而言,这提供了一个非常直观的概念,即箭头应用程序的功能。 但这是正确的吗?

测试我运行的结果,例如,以下,

λ> ((\z g h v -> [z, g, h, v]) <*> (1+) <*> (2+) <*> (3+)) 4
[4,5,6,7]

符合上面得出的结果。

在进行上述扩展之前,我发现这个应用程序非常难以理解,因为非常复杂的行为可能是由于它的使用而导致的。特别是在

 (f <*> g <*> h <*> v) z = f z (g z) (h z) (v z)

函数可以返回其他函数。这是一个例子:

λ> ((\z g -> g) <*> pure (++) <*> pure "foo" <*> pure "bar") undefined
"foobar"

在这种情况下,z=undefined被所有函数忽略,因为pure x z = x和第一个函数通过构造忽略z。此外,第一个函数只接受两个参数,但返回一个带有两个参数的函数。

1 个答案:

答案 0 :(得分:4)

是的,你的计算是正确的。