了解Haskell中的箭头

Bil*_*ill 54 haskell arrows

我一直试图控制箭头,因为它们是大多数FRP实施的基础.我想我理解了它的基本思想 - 它们与monad有关但在每个绑定操作符中存储静态信息,因此您可以遍历一系列箭头并查看静态信息,而无需评估整个箭头.

但是,在我们开始讨论第一,第二和交换时,我迷路了.2元组与箭头有什么关系?教程提供了元组的东西,好像它是明显的下一步,但我并没有真正看到连接.

就此而言,箭头语法直观地意味着什么?

ken*_*ytm 48

请查看http://www.cs.yale.edu/homes/hudak/CS429F04/AFPLectureNotes.pdf,其中解释了Arrows如何在FRP中工作.

2元组用于定义箭头,因为它需要表示带有2个参数的箭头化函数.

在FRP中,常量和变量通常表示为忽略其"输入"的箭头,例如

twelve, eleven :: Arrow f => f p Int
twelve = arr (const 12)
eleven = arr (const 11)
Run Code Online (Sandbox Code Playgroud)

然后将函数应用程序转换为composition(>>>):

# (6-) 12

arr (6-) <<< twelve
Run Code Online (Sandbox Code Playgroud)

现在我们如何将2参数函数转换为箭头?例如

(+) :: Num a => a -> a -> a
Run Code Online (Sandbox Code Playgroud)

由于currying,我们可以将其视为返回函数的函数.所以

arr (+) :: (Arrow f, Num a) => f a (a -> a)
Run Code Online (Sandbox Code Playgroud)

现在让我们将它应用于常数

arr (+)             -- # f     a (a -> a)
  <<< twelve        -- # f b Int
                      :: f b     (Int -> Int)

+----------+      +-----+      +--------------+
| const 12 |----> | (+) |  ==  | const (+ 12) |
+----------+      +-----+      +--------------+
Run Code Online (Sandbox Code Playgroud)

嘿等等,它不起作用.结果仍然是一个返回函数的箭头,但我们期望类似的东西f Int Int.我们注意到,由于只允许组合,因此curry中的currying失败.因此,我们必须首先解决这个问题

uncurry :: (a -> b -> c) -> ((a, b) -> c)

uncurry (+) :: Num a => (a, a) -> a
Run Code Online (Sandbox Code Playgroud)

然后我们有箭头

(arr.uncurry) (+) :: (Num a, Arrow f) => f (a, a) a
Run Code Online (Sandbox Code Playgroud)

因此,2元组出现了.然后&&&需要一堆函数来处理这些2元组.

(&&&) :: f a b -> f a d -> f a (b, d)
Run Code Online (Sandbox Code Playgroud)

然后可以正确地执行添加.

(arr.uncurry) (+)        -- # f   (a,    a) a
  <<<     twelve         -- # f b  Int
      &&& eleven         -- # f b      Int
                           :: f b           a

+--------+
|const 12|-----.
+--------+     |       +-----+      +----------+
              &&&====> | (+) |  ==  | const 23 |
+--------+     |       +-----+      +----------+
|const 11|-----'
+--------+
Run Code Online (Sandbox Code Playgroud)

(现在,为什么我们不需要像&&&&3元组这样的函数有3个参数?因为((a,b),c)可以使用a代替.)


编辑:从John Hughes的原始论文Generalising Monads到Arrows,它说明了原因

4.1箭头和对

然而,即使在单子运营商的情况下return,并>>=都是我们需要开始编写有用的代码,箭头类似的运营商arr,并>>>是不够的.甚至我们之前看到的简单的monadic添加功能

   add :: Monad m => m Int -> m Int -> m Int
   add x y = x >>= \u -> (y >>= \v -> return (u + v))
Run Code Online (Sandbox Code Playgroud)

还不能用箭头表示.依赖于输入显式,我们看到类似的定义应该采用这种形式

   add :: Arrow a => a b Int -> a b Int -> a b Int
   add f g = ...
Run Code Online (Sandbox Code Playgroud)

在哪里我们必须结合f并按g顺序.唯一可用的测序操作员>>>,但fg没有正确的类型组成.实际上,该add函数需要在计算中保存类型的输入,以便能够提供相同的输入.同样,必须在计算中保存结果,以便最终可以将两个结果相加并返回.到目前为止引入的箭头组合器使我们无法在另一个计算中保存值,因此我们别无选择,只能引入另一个组合器.bfgfg