Haskell function composition operator of type (c→d) → (a→b→c) → (a→b→d)
Ordinary function composition is of the type
(.) :: (b -> c) -> (a -> b) -> a -> c
I figure this should generalize to types like:
(.) :: (c -> d) -> (a -> b -> c) -> a -> b -> d
A concrete example: calculating difference-squared. We could write diffsq a b = (a - b) ^ 2
, but it feels like I should be able to compose the (-)
and (^2)
to write something like diffsq = (^2) . (-)
.
I can't, of course. One thing I can do is use a tuple instead of two arguments to (-)
, by transforming it with uncurry
, but this isn't the same.
Is it possible to do what I want? If not, what am I misunderstanding that makes me think it should be possible?
Note: This has effectively already been asked here, but the answer (that I suspect must exist) was not given.
My preferred implementation for this is
fmap . fmap :: (Functor f, Functor f1) => (a -> b) -> f (f1 a) -> f (f1 b)
If only because it is fairly easy to remember.
When instantiating f and f1 to (->) c
and (->) d
respectively you get the type
(a -> b) -> (c -> d -> a) -> c -> d -> b
which is the type of
(.) . (.) :: (b -> c) -> (a -> a1 -> b) -> a -> a1 -> c
but it is a bit easier to rattle off the fmap . fmap
version and it generalizes to other functors.
Sometimes this is written fmap fmap fmap
, but written as fmap . fmap
it can be more readily expanded to allow more arguments.
fmap . fmap . fmap
:: (Functor f, Functor g, Functor h) => (a -> b) -> f (g (h a)) -> f (g (h b))
fmap . fmap . fmap . fmap
:: (Functor f, Functor g, Functor h, Functor i) => (a -> b) -> f (g (h (i a))) -> f (g (h (i b))
etc.
In general fmap
composed with itself n times can be used to fmap
n levels deep!
And since functions form a Functor
, this provides plumbing for n arguments.
For more information, see Conal Elliott's Semantic Editor Combinators.
The misunderstanding is that you think of a function of type a -> b -> c
as a function of two arguments with return type c
, whereas it is in fact a function of one argument with return type b -> c
because the function type associates to the right (i.e. it's the same as a -> (b -> c)
. This makes it impossible to use the standard function composition operator.
To see why, try applying the (.)
operator which is of type (y -> z) -> (x -> y) -> (x -> z)
operator to two functions, g :: c -> d
and f :: a -> (b -> c)
. This means that we must unify y
with c
and also with b -> c
. This doesn't make much sense. How can y
be both c
and a function returning c
? That would have to be an infinite type. So this does not work.
Just because we can't use the standard composition operator, it doesn't stop us from defining our own.
compose2 :: (c -> d) -> (a -> b -> c) -> a -> b -> d
compose2 g f x y = g (f x y)
diffsq = (^2) `compose2` (-)
Usually it is better to avoid using point-free style in this case and just go with
diffsq a b = (a-b)^2
I don't know of a standard library function that does this, but the point-free pattern that accomplishes it is to compose the composition function:
(.) . (.) :: (b -> c) -> (a -> a1 -> b) -> a -> a1 -> c