I know that for data types that derive Data.Data, constrFields
gives the list of field names. Looking at the GHC.Generics documentation, I think the same should be possible for Generic
as well. (but miserably failed to figure out how to do it myself).
More specifically, I am looking for two things:
List all record fields
... within a Haskell program. I knew that aeson is capable of automatically inferring the JSON representation of any record data type that derives Generic
, but reading its source code only confirmed that I'm clueless here. From what I can guess, aeson must be able to obtain all field names (as String
s or ByteString
s) from a record data type, as well as their types (that has the type of something like TypeRep
in Data.Typeable, or an instance of Eq
: anything that can be used for case
block pattern matching would do).
I vaguely assume that creating a class and instances for M1
, :*:
, etc. is the way, but I couldn't make it to the type checker.
Inspect a record selector
To get the record data type it belongs to, the record field name (as String
), etc.
For example, given
data Record = Record
{ recordId :: Int32
, recordName :: ByteString
} deriving Generic
A function magic
that is like
typeOf (Record {}) == typeOf (magic recordId)
Are these possible with deriving Generic
, or do I have to resort to Template Haskell?
List all record fields
This one is very much possible, and it's indeed done by recursing on the structure of
Rep
, using a class. The solution below works for single-constructor types and returns empty string names for fields without selectors:Now we can have:
The least obvious part here is
selName
andSelector
: this class can be found inGHC.Generics
, and it allows us to extract the selector names from the generated selector types. In the case ofRecord
, the representation isand the selector types are
Main.S1_0_0Record
andMain.S1_0_1Record
. We can only access these types by extracting them from theRep
type using classes or type families, because GHC doesn't export them. Anyway,selName
gets us the selector name from anyM1
node with ans
selector tag (it has a more general typet s f a -> String
but that doesn't concern us here).It's also possible to handle multiple constructors, and have
selectors
return[[(String, TypeRep)]]
. In that case we would probably have two classes, one similar to the one above, used for extracting selectors from a given constructor, and another class for gathering the lists for constructors.Inspect a record selector
It's easy to get the record type from a function:
Or statically:
However, without TH we can't know whether a function is a legitimate selector or just a function with the right type; they're indistinguishable in Haskell. There is no way to inspect the name "recordId" in
magic recordId
.