【问题标题】:How are Arrows and Functions different in haskell?haskell 中的箭头和函数有何不同?
【发布时间】:2016-07-09 14:17:16
【问题描述】:

我学习并搜索了一段时间关于 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 的标准函数。如果是真的,为什么我们使用箭头而不是函数?

【问题讨论】:

    标签: haskell arrows


    【解决方案1】:

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

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

    Arrow 的重点是描述计算

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

    所以箭头A b c 不是函数a -> b。箭头很可能在内部实现为函数,但更复杂,实现Arrow 接口的目的是描述(除其他外)它们是如何构成的。

    特别是对于箭头,您拥有arrow notation,它允许您对任何有效的Arrow 使用相同的符号。举个例子,在netwire 包中,Wire 数据类型实现了Arrow,因此您可以使用箭头符号以及所有适用于箭头的实用函数。如果没有实例,你要么必须有一些特定于 netwire 的语法,要么只使用 netwire 提供的函数。

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


    更新: 可组合性可能有多种概念,但我猜你的意思是类似函数的组合。是的,这种可组合性来自ArrowCategory 超类,它定义了身份和组合。

    Arrow 的另一部分来自Strong profunctor,正如我最近从What's the relationship between profunctors and arrows? 学到的那样(尽管这没有在类型类层次结构中捕获,因为Profunctor 类型类比@987654349 更年轻@)。 Profunctors 允许通过“双方”的纯计算进行修改,请参阅 Profunctor 中的 lmap/rmap/dimap。对于箭头,我们有(<<^)(^>>),它们使用arr>>> 表示,方法是用来自任一侧的箭头和使用arr 构造的纯箭头组合而成。

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

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

    请参阅Arrows can multitask 了解操作的精美图纸。

    更多理论背景Arrows are Strong Monads 可能会很有趣(我还没读过)。

    【讨论】:

    • 哦,现在我明白了。那么我可以认为Arrow typeclass 是“可组合”对象的类吗?
    • @Yang 是的,有点,但这只是Arrow 的一部分,尤其是Category,它定义了身份和组成。箭头也有other properties,特别是profunctor(虽然目前还没有被类型类层次结构捕获),它允许从函数创建箭头(arr)并通过元组(@ 987654369@).
    • 你的意思是可组合对象的类与Arrow不同吗? John Hughes 的论文说提供arr>>> 来定义Arrow 就足够了。可组合性来自>>>,但其他属性从何而来?我不认为arr 有什么特别之处。
    • @Yang 我扩展了答案。组合只是使箭头有趣的一部分,另外两个允许使用纯函数(arr)操作它们并允许仅对输入的一部分进行操作(first)。
    【解决方案2】:

    那是因为您只查看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))
    

    【讨论】:

    • 我无法理解这个答案。 (->) 实例做同样的事情并且具有与标准函数完全相同的属性。像这样,Kleisli 箭头具有与单子完全相同的属性。所以我认为每个箭头都具有与其内容完全相同的属性。那我们为什么要用箭头呢?
    • 另外,我有点困惑,因为 Kleisli 仍然代表一些功能。我以为箭头是功能的概括,但我看到的每个箭头仍然代表现有的功能。那么,我们为什么要使用箭头而不是现有的函数呢?
    • 它是一个带有一些额外结构的函数的包装器。大多数时候,除了函数,您不使用任何东西,但这并不意味着 Arrow 类型类仅限于普通函数。
    • 一个函数从输入 -> 输出使用 ($):: (input -> output) -> input -> output。箭头允许您捆绑箭头 a,它告诉您如何将输入更改为输出,可能还有额外的输入和输出,以及输入和输出。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-11-15
    • 1970-01-01
    • 2011-03-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多