Say I have a typeclass:
data Proxy a = Proxy
class Fixed a where
fixed :: Proxy a -> Int
The definition for fixed is quite trivial so I derive it using GHC.Generics:
class GFixed f where
gfixed :: Proxy (f a) -> Int
instance (GFixed f, GFixed g) => GFixed (f :*: g) where ...
instance (GFixed f, GFixed g) => GFixed (f :+: g) where ...
instance GFixed f => GFixed (M1 i c f) where ...
instance Fixed a => GFixed (K1 i a) where ...
....
default fixed :: (Generic a, GFixed (Rep a)) => Proxy a -> Int
fixed _ = fixed (Proxy :: Proxy (Rep a b))
I don't include an instance for GFixed U1 because it doesn't make sense to have an instance
of Fixed for void types. My understanding of Generics machinery is not very good - specifically, what the types of M1 and K1 mean. The question is as follows: can I restrict GFixed at the type level, so that the default definition of fixed doesn't work with recursive types?
For example, if I write:
data Void
instance Fixed Void
I get a type error: No instance for (GFixed V1). I would like to get type error for things like instance Fixed [Int].
The documentation is moderately helpful for the meanings of the constructors. M1 specifies meta-information (such as the names of record selectors), and K1 is a bit of a grab-bag of various things with kind *. If you want to disallow all recursion, you'll need to ensure that no instance in scope matches K1 R a. You'll still want some of the other K instances in scope, so you should change
instance Fixed a => GFixed (K1 i a) where
to
instance Fixed a => GFixed (K1 P a) where
I don't know if there are other values that can be the first parameter to K1, but if any arise it should be safe to add them, except for K1 R of course.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With