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
mk
returning? Why is it a functor? What is the interpretation of its result? I can see that theK1 i c
instance ofMk
returns a function (I understand this is a functor) that takes a value and wraps it inK1
, butmk
forMk (l :*: r)
andMk (M1 i c f)
are completely lost on me.I'm guessing
Compose
comes fromData.Functor.Compose
, which means that when I dofmap f x
, it does thefmap
two levels deep into the composed functors. But I can't make sense of the nestedfmap
s inside theCompose
.For the instance of
M1 i c f
, I thought it would just wrap the inner values inM1
, so the need toM1 <$> mk
orfmap M1 mk
makes 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.