我学习并搜索了一段时间的箭头,我对Arrow课程的必要性感到有些困惑。
据我所知,Arrow类是函数的抽象,而Arrow A a b c
表示需要输入类型b和输出类型c的东西。此外,它还提供了一些基本操作,例如>>>
,arr
和first
。
但是,我找不到类型b -> c
的标准函数和类型A a b c
的箭头之间的任何区别。在我看来,first
和>>>
可以替换为\(b, c) -> (f b, c)
和(.)
。此外,由于箭头内的每个计算都由函数表示,如果我们用这些函数替换箭头,我认为没有区别。
简而言之,我认为Arrows的计算图的每个节点(类似于https://www.haskell.org/arrows/syntax.html)都可以被Haskell的标准函数替换。如果是真的,为什么我们使用Arrow而不是函数?
答案 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