Lane's Blog

Roguestar, Haskell, and Fun

Skip to: Content | Sidebar | Footer

MaybeArrow?

4 January, 2009 (19:16) | Haskell | By: Lane

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.

Comments

Comment from coeus
Time 19 January 2009 at 10:45 pm

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.)

Comment from Lane
Time 19 January 2009 at 10:57 pm

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.

Comment from coeus
Time 19 January 2009 at 10:58 pm

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]

Comment from coeus
Time 19 January 2009 at 11:05 pm

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)

Comment from coeus
Time 19 January 2009 at 11:06 pm

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

Comment from coeus
Time 19 January 2009 at 11:10 pm

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

Comment from coeus
Time 19 January 2009 at 11:11 pm

argh!
in words:

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

Comment from Lane
Time 20 January 2009 at 12:00 am

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.

Comment from coeus
Time 20 January 2009 at 12:26 am

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

Comment from Lane
Time 1 February 2009 at 10:02 am

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.

Write a comment