应用箭头上的幺半群时出现意外结果

Unexpected Result When a Monoid on Arrows is Applied

我有这个简单的 "arrows":

main = do
        let
        -- arr :: (Arrow a) => (b -> c) -> a b c
        -- (>>>) :: Category cat => cat a b -> cat b c -> cat a c
        -- (<+>) :: ArrowPlus a => a b c -> a b c -> a b c
        -- infixr 5 <+>
        -- infixr 1 >>>
        -- runKleisli :: Kleisli m a b -> a -> m b
          prepend x = arr (x ++)
          append  x = arr (++ x)

          xform = (prepend "<") >>> (append ">") <+> (prepend "{") >>> (append "}")
          xs = ["foobar"] >>= (runKleisli xform)
        mapM_ putStrLn xs

<+> returns:

<foobar>}
{<foobar}

如果我将 xform 替换为:

xform = ((prepend "<") >>> (append ">")) <+> ((prepend "{") >>> (append "}"))

我得到:

<foobar>
{foobar}

为什么我得到这 2 个结果?即使查看 infixrs(作为代码中的注释)也无济于事。

让我们写得更切题:

xform = prepend "<" >>> append ">" <+> prepend "<" >>> append ">"
xform' = (prepend "<" >>> append ">") <+> (prepend "<" >>> append ">")

现在 xform,因为 infixr 5 <+>infixl 1 >>> 绑定得更紧密,被解析为

xform = prepend "<" >>> (append ">" <+> prepend "<") >>> append ">"

如图所示

                           ┌─append ">"──┐
     ──────prepend "<"─────<             +>═══append ">"═════▶
                           └─prepend "<"─┘

xform' 仅对应于

            ┌─prepend "<"──append ">"─┐
     ───────<                         +>════▶
            └─prepend "<"──append ">"─┘

这应该可以解释。

从你的问题中不清楚你期望它做什么。如果这个答案仍然没有帮助,编写您希望代码执行的操作将有助于我们了解您的想法。

您正在使用的箭头是 Kleisli [] —— 即,构建在列表 monad 上的箭头。作为 monad 的列表是一种非确定性抽象——也就是说,列表的行为类似于可能性的集合。

Kliesli [] a b 类型等同于 a -> [b],被视为单子函数,即:它接受单个输入和 returns 多个可能的输出。

<+> 所做的是通过每个参数转换输入,然后对可能性进行并集。所以如果你 运行 箭头:

arr id <+> arr reverse

输入"hello",那么你会得到两个答案:

hello
olleh

如果你把它和任何东西组合起来,每一种可能性都会组合成:

prepend "<" >>> (arr id <+> arr reverse)

然后你会得到

<hello
olleh<

因此 <+> 的每个备选方案都被考虑 "in parallel"。 < 出现在第二行的原因是因为输入 arr reverse 已经有 "<" 前缀。相比之下,如果你这样做了

(arr id <+> arr reverse) >>> prepend "<"

然后你得到

<hello
<olleh

为什么你得到现在的输出有意义吗?