Systematic treatment of static arguments

Hello list The Monad and Applicative instances for functions are "equivalent" to the respective Reader vesions (I use "equivalent" along the lines of - operationally the same but without the type distinction / newtype). There is also the Monoid instance for functions which is pretty slick. Has anyone looked at variations with two or more static arguments though? For example mappend with two static arguments needs to be defined either as a new function:
mappendR2 :: Monoid a => (r1 -> r2 -> a) -> (r1 -> r2 -> a) -> r1 -> r2 -> a mappendR2 f g = \x y -> f x y `mappend` g x y
or an overlapping instance:
instance Monoid a => OPlus (r1 -> r2 -> a) where f `mappend` g = \x y -> f x y `mappend` g x y
Working in graphics I've found two static arguments comes up quite often - preliminarily most of my functions are functions from the DrawingContext to something (drawing context is an environment that tracks line width, stroke colour, fill colour, etc.): fn1 :: DrawingCtx -> a Many of my functions statically use a 'start' point as the only coordinate reference, so they are in a "coordinate free" style: fn2 :: Point -> DrawingCtx -> a Some functions even have have a third static argument, for example drawing arrowheads for lines seems nice if the angle of the line is parametric and not used explicitly: fn3 :: Radian -> Point -> DrawingCtx -> a The help from Applicative, Monad and Monoid is "used up" by the one static argument version, so I find that I have to introduce points (in the pointed / point-free sense not in the graphic sense) in the fn2 and fn3 versions where a point-free version might still be nice. Is there any prior work looking at sets of combinators for these higher arity functions - papers or code? I'd prefer not to introduce a whole new lexicon of combinator names if I can help it. Thanks Stephen

On 10/16/10 3:39 PM, Stephen Tetley wrote:
Hello list
The Monad and Applicative instances for functions are "equivalent" to the respective Reader vesions (I use "equivalent" along the lines of - operationally the same but without the type distinction / newtype). There is also the Monoid instance for functions which is pretty slick.
Has anyone looked at variations with two or more static arguments though?
For example mappend with two static arguments needs to be defined either as a new function:
mappendR2 :: Monoid a => (r1 -> r2 -> a) -> (r1 -> r2 -> a) -> r1 -> r2 -> a mappendR2 f g = \x y -> f x y `mappend` g x y
or an overlapping instance:
instance Monoid a => OPlus (r1 -> r2 -> a) where f `mappend` g = \x y -> f x y `mappend` g x y
It's not the prettiest, but you can also just make use of uncurrying and have (r1,r2)->a. Via closed cartesian categories, the versions with additional arguments are "uninteresting": which is to say, equivalent to the uncurried version. You may want to introduce a shorthand though, > mappendR2 f g = curry (uncurry f `mappend` uncurry g)
Working in graphics I've found two static arguments comes up quite often - preliminarily most of my functions are functions from the DrawingContext to something (drawing context is an environment that tracks line width, stroke colour, fill colour, etc.): [...] Many of my functions statically use a 'start' point as the only coordinate reference, so they are in a "coordinate free" style:
This sounds like the (r1,r2)->a approach is even reasonable for capturing the actual semantics of your program. Though you may want to rename (Point,DrawingCtx) to something like Coordinateful_DrawingCtx. -- Live well, ~wren

On Sat, Oct 16, 2010 at 21:39, Stephen Tetley
Hello list
The Monad and Applicative instances for functions are "equivalent" to the respective Reader vesions (I use "equivalent" along the lines of - operationally the same but without the type distinction / newtype). There is also the Monoid instance for functions which is pretty slick.
Has anyone looked at variations with two or more static arguments though?
For example mappend with two static arguments needs to be defined either as a new function:
mappendR2 :: Monoid a => (r1 -> r2 -> a) -> (r1 -> r2 -> a) -> r1 -> r2 -> a mappendR2 f g = \x y -> f x y `mappend` g x y
or an overlapping instance:
instance Monoid a => OPlus (r1 -> r2 -> a) where f `mappend` g = \x y -> f x y `mappend` g x y
I think you can just use the original instance for Monoid for this. It declares instance Monoid b => Monoid a -> b since 'b' can itself be a function (and r1 -> r2 -> a is parenthesized as r1 -> (r2 -> a)), this will work. 'b' can be instantiated to (r2 -> a) in your case, which is a Monoid due to another instance for functions, this time with 'b' instantiated to 'a'. This doesn't work for Reader, though. There, you're probably easier off using just a tuple, or, even better, a record, where you can use the 'asks' function to extract a single component. Erik

Hi Erik and wren
Thanks for both messages.
On 17 October 2010 10:12, Erik Hesselink
I think you can just use the original instance for Monoid for this. It declares
instance Monoid b => Monoid a -> b
since 'b' can itself be a function (and r1 -> r2 -> a is parenthesized as r1 -> (r2 -> a)), this will work. 'b' can be instantiated to (r2 -> a) in your case, which is a Monoid due to another instance for functions, this time with 'b' instantiated to 'a'.
Thanks - it does work correctly, I was thinking it had to be (r1 -> r1 -> a), but (r1 -> r2 -> a) is fine too. Somewhere when I was experimenting with GHCi, I must have misread an error message to make me think I needed a new function mappendR2 for (r1 -> r2 -> a).
This doesn't work for Reader, though. There, you're probably easier off using just a tuple, or, even better, a record, where you can use the 'asks' function to extract a single component.
I'm not sure about that one - using a record mandates the upfront work of a record for each combination and then using Applicative or Monad combinators with 'asks' for each operation. By extending the combinator set I can avoid a lot of the clutter from 'asks' but the downside is I'm introducing my own notation and naming system. Using a tuple as wren also suggested would probably mean I'd likewise have to make extensive use of tuple projection functions rather than 'asks'. I think what I'd doing has similarities to Conal Elliott's 'semantic editor combinators' so I'll investigate them again, although I feel Conal's notation is too general for my purposes in this case. Conal encodes a "path" directing what the lifted function operates on as part of each "editor", because my "paths" are shorter and I have fewer of them I'd prefer to encode them directly as named combinators. Thanks again Stephen
participants (3)
-
Erik Hesselink
-
Stephen Tetley
-
wren ng thornton