Using standard haskell generics libraries for typed type-isomorphisms

Viewed 865

There are several generics libraries with numerous overlapping modules in just the Haskell Platform alone (syb, Data.Typeable, Data.Data, GHC.Generics), but I'm having trouble with a very basic generic programming task.

I want to be able to convert between types of the same shape, i.e. I want a polymorphic, typed conversion function between isomorphic types, essentially what is offered at the end of this paper(PDF) where indexed type families are mentioned.

I'm not concerned with scrapping my boilerplate, but rather with being able to build new libraries around sum and product abstractions.

The question below is in terms of GHC.Generic which I thought was closest to what I needed, but other solutions are welcome.


The following two types have the same shape

data Pair = Pair Char Int deriving (Generic, Show)
data Pair2 = Pair2 Char Int deriving (Generic, Show)

I want to convert values between them using GHC.Generics. The following fails to typecheck because of all the phantom parameters and other nonsense:

f :: Pair -> Pair2
f = to . from

Ultimately I want a function akin to fromInteger that has a polymorphic return value for any Generic (or whatever other class could support this) instance. I guess I'm looking for something like GHC.Generics:

--class:
type family NormalForm a
class ToGeneric a where
    to :: a -> NormalForm a
class FromGeneric b where
    from :: NormalForm b -> b

--examples:
data A = A Char Int deriving Show
data B = B Char Int deriving Show

type instance NormalForm A = (Char,Int)
instance ToGeneric A where
    to (A a b) = (a,b)
instance FromGeneric A where
    from (a,b) = A a b

type instance NormalForm B = (Char,Int)
instance ToGeneric B where
    to (B a b) = (a,b)
instance FromGeneric B where
    from (a,b) = B a b

-- the function I'm looking for
coerce :: (ToGeneric a, FromGeneric b, NormalForm a ~ NormalForm b)=> a -> b
coerce = from . to

With the above we can do everything I want:

*Main> (coerce $A 'a' 1) :: B
B 'a' 1
*Main> (coerce $A 'a' 1) :: A
A 'a' 1

EDIT: This is how Nathan Howell's f function seems to work below, actually.

Questions

  1. Is this possible to do with libraries currently in the haskell platform?

  2. If not, could a library be defined that leveraged the existing deriving mechanism for Generic, Data, etc. without resorting to TH?

2 Answers
Related