If I have these definitions
twice f = f . f
swap (x,y) = (y,x)
The type of twice is infered to (a -> a) -> a -> a and swap is infered to (a,b) -> (b,a).
If I write swap . swap the type of that expression is (a, b) -> (a, b).
But if I ask for twice swap its type is (a, a) -> (a, a).
I know that twice is restricting the type of swap. But I'm wondering if there is a way to write twice so that it accepts swap without restricting its type. That is, you can write twice swap and accept pairs where each component has a different type.
The same happens with flip :: (a -> b -> c) -> b -> a -> c, because twice flip :: (a -> a -> b) -> a -> a -> b.
This is tricky to do, because you need to express that the result- and argument types are polymorphic, yet in a particular functional relationship. You need to provide this relationship in an explicit way. The most common way of expressing a type-level function is with a type family, but such functions can not be named as first-class entities. Also they can't encode the bidirectional natural of type relations like in
swap.What you can do instead is represent the function with a unique type-tag, and link the argument- and result types together with a MPTC.
Now,
This is fairly versatile,
but it has its limitations as well, in particular the way I wrote it above the argument and result of the function must be inferrable from each other. But for many functions, only the argument type can be inferred from the result type and not vice versa, or sometimes only the result type from the arguments. You would have to use different classes to cover all the possible ways the type information can flow, I don't think there's a single way that does it all automatically.
Edit It turns out there is actually a single way that does it automatically, but, erm... don't try this at home, kids...