具有多个通道的功能组合

模糊地讲, Arrow 是一类构成类似函数的态射,具有连续组合和并行组合。虽然最有趣的是函数的泛化,但 Arrow (->) 实例本身已经非常有用。例如,以下功能:

spaceAround::Double -> [Double] -> Double
spaceAround x ys = minimum greater - maximum smaller
 where (greater, smaller) = partition (>x) ys

也可以用箭头组合器编写:

spaceAround x = partition (>x) >>> minimum *** maximum >>> uncurry (-)

这种组合最好用图表可视化:

                       ──── minimum ────
                   ╱           *            ╲
──── partition (>x) >>>        *        >>>  uncurry (-) ───
                   ╲           *            ╱
                       ──── maximum ──── 

这里,

  • >>> 运算符只是一个翻转普通 . 组成运算符的版本(也有对构成从右到左一 <<< 版)。它将数据从一个处理步骤传输到下一个处理步骤。

  • 外出的 表示数据流被分成两个通道。就 Haskell 类型而言,这是通过元组实现的:

    partition (>x) :: [Double] -> ([Double], [Double])
    

    在两个 [Double] 通道中分流,而

    uncurry (-) :: (Double,Double) -> Double
    

    合并了两个 Double 频道。

  • *** 是并行 组合算子。它允许 maximumminimum 在不同的数据通道上独立运行。对于函数,此运算符的签名是

    (***) :: (b->c) -> (β->γ) -> (b,β)->(c,γ)
    

至少在 Hask 类别中(即在 Arrow (->) 实例中),f***g 实际上不会在不同的线程上并行计算 fg。不过,理论上这是可能的。