Inspired by a question on polymorphic function between ADTs I'm trying to create isomorphisms between multiple (not just 2) types, so that every time I need an isomorphic but not the same type, I can sprinkle my code with some convert
.
Suppose I have 3 ADTs:
data AB = A | B deriving (Show)
data CD = C | D deriving (Show)
data EF = E | F deriving (Show)
Using lens
I can implement 2 isomorphisms between AB and CD, and CD and EF:
{-# LANGUAGE MultiParamTypeClasses #-}
class Isomorphic a b where
convert :: Iso' a b
instance Isomorphic AB CD where
convert = iso ab2cd cd2ab
where ab2cd A = C
ab2cd B = D
cd2ab C = A
cd2ab D = B
instance Isomorphic AB EF where
convert = iso ab2ef ef2ab
where ab2ef A = E
ab2ef B = F
ef2ab E = A
ef2ab F = B
Converting A
to E
is easy: A^.convert :: EF
. Converting D
to B
is also easy: D^.from convert :: AB
. But if I want to convert from C
to E
via A
, I have to annotate types for every intermediary conversion:
(C^.from convert :: AB)^.convert :: EF
I understand why compiler can't infer intermediate types. It could be that there are several isomorphisms through which one could get from C
to E
. But can I simplify my code so I don't manually annotate types everywhere?
I could just write yet another instance to convert directly between CD
and EF
, but what if I have more than 3 types? If I had 5 isomorphic types, I would have to specify 10 instances, because the number of isos between isomorphic objects is a number of edges in a complete graph, which is a triangular number. I'd rather specify n-1
instances, with a trade-off that I write more convert
or from convert
.
Is there an idiomatic way to establish isomorphisms between multiple types using Iso
from lens
so that there is least amount of boilerplate and I don't have to type-annotate everything? If I have to use TemplateHaskell for that, how do I do that?
The motivation is that in my work I have many ridiculously complicated but stupid types, where () -> (() -> ()) -> X
and ((), X)
are isomorphic to X
. I have to manually wrap and unwrap everything and I would like some polymorphic way to reduce the complicated types to simpler isomorphic types.