...
Often I use an arity family of starling combinators to manipulate records:
star :: (r -> a -> ans) -> (r -> a) -> r -> ans
star2 :: (r -> a -> b -> ans) -> (r -> a) -> (r -> b) -> r -> ans
star comb f1 x = comb x (f1 x)
star2 comb f1 f2 x = comb x (f1 x) (f2 x)
Tracking source position in a parser is a typical example:
-- Example a 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 (\s i -> s { src_column=i+1 }) src_column
incrTab :: SrcPos -> SrcPos
incrTab = star2 (\s i t -> s { src_column=i+t }) src_column src_tab_stop
incrLine :: SrcPos -> SrcPos
incrLine = star (\s i -> s { src_line =i+1, src_column=1 }) src_line
For the 1 arity case, infix obviously works:
incrLine :: SrcPos -> SrcPos
incrLine = (\s i -> s { src_line =i+1, src_column=1 }) `star` src_line
However, giving `star` the appropriate left fixity means composition works well, removing the need for an arity family:
incrTab :: SrcPos -> SrcPos
incrTab = (\s i t -> s { src_column=i+t }) `star` src_column `star` src_tab_stop
As Applictive's (<*>) is starling for the function instance, I don't even need a new function:
incrTab :: SrcPos -> SrcPos
incrTab = (\s i t -> s { src_column=i+t }) <*> src_column <*> src_tab_stop
Note, I don't use (<$>) and the left hand function has one more argument 's' than usual for the applicative style.