Unexpected Result When a Monoid on Arrows is Applied

69 views Asked by At

I have this simple "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

The <+> returns:

<foobar>}
{<foobar}

and if I replace the xform with:

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

I get:

<foobar>
{foobar}

Why I get these 2 results? Even looking at infixrs (as comments in the code) doesn't really help.

2

There are 2 answers

0
leftaroundabout On BEST ANSWER

Lets write that a bit more to the point:

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

Now xform, because infixr 5 <+> binds more tightly than infixl 1 >>>, is parsed as

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

which as a diagram reads

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

whereas xform' corresponds simply to

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

That should explain it.

1
luqui On

It's not clear from your question what you were expecting it to do. If this answer still doesn't help, writing what you expect your code to do will help us understand how you're thinking.

The arrow you are working with is Kleisli [] -- that is, the arrow built on the list monad. The list as a monad is a nondeterminism abstraction -- that is, lists behave like collections of possibilities.

The Kliesli [] a b type is equivalent to a -> [b], treated as a monadic function, that is: it takes a single input and returns many possible outputs.

What <+> does is transform the input by each of its arguments, then take the union of the possibilities. So if you run the arrow:

arr id <+> arr reverse

with the input "hello", then you will get two answers:

hello
olleh

If you compose it with anything, each possibility will be composed:

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

then you'll get

<hello
olleh<

so each alternative of <+> is being considered "in parallel". The reason the < comes at the of the second line is because the input arr reverse is getting has already had "<" prepended. By contrast, if you did

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

Then you get

<hello
<olleh

Is this making sense why you get the output you did now?