I'm trying to learn about how to use GHC.Generics. A fascinating topic but daunting.
While reading through the blog entry 24 Days of GHC Extensions: DeriveGeneric, I learned how to take a value and navigate its Rep. Okay.
However, reading the blog entry Building data constructors with GHC Generics which describes the analog of constructing the Rep and converting it back to a value, I got stumped. I've read through a number of other resources, but to no great help.
In the blog entry is the following code. First, constructing the Rep:
class Functor f => Mk rep f | rep -> f where
mk :: f (rep a)
instance Mk (K1 i c) ((->) c) where
mk = \x -> K1 x
instance (Mk l fl, Mk r fr) => Mk (l :*: r) (Compose fl fr) where
mk = Compose (fmap (\l -> fmap (\r -> l :*: r) mk) mk)
instance (Mk f f') => Mk (M1 i c f) f' where
mk = M1 <$> mk
Then, dealing with the Compose:
class Functor f => Apply f a b | f a -> b where
apply :: f a -> b
instance Apply ((->) a) b (a -> b) where
apply = id
instance (Apply g a b, Apply f b c) => Apply (Compose f g) a c where
apply (Compose x) = apply (fmap apply x)
Then dealing with type ambiguity:
type family Returns (f :: *) :: * where
Returns (a -> b) = Returns b
Returns r = r
make :: forall b f z. (Generic (Returns b), Apply f (Returns b) b, Mk (Rep (Returns b)) f) => b
make = apply (fmap (to :: Rep (Returns b) z -> (Returns b)) (mk :: f (Rep (Returns b) z)))
Wow.
Really, I'm stuck at the very beginning, at the class Mk where mk returns a functor. My questions:
What is
mkreturning? Why is it a functor? What is the interpretation of its result? I can see that theK1 i cinstance ofMkreturns a function (I understand this is a functor) that takes a value and wraps it inK1, butmkforMk (l :*: r)andMk (M1 i c f)are completely lost on me.I'm guessing
Composecomes fromData.Functor.Compose, which means that when I dofmap f x, it does thefmaptwo levels deep into the composed functors. But I can't make sense of the nestedfmaps inside theCompose.For the instance of
M1 i c f, I thought it would just wrap the inner values inM1, so the need toM1 <$> mkorfmap M1 mkmakes no sense to me.
Obviously I'm not grokking the intent or meaning of these instances and how these instances interact to create the final Rep. I am hoping someone can enlighten me and provide a good explanation of how to use GHC.Generics along the way.