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.

4 Comments

  1. Conal Elliott » Blog Archive » Sequences, segments, and signals:

    [...] About « Prettier functions for wrapping and wrapping [...]

  2. Conal 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 [...]

  3. conal:

    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 inO to “say what I mean plainly: inO applies unO to the argument and O to the result.”

    inO = result O . argument unO

    Matt’s idea is to package up this pattern of use of result and argument and 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.

    infixr 2 ~>
    f ~> g = argument f . result g

    Now inO, inO2, etc have lovely, simple definitions:

    inO  = unO ~> O
    inO2 = unO ~> inO
    inO3 = unO ~> inO2
    -- ...

    Written out,

    inO3 = unO ~> unO ~> unO ~> O

    which can be read as saying that the semantic editor combinator inO3 passes three arguments through unO before invoking a given editor and then the editor’s result through O.

    This notational technique can be used in many other ways besides stripping and restoring newtype constructors. Matt’s examples include use of id when arguments and/or results do not need transforming.

    Afterthought: SECs built up with (~>) (or explicitly with result and argument) 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 denotation semantics for method combination might be written elegantly in terms of argument, result, and (~>).

  4. 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 [...]

Leave a comment