As I’ve found myself repeatedly writing a bit of code that looked vaguely like this:

get :: SomeArrow String (Maybe Thing)

foo :: [Thing] -> FooThing

. . .

getAToZ :: SomeArrow () (Maybe FooThing)
getAToZ = proc () ->
    do m_a <- get -< "a"
       m_b <- get -< "b"
       . . .
       m_z <- get -< "z"
       returnA -<
           do a <- m_a
              b <- m_b
              . . .
              z <- m_z
              return $ foo [a,b . . . z]

It seemed that there would have to be a way to automate this. So I wrote this MaybeArrow (careful, I’m still using 6.8.2 so it’s the old arrows with ‘pure’.). I know that there is already an ErrorArrow. But ErrorArrow as far as I can see requires ArrowChoice, and I’m not doing that. In the MaybeArrow, if an earlier computation throws a Nothing, then later computations are still allowed to perform side effects, although their outputs are snuffed, which is the behavior I want.

I would appreciate any constructive feedback.

If you’re wondering the use case has to do with waiting on several separate pieces of information to arrive from a server and combining them into one output message.

10 thoughts on “MaybeArrow?

  1. hm, nice puzzle.
    because of the missing ArrowChoice class,
    you need sth like (“a”,(“b”,(“c”,(…))) inside the arrow. that means: a fixed length list.

    but you need the fixed length only ‘inside’ the arrow, not in your sourcecode. so build it!

    the idea is to build a chain of equal arrows, which can be simply combined… using lists instead of tuples. each link in that chain is a double list-/stream-transformer. each takes one item from the input list/stream and applies the computed output to the output list/stream. (chainlink)

    that chain should process the list of inputs and should generate the list of outputs. (amap)

    then on those outputs we have to test wether they all are Just cases… you use the Monad property, which is quite handy, but only if you know some library functions like sequence.

    [my next reply shows the code]

    well, reading your code above, i guess you still think a tiny bit ‘the imperative way'(tm) …so i advise to read Data.List and half of the paper “Functional Programming with Bananas, Lenses, Envelopes and Barbed Wire (1991)” (, that will blow most of that tedious imperative style away.😉 it’s about foldr and friends.

    – marc

    (i’ve just read HWN-100, so this didn’t take 2 weeks.)

  2. Ah, I see what you’re driving at. But I think there is a deficiency in the example, rather than the solution. The example ought to show a foo that takes some heterogeneous parameters.

  3. hm… @moderator: please remove the code above. this is my 2nd try.

    {-# LANGUAGE Arrows,TypeOperators,EmptyDataDecls #-}
    module MaybeArrow where
    --import Control.Category hiding ((.),id)
    import Control.Arrow
    data Thing
    data FooThing
    data SomeArrow b c
    --instance Control.Category.Category SomeArrow where
    instance Arrow SomeArrow where
    get :: SomeArrow String (Maybe Thing)
    get = undefined
    foo :: [Thing] -&gt; FooThing
    foo = undefined
    -- chainlink :: ... -&gt; ((a:ar,br) ~&gt; (ar,b:br))
    chainlink :: Arrow (~&gt;) =&gt; (a ~&gt; b) -&gt; (([a],[b])~&gt;([a],[b]))
    chainlink f = proc (a:ar,br) -&gt; do
        b &lt;- f -&lt; a
        returnA -)) =&gt; (a ~&gt; b) -&gt; [a] -&gt; (() ~&gt; [b])
    amap f as = proc x -&gt; do
        (_,bs) &gt;&gt;) (arr id) $ replicate (length as) (chainlink f) ) -&lt; (as,[])
        returnA -&lt; reverse bs
    getAToZ :: SomeArrow () (Maybe FooThing)
    getAToZ = amap get [[s]|s&gt;&gt; arr (fmap foo . sequence)
  4. hm… i don’t know how to submit code properly.
    hey, you are online:)

    what do you mean with heterogeneous parameters?

    chainlink :: Arrow (~>) => (a ~> b) -> (([a],[b])~>([a],[b]))
    chainlink f = proc (a:ar,br) -> do
    b <- f -< a
    returnA -< (ar,b:br)

  5. amap :: (Arrow (~>)) => (a ~> b) -> [a] -> (() ~> [b])
    amap f as = proc x -> do
    (_,bs) > > ) (arr id) $ replicate (length as) (chainlink f) ) -< (as,[])
    returnA -< reverse bs

  6. the three ‘>’ are the problem.
    amap :: (Arrow (~>)) => (a ~> b) -> [a] -> (() ~> [b])
    amap f as = proc x -> do
    (_,bs) ,>,>) (arr id) $ replicate (length as) (chainlink f) ) -< (as,[])
    returnA -< reverse bs

  7. argh!
    in words:

    ( foldr (
    ) (arr id) $ replicate (length as) (chainlink f) ) -< (as,[])

  8. Heterogenous parameters, in other words, what I really want is something like:

    proc _ ->
        do m_a <- getA -< ()
           m_b <- getB -< ()
           m_c <- getC -< ()
           returnA -<
               do a <- m_a
                  b <- m_b
                  c <- m_c
                  return $ foo a b c

    And for some reason, I didn’t think clearly about that when I did the original example.

  9. hm, k.
    different getA/B/C, same monad.
    as long as the example is short, this should work:

    fmap (uncurry $ uncurry $ liftM3 foo) ((getA &&& getB) &&& getC) -< ()

    i didn’t compile it, it’s 5:25 at night in germany.
    – marc

  10. Oh, that’s really annoying. I had to change a little bit of formatting with the new theme, and it got relisted on the planets.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s