MaybeArrow?

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.

  • coeus

    January 19th, 2009

    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)” (http://en.wikipedia.org/wiki/Catamorphism), that will blow most of that tedious imperative style away. ;) it’s about foldr and friends.

    regards
    - marc

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

  • Lane

    January 19th, 2009

    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.

  • coeus

    January 19th, 2009

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

    [code]

    {-# 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] -> FooThing
    foo = undefined

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

    getAToZ :: SomeArrow () (Maybe FooThing)
    getAToZ = amap get [[s]|s>> arr (fmap foo . sequence)

    [/code]

  • coeus

    January 19th, 2009

    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)

  • coeus

    January 19th, 2009

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

  • coeus

    January 19th, 2009

    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

  • coeus

    January 19th, 2009

    argh!
    in words:

    (_,bs)
    “arrow-left”
    ( foldr (
    “GTGTGT”
    ) (arr id) $ replicate (length as) (chainlink f) ) -< (as,[])

  • Lane

    January 20th, 2009

    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.

  • coeus

    January 20th, 2009

    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.
    goodn8.
    - marc

  • Lane

    February 1st, 2009

    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.

  • No trackbacks yet

Leave a Comment

* are Required fields