[Haskell-cafe] Sequence of lifting transformation operators

Tyson Whitehead twhitehead at gmail.com
Fri Jan 27 20:39:52 UTC 2017


A couple of times I find myself wishing for an easy way to transform a
function by picking and choosing which of its operators are lifted.
For example, consider

f <$> x1 <*> pure x2 <*> x3 <*> pure x4

we can put all the details of this into f itself

f' x1 x2 x3 x4 = f <$> x1 <*> pure x2 <*> x3 <*> pure x4

and then just write

f' x1 x2 x3 x4

I think it might be nice to have a series of transformation functions
that made this easy to write.  Something like a series of operator
like so

f' = f <$_$_>

where it would be read that f' is a lifted f such that the first
argument is lifted, the second is not, the third is, and the fourth is
not (this is consistent with f <$> returning a being a lifted f with
first argument being lifted).

This would make code such as the following

flip (go finalX finalY) y `liftM` mx

= go <__$_> finalX finalY mx y

https://github.com/snoyberg/conduit/blob/be803218b5b2acaad2eb720ca3a27a4d0734fba8/conduit/Data/Conduit/Internal/Conduit.hs#L543

much more robust to write (how it gets written now depends very much
on the number of arguments, their order, which ones are lifted, etc.),
a whole lot easier to read, and don't even get me started on the
points free use (go <__$_>)!

A library could easily provide the first six or so variants (2^6 = 64
functions).  If they were really useful the compiler could provide the
rest.

<$> :: (a -> b) -> f a -> f b
<$$> :: (a -> b -> c) -> f a -> f b -> f c
<$_> :: (a -> b -> c) -> f a -> b -> f c
<_$> :: (a -> b -> c) -> a -> f b -> f c
...

Cheers!  -Tyson

PS:  It would be nice to also have ones that work with f being lifted.
That is like how we have <$> for an unlifted f and <*> for a lifted f.

<*> :: f (a -> b) -> f a -> f b
<**> :: f (a -> b -> c) -> f a -> f b -> f c
<*_> :: f (a -> b -> c) -> f a -> b -> f c
<_*> :: f (a -> b -> c) -> a -> f b -> f c
...

A bit of a pain here is that <**> is actually already taken as <*>
with reversed arguments.  Possibly this would call for something like
this instead

<$^> :: (a -> b) -> f a -> f b
<$^^> :: (a -> b -> c) -> f a -> f b -> f c
<$^_> :: (a -> b -> c) -> f a -> b -> f c
<$_^> :: (a -> b -> c) -> a -> f b -> f c
...

and

<*^> :: f (a -> b) -> f a -> f b
<*^^> :: f (a -> b -> c) -> f a -> f b -> f c
<*^_> :: f (a -> b -> c) -> f a -> b -> f c
<*_^> :: f (a -> b -> c) -> a -> f b -> f c
...

where they are prefixed by $ or * to indicate what type f is and then
_ encodes an unlifted argument position and ^ a lifted argument
position.


More information about the Haskell-Cafe mailing list