[Haskell-cafe] Pointfree composition for higher arity

Stephen Tetley stephen.tetley at gmail.com
Wed Feb 17 17:54:18 EST 2010


Hi Sean

Thanks for the comment.

David Menendez pointed out on the other thread that they generalize
nicely to functors:
http://www.haskell.org/pipermail/haskell-cafe/2009-December/071428.html

Typographically they are a pun on ML's composition operator (o), if
you don't define o - (aka 'monocle' - little need as we've already got
(.) ) then I'd imagine there won't be too many name clashes with
people's existing code. 'Specs' was an obvious name for the family
once you use them infix.

Many of the combinator 'birds' that aren't already used by Haskell
seem most useful for permuting other combinator birds rather than
programming with - their argument orders not being ideal. The most
useful ones I've found that expand to higher arities have the first
argument as a 'combiner' (combining all the intermediate results), one
or more 'functional' arguments (producing intermediate results from
the 'data' arguments), then the 'data' arguments themselves.


The liftM and liftA family are of this form, considering the
functional type instances ((->) a):

liftA  :: (a -> ans) -> (r -> a) -> r -> ans
liftA2 :: (a -> b -> ans) -> (r -> a) -> (r -> b) -> r -> ans
liftA3 :: (a -> b -> c -> ans) -> (r -> a) -> (r -> b) -> (r -> c) -> r -> ans

... or the full general versions:

liftA  :: Applicative f => (a -> b) -> f a -> f b
liftA2 :: Applicative f => (a -> b -> c) -> f a -> f b -> f c
liftA3 :: Applicative f => (a -> b -> c -> d) -> f a -> f b -> f c -> f d

liftA  for functions is bluebird
liftA2 for functions is phoenix or starling' or Big Phi

----

An arity family of Starlings can be quite nice for manipulating records.

starling :: (a -> b -> c) -> (a -> b) -> a -> c


star  :: (a -> r -> ans) -> (r -> a) -> r -> ans
star2 :: (a -> b -> r -> ans) -> (r -> a) -> (r -> b) -> r -> ans
star3 :: (a -> b -> c -> r -> ans) -> (r -> a) -> (r -> b) -> (r -> c)
-> r -> ans
star4 :: (a -> b -> c -> d -> r -> ans)
      -> (r -> a) -> (r -> b) -> (r -> c) -> (r -> d) -> r -> ans
star5 :: (a -> b -> c -> d -> e -> r -> ans)
      -> (r -> a) -> (r -> b) -> (r -> c) -> (r -> d) -> (r -> e) -> r -> ans

An example - tracking the source position in a parser:

data SrcPos = SrcPos {
                 src_line       :: Int,
                 src_column     :: Int,
                 src_tab_stop   :: Int
               }


incrCol :: SrcPos -> SrcPos
incrCol = star (\i s -> s { src_column=i+1 }) src_column

incrTab :: SrcPos -> SrcPos
incrTab = star2 (\i t s -> s { src_column=i+t }) src_column src_tab_stop


incrLine :: SrcPos -> SrcPos
incrLine = star (\i s -> s { src_line =i+1, src_column=1 }) src_line

----

Permuted variants of cardinal-prime can be useful for adapting a
function to a slightly different type. I originally called them combfi
etc. 'f' to indicate where a function was applied, and 'i' where
identity was applied; but I'm no so happy with the name now:

combfi   :: (c -> b -> d) -> (a -> c) -> a -> b -> d
combfii  :: (d -> b -> c -> e) -> (a -> d) -> a -> b -> c -> e
combfiii :: (e -> b -> c -> d -> f) -> (a -> e) -> a -> b -> c -> d -> f


I've sometimes used them to generalize a function's interface, e.g a
pretty printer:

f1 :: Doc -> Doc -> Doc

adapted_f1 :: Num a => a -> Doc -> Doc
adapted_f1 = f1 `combfi` (int . fromIntegral)

... not particularly compelling I'll admit.


Slowly I'm synthesizing sets of 'em when they seem to apply to an
interesting use. Actually finding valid uses and coining good
names is harder than defining them. The 'specs' were lucky in that
they pretty much named themselves.

Best wishes

Stephen


More information about the Haskell-Cafe mailing list