Overview =Metamorphosis= provides some Template Haskell functions to transform types and generate conversion function between the originals types and the metamorphosed ones. Conversions can be straightforward or done within an applicative functor allowing, traversing types but also automatic lifting of missing values. It allows for example to :
split a type into many smaller ones
data AB = AB Int String
to
data A = A Int
data B = B String
data A = A Int
data B = B String
to
data AB = AB Int String
data Product = Product { name :: String, price :: Double }
to
data ProductM = ProductM { name :: String, price :: (Maybe Double) }
data Product = Product { name :: String, price :: Double }
to
data ProductF f = ProductF { name :: String, price :: f Double }
etc ...
For example, let's say I have a bunch of product with a name and price :
data Product = Product { name :: String, amount :: Double}
I want to be able to group them by name and sum the amount. In SQL I would do
SELECT name, SUM amount
FROM products
GROUP by name
In haskell , I would like to be able to do something similar, which at some point involves collapsing all product with an identical name to one product. I could almost use a Monoid instance for Product, but I have a problem with aggregating the names. One solution would be to have, name being a =First String= instead of =String=, or maybe =Last String= or even maybe just ignore it and use =Const () String=. The traditional answer to this is, just define
data Product f = Product { name :: f String, amount :: Double}
and then I can define a monoid instance for =Monoid (f String) => Monoid (Product f)=. Again, I might not be able to modify =Product= as it might be generated from a db schema. I can then have a =Product= (parametric) and a =DbProduct= non-parametric, but there we go. I need converters between them. Better generate =Product= from =DbProduct= and the required converters.
It might be because I'm not thinking and modelling the haskell way, and should realize there are code smells, which I should sort out. However, I often found that the solution to my problems could be easily solved by just copy pasting an existing type, add or modify a few fields and write a converter between the old and the new type. But I like DRY code and don't copy paste, so the answer is either TH or Generics. Generics, can probably take care of the converter but it can't generate the new data types, so I'll have to go the TH way.
This allows every converters to be written with the following shapes and compiles even if types mismatch. For example, given
data A a = A a
data B b = B b (Maybe Int)
The converter will be
aAtoB (A a) = B <$> convertA a <*> convertA ()
=()= will be converted to =Nothing= (needed for =Maybe Int=) and depending and =a= and =b=, we could convert between =A= and =B= or not. We could convert between =A Int= and =B Int= or =A (Maybe Int)= to =B ([Int])= using =Identity= (=Maybe Int= can be converted without loss to a =[Int=). However converting between =A (Maybe Int)= and =B Int= would require using =Maybe=. Converting between =A Double= and =B Int= wouldn't compiles. However, the compilation will fail not at the converter declaration, but when trying to use it.