我花了一段时间学习和搜索了Arrows,但对Arrow类的必要性感到有些困惑。
据我所知,Arrow类是函数的抽象,而Arrow A a b c
表示某些东西需要b类型的输入和c类型的输出。此外,它提供了一些基本操作,例如>>>
,arr
和first
。
但是,我找不到b -> c
类型的标准函数和A a b c
类型的Arrow之间的任何区别。在我看来,first
和>>>
可以替换为\(b, c) -> (f b, c)
和(.)
。另外,由于箭头内部的每个计算都由函数表示,因此如果我们用这些函数替换箭头,我认为不会有任何区别。
简而言之,我认为Arrows计算图的每个节点(类似于https://www.haskell.org/arrows/syntax.html)都可以由Haskell的标准函数代替。如果是这样,为什么我们使用Arrow而不是function?
最佳答案
遵循特定规则的抽象允许您进行通用编程。您可以在没有任何类型类的情况下进行编程(没有monad,应用程序,没有相等性/顺序等),但这将非常不便,因为您将无法编写利用这些属性的通用代码。
就像您说不想使用Ord
实例一样,然后您必须针对可以订购的每种数据类型分别重写Set
的实现。Arrow
的要点是描述计算
因此,箭头
A b c
不是函数a -> b
。箭头最有可能在内部作为一种功能实现,但更为复杂,实现Arrow
接口(interface)的目的是(除其他外)描述它们的组成。特别是对于箭头,您拥有arrow notation,它允许您对任何有效的
Arrow
使用相同的符号。举个例子,在netwire包中, Wire
数据类型实现Arrow
,因此您可以使用箭头符号以及在箭头上起作用的所有实用程序功能。没有实例,您将必须具有特定于netwire的语法,或者仅使用netwire提供的功能。举一个例子:与
State
monad对应的箭头是a -> s -> (b, s)
。但是两个这样的函数不能使用(.)
编写。您需要描述它们的组成,而这正是Arrow
所做的。更新:可能存在多种可组合性概念,但我想您的意思是类似于函数的组合。是的,这种可组合性来自
Arrow
的 Category
父类(super class),该父类(super class)定义了身份和组成。正如我最近从
Arrow
中学到的,Strong
的另一部分来自What's the relationship between profunctors and arrows?提示器(尽管这不是在类型类层次结构中捕获的,因为Profunctor
类型类比Arrow
更年轻)。可以通过纯计算从“两面”修改修饰符,请参阅lmap
中的rmap
/dimap
/Profunctor
。对于箭头,我们有(<<^)
和(^>>)
,它们是通过arr
和>>>
通过将bv左右两侧的箭头与使用arr
构造的纯箭头组合而成的。最后,箭头的强度为
(,)
,由first :: Arrow a => a b c -> a (b, d) (c, d)
捕获。这意味着我们只能在输入的一部分上使用箭头,而传递另一个不变的箭头。这允许使用“平行线”构造“电路”-如果没有first
,将无法保存一部分计算的输出并在以后的其他地方使用它。一个好的练习是绘制一个表示计算的电路,然后尝试使用
Arrow
基元/实用程序或使用箭头语法表示法来表示它。您会看到first
(或***
)对此至关重要。有关操作的精美图片,请参见Arrows can multitask。
对于更多的理论背景,Arrows are Strong Monads可能很有趣(我尚未阅读)。
关于haskell - haskell中的箭头和函数有何不同?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/38282740/