This may or may not answer your question depending on what you really need to do.
The standard solution is to use newtype to define new instances. For instance.
newtype Pair a = Pair (a, a) instance C ab Pair where f = ...
However, this will cause f to be of type
f :: (a, b) -> (Pair a, Pair b)
instead of the desired
f :: (a, b) -> ((a, a), (b, b))
The latter can be restored in a boring way:
f' :: (a, b) -> ((a, a), (b, b)) f' p = case fp of (Pair x, Pair y) -> (x, y)
Now writing functions of an βadapterβ, such as f' , looks redundant: after all, we just remove the newtype shell, which does not change the presentation of the runtime. Worse, it can be inefficient: consider the conversion of [Pair a] to [(a, a)] . To do this, we will need to display the entire list, so we pay O (n), in fact, do nothing.
An effective alternative can be built using safe constraints :
import Data.Coerce f'' :: forall a b. (a, b) -> ((a, a), (b, b)) f'' = coerce (f :: (a, b) -> (Pair a, Pair b))
This allows you to use newtype to select an instance of the instance, but at the same time remove them when they interfere.
Now, if your goal is to define new instances without newtype s, this helps a little. If instead you just want to remove the newtype wrappers from the instance methods, this may help.