Closed treeowl closed 2 years ago
You may be interested in th-reify-many
's reifyManyWithoutInstances
function, which recursively names mentioned in data type declarations that do not have instances of a particular class. For your program, an example of its usage might look like:
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE PackageImports #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}
module Foo where
import "aeson" Data.Aeson (ToJSON)
import "aeson" Data.Aeson.TH (deriveToJSON, defaultOptions)
import "base" GHC.Generics (Generic)
import "generic-deriving" Generics.Deriving.TH (deriveAll0)
import "th-reify-many" Language.Haskell.TH.ReifyMany
import Sigh (Gummy)
$(do -- Don't bother trying to define a Generic instance for Int
let genPred n = n /= ''Int
genericInsts <- reifyManyWithoutInstances ''Generic [''Gummy] genPred
>>= traverse deriveAll0
toJSONInsts <- reifyManyWithoutInstances ''ToJSON [''Gummy] (const True)
>>= traverse (deriveToJSON defaultOptions)
pure $ concat $ genericInsts ++ toJSONInsts)
Template Haskell reification works on non-exported entities, so this works even if Gel
is not exported. Moreover, reifyManyWithoutInstances
remembers names it has already seen before, so this also works if Gel
and Gummy
are mutually recursive.
That sounds like exactly what I'm looking for! Thanks!
By the way, why doesn't Int
have a Generic
instance? That seems very strange.
Before GHC 8.0, Int
actually did have a Generic
instance, which looked something like:
-- Int
data D_Int
data C_Int
instance Datatype D_Int where
datatypeName _ = "Int"
moduleName _ = "GHC.Int"
packageName _ = "base"
instance Constructor C_Int where
conName _ = "" -- JPM: I'm not sure this is the right implementation...
instance Generic Int where
type Rep Int = D1 D_Int (C1 C_Int (S1 NoSelector (Rec0 Int)))
from x = M1 (M1 (M1 (K1 x)))
to (M1 (M1 (M1 (K1 x)))) = x
This Rep
instance was dodgy, as it claimed that the structure of Int
was something like data Int = Int Int
. Back then, GHC.Generics
had no support for unlifted types like Int#
at all, so this was the best one could do. Ultimately, this instance (and similar instances for primitive types) were removed for the reasons outlined here.
Nowadays, one could hypothetically define a Generic
instance for Int
by using URec
to encode the fact that its field is Int#
. Then again, the amount of primitive types you could make this work for is pretty limited, so I've never seriously thought about trying to resurrect these kinds of Generic
instances. (To be honest, I think GHC.Generics
' design vis-à-vis unlifted types is flawed as a whole, but that's a story for another day...)
Sometimes, whole sets of types are missing
Generic
and other instances, and it's desirable to write orphan instances to work around that. For example, there might be a moduleTo derive many instances for
Gummy
generically, an app might need to derive instances for both types:Unfortunately, this gets much nastier when not all the types or constructors are exported:
Is there any way to write functions to help in this kind of situation? What about when there are mutually recursive types involved?