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

但是,我找不到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/

    10-14 06:50