应用箭头上的幺半群时出现意外结果
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 个结果?即使查看 infixr
s(作为代码中的注释)也无济于事。
让我们写得更切题:
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
为什么你得到现在的输出有意义吗?
我有这个简单的 "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 个结果?即使查看 infixr
s(作为代码中的注释)也无济于事。
让我们写得更切题:
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
为什么你得到现在的输出有意义吗?