haskell中的箭头和函数有何不同?

时间:2016-07-09 14:17:16

标签: haskell arrows

我学习并搜索了一段时间的箭头,我对Arrow课程的必要性感到有些困惑。 据我所知,Arrow类是函数的抽象,而Arrow A a b c表示需要输入类型b和输出类型c的东西。此外,它还提供了一些基本操作,例如>>>arrfirst

但是,我找不到类型b -> c的标准函数和类型A a b c的箭头之间的任何区别。在我看来,first>>>可以替换为\(b, c) -> (f b, c)(.)。此外,由于箭头内的每个计算都由函数表示,如果我们用这些函数替换箭头,我认为没有区别。

简而言之,我认为Arrows的计算图的每个节点(类似于https://www.haskell.org/arrows/syntax.html)都可以被Haskell的标准函数替换。如果是真的,为什么我们使用Arrow而不是函数?

2 个答案:

答案 0 :(得分:5)

遵守某些法律的抽象允许您进行泛型编程。你可以在没有任何类型类的情况下编程(没有monad,applicatives,没有相等/排序等),但是这将非常不方便,因为你将无法编写利用这些属性的通用代码。

就像你说你不想要Ord实例一样,然后你必须为你可以订购的每种数据类型分别重写Set的实现。

Arrow的要点是描述

的计算
  • 接受输入,
  • 生成输出,
  • 中间确定(效果是口语表达式)。

因此箭头A b c不是函数a -> b。很可能箭头在内部实现为一个函数,但是更复杂一个,实现Arrow接口的目的是描述(以及其他方面)它们的组成方式。

特别是对于箭头,您有arrow notation,允许您对任何有效的Arrow使用相同的表示法。举一个例子,在netwire包中,Wire数据类型实现了Arrow,因此您可以使用箭头表示法,以及所有适用于箭头的实用程序函数。如果没有实例,您必须具有一些特定于网络的语法,或者只使用netwire提供的功能。

举个例子:State monad对应的箭头是a -> s -> (b, s)。但是使用(.)时,两个这样的函数不构成。你需要描述它们的构成,而这正是Arrow所做的。

更新:可以有各种可组合性概念,但我想你的意思是功能组合。是的,这个可组合性来自Arrow的{​​{3}}超类,它定义了身份和组合。

Arrow的另一部分来自Category profunctor,因为我最近从Strong学到了(虽然这不是在类型层次结构中捕获的,因为{{1} typeclass比Profunctor更年轻。 Profunctors允许通过“双方”的纯计算进行修改,请参阅Arrow中的lmap / rmap / dimap。对于箭头,我们有Profunctor(<<^),它们使用(^>>)arr表示,通过使用>>>构建的纯箭头从任意一侧组成箭头

最后箭头具有力量 wrt arr,由(,)捕获。这意味着我们只能在输入的一部分上使用箭头,而不会更改另一个。这允许用“并行线”构造“电路” - 没有first :: Arrow a => a b c -> a (b, d) (c, d)就不可能保存计算的一部分的输出并在以后的某个地方使用它。

一个好的练习是绘制一个表示计算的电路,然后尝试使用first基元/实用程序表达它,或者使用箭头语法表示法。您会看到Arrow(或first)对此至关重要。

有关操作的精美图纸,请参阅What's the relationship between profunctors and arrows?

更多理论背景Arrows can multitask可能很有意思(我还没有读过)。

答案 1 :(得分:4)

那是因为您只关注(->)的{​​{1}}个实例。其他类型也可以声明Arrow的实例,其中操作更复杂。例如:

Arrow