The compiler error here is helpful, but only in that annoying way where it tells you exactly what is wrong but not why it is wrong.
Expected a type but "Rep a" has kind "* -> *".
So the problem here is that Rep
(a type family) needs two arguments (call them a
and p
, as in Rep a p
); it as a type-level function maps these two type arguments into the "generic" type. For example,
data Empty deriving Generic
instance Generic Empty where
type Rep Empty =
D1 ('MetaData "Empty" "Main" "package-name" 'False) V1
-- taken from https://hackage.haskell.org/package/base-4.9.0.0/docs/GHC-Generics.htm
a
, e.g. Empty
, represents the type from which we are genericizing.
p
is a dummy type so that we can reuse our representation types for higher-level types (see Generic1
in the documentation).
So, in the above example, Rep Empty p
would simplify to D1 ('MetaData ...) V1 p
.
We can usually ignore p
except when it comes to defining new typeclasses that take advantage of generics. We want to pattern match on on types like D1 ('MetaData ...) V1 p
but we need some way of handling the extra parameter.
A trick then is to treat D1 ('MetaData ...) V1
like a higher-level type (like a functor). This is our f
in GDefault
.
class GDefault f where
gdef :: f a
Yes a
will always be this stupid parameter that we will never use, but in return for line noise we get the ability to pattern match on the f
in our instances. Here are four instances that allow for automatic generic def
implementations for product types (:*:
being a lifted tuple):
instance GDefault U1 where
gdef = U1
instance Default a => GDefault (K1 i a) where
gdef = K1 def
instance (GDefault a, GDefault b) => GDefault (a :*: b) where
gdef = gdef :*: gdef
instance GDefault a => GDefault (M1 i c a) where
gdef = M1 gdef
This, along with some sensible defaults for the numeric tower, will let us define datatypes like data Foo = Foo Int Char Float deriving (Show, Generic)
and evaluate show (def :: Foo)
to "Foo 0 0 0.0"
.
Your code had gdef :: a
, which is the wrong kind. We want gdef :: f a
because the typeclass is defined on types with kind * -> *
, hence the error message.
And to take advantage of this helper class, we do much as you did:
class Default a where
def :: a
default def :: (Generic a, GDefault (Rep a)) => a
def = to gdef
to :: Rep a x -> a
introduces a spurious x
, which unifies with our gdef :: f a
to produce f ~ Rep a
, throwing away the x
and being exactly what we intended.
You can see this approach elaborated in the data-default
package.
f
to have kind* -> *
(since it uses it asf a
) but your class argumenta
(ofGDefault
) expects only a type (so kind*
), but you are still feeding it something of kind* -> *
. – Alec Sep 21 '16 at 14:53