haskell - 箭头运算符应用的直观想法

标签 haskell

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

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

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

为了理解 applicative 的作用,我从基础中箭头 applicative 的定义开始

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]

与上面得出的结果一致。

在进行上述扩展之前,我发现这个应用程序非常难以理解,因为柯里化(Currying)的使用可能会导致极其复杂的行为。特别是,在

 (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通过 build 。此外,第一个函数仅接受两个参数,但返回一个接受两个参数的函数。

最佳答案

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

关于haskell - 箭头运算符应用的直观想法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46992307/

相关文章:

haskell - COMPLETE pragma 不会阻止不完整模式警告

haskell - 导出模板 haskell 生成的定义

haskell - 多线程程序中Haskell数据类型的设计选择

javascript - Fay、渲染循环和鼠标事件

list - Haskell 列表中元素的索引

haskell - SML模块系统和Haskell的Type和Typeclass系统在功能上有什么区别?

haskell - 如何使用 QuickCheck 为 StateT 编写测试

haskell - Haskell 中的无限映射

haskell - 什么是 haskell 中的自然转换?

haskell - 切换参数顺序的函数的类型是什么?