今天我有几个小时的乐趣试图理解箭头操作符应用程序在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
。此外,第一个函数只接受两个参数,但返回一个带有两个参数的函数。
答案 0 :(得分:4)
是的,你的计算是正确的。