haskell中的箭头和功能有何不同?

Yan*_*ang 6 haskell arrows

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

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

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

che*_*ner 5

那是因为您仅在查看的(->)实例Arrow。其他类型也可以声明的实例Arrow,其中操作更为复杂。例如:

instance Monad m => Arrow (Kleisli m) where
    arr f = Kleisli (return . f)
    first (Kleisli f) = Kleisli (\ ~(b,d) -> f b >>= \c -> return (c,d))
    second (Kleisli f) = Kleisli (\ ~(d,b) -> f b >>= \c -> return (d,c))
Run Code Online (Sandbox Code Playgroud)


Pet*_*lák 5

遵循某些定律的抽象使您可以进行通用编程。您可以在没有任何类型类的情况下进行编程(没有monad,应用程序,没有相等/顺序等),但这将非常不便,因为您将无法编写利用这些属性的通用代码。

就像您说不想使用该Ord实例一样,然后必须Set针对可以订购的每种数据类型分别重写实现。

的要点Arrow是描述计算

  • 接受输入
  • 产生输出,并且
  • 中间要有确定性(效果是口语表达)。

因此,箭头A b c不是函数a -> b。箭头最有可能在内部作为一种功能实现,但更为复杂,实现Arrow界面的目的是(除其他外)描述它们的组成方式。

专门针对箭头,您可以使用箭头符号来对任何有效的使用相同的符号Arrow。举个例子,在netwire包中,Wire数据类型实现了Arrow,因此您可以使用箭头符号以及在箭头上起作用的所有实用程序功能。没有实例,您将必须具有特定于netwire的语法,或者仅使用netwire提供的功能。

举一个例子:与Statemonad 对应的箭头是a -> s -> (b, s)。但是两个这样的函数不能用组合(.)。您需要描述它们的组成,而事实恰恰是Arrow这样。


更新:可能存在多种可组合性概念,但我想您的意思是类似于函数的组合。是的,这种可组合性来自ArrowCategory超类,该超类定义了身份和组成。

我最近从中了解到,另一部分Arrow来自Strong发音器。发音器和箭头之间是什么关系?(尽管在type-class层次结构中未捕获到此内容,因为Profunctortypeclass比年龄年轻Arrow)。Profunctors允许由纯计算从“双方”改性,见lmap/ rmap/ dimapProfunctor。对于箭头,我们有(<<^)(^>>),用arr>>>通过用组成bv的左右箭头与用构造的纯箭头来表示arr

最后箭的力量为 wrt (,),被俘获first :: Arrow a => a b c -> a (b, d) (c, d)。这意味着我们只能在输入的一部分上使用箭头,而传递另一个不变的箭头。这允许使用“平行线”构造“电路”- first不可能保存一部分计算的输出并在以后的某个地方使用它。

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

有关操作的详细说明,请参见“ 箭头可以多任务处理 ”。

对于更多的理论背景,“ 箭是坚强的Monads”可能很有趣(我尚未阅读)。