Prettier functions for wrapping and wrapping
The post Semantic editor combinators gave an example of a pattern that comes up a lot for me in Haskell programming.
I want to apply functions inside of a newtype without cumbersome unwrapping and wrapping of the representation insides.
While chatting with Daniel Peebles in #haskell today, the realization hit me that these “higher-order wrappers” can not only make other code pretty, but can themselves be expressed more beautifully and clearly, using two of the combinators given in that post.
The example I gave was type composition, taken from the TypeCompose library:
newtype (g :. f) a = O { unO :: g (f a) }
The convenient higher-order wrappers apply n-ary function within O constructors:
inO h (O gfa) = O (h gfa)
inO2 h (O gfa) (O gfa') = O (h gfa gfa')
...
Then I get to implement Functor and Applicative instances in the style of semantic editor combinators.
instance (Functor g, Functor f) => Functor (g :. f) where
fmap = inO . fmap . fmap
instance (Applicative g, Applicative f) => Applicative (g :. f) where
pure = O . pure . pure
(<*>) = (inO2 . liftA2) (<*>)
The point-free definitions I gave before are pretty cryptic if you’re not used to the style:
inO = ( O .) . (. unO)
inO2 = (inO .) . (. unO)
inO3 = (inO2 .) . (. unO)
...
What dawned on me today is that I can instead say what I mean plainly: inO applies unO to the argument and O to the result.
inO = result O . argument unO
Similarly, inO2 applies unO to the (first) argument and inO to the resulting function.
Similarly for inO3:
inO2 = result inO . argument unO
inO3 = result inO2 . argument unO
...
The unwrapping and wrapping don’t interact, so, we can write equivalent definitions, swapping the compositions:
inO2 = argument unO . result inO
Equivalence follows from associativity of function composition.
Conal Elliott » Blog Archive » Sequences, segments, and signals:
[…] About « Prettier functions for wrapping and wrapping […]
5 December 2008, 12:14 amConal Elliott » Blog Archive » Another angle on functional future values:
[…] definitions of these helpers are very simple with the ideas from Prettier functions for wrapping and wrapping and a lovely notation from Matt Hellige’s Pointless […]
4 January 2009, 8:02 pmconal:
In December of 2008, Matt Hellige suggested a notational refinement that I’ve been using & enjoying in my day-to-day programming ever since. See his post Pointless Fun.
Above, I suggested redefining
inOto “say what I mean plainly:inOappliesunOto the argument andOto the result.”Matt’s idea is to package up this pattern of use of
resultandargumentand give it a name. And not just any name, but an infix operator that looks like an arrow and is right-associative, to mimic the notation of function types.Now
inO,inO2, etc have lovely, simple definitions:inO = unO ~> O inO2 = unO ~> inO inO3 = unO ~> inO2 -- ...Written out,
which can be read as saying that the semantic editor combinator
inO3passes three arguments throughunObefore invoking a given editor and then the editor’s result throughO.This notational technique can be used in many other ways besides stripping and restoring
newtypeconstructors. Matt’s examples include use ofidwhen arguments and/or results do not need transforming.Afterthought: SECs built up with
9 March 2010, 10:16 am(~>)(or explicitly withresultandargument) remind me vaguely of method combination in some object systems, like CLOS, but in a functional style — where after and before methods combine with inherited methods via function composition rather than sequential execution. Even this apparent difference diminishes if one considers a standard denotational semantics of sequential execution as function composition. For instance, a denotational semantics for method combination might be written elegantly in terms ofargument,result, and(~>).Conal Elliott » Blog Archive » Another angle on zippers:
[…] inO is from Control.Compose, and is defined using the ideas from Prettier functions for wrapping and wrapping and the notational improvement from Matt Hellige’s Pointless […]
29 July 2010, 6:38 pm