Search code examples
haskellmetaprogrammingtype-level-computationghc-genericsgenerics-sop

Deriving projection functions using `generics-sop`


How would I go about deriving the function

getField :: (Generic a, HasDatatypeInfo a) => Proxy (name :: Symbol) -> a -> b

to project a field from an arbitrary record using a type-level string (Symbol), using the generics-sop library?

This is similar to Retrieving record function in generic SOP, but I have the following problems:

  • The OP does not explain how to go the last mile to get the signature I desire.
  • The OP defines complex special-purpose helper types, which I am keen to avoid
  • The given solution only errors out at runtime, but compile-time matching should be possible, since a type-level DataTypeInfo is provided through the DatatypeInfoOf type family (nice to have, but not necessary).

The lens-sop package also seems to do something similar, but I can't work out how to make it work for me.

I would also prefer a solution that uses the IsProductType typeclass.


Solution

  • As of version 0.1.1.0, records-sop provides this function:

    getField :: forall s a b ra. (IsRecord a ra, IsElemOf s b ra) => a -> b
    

    which needs the field name supplied as a type application rather than a proxy, like so:

    data Foo = Foo { bar :: Int }
    
    getField @"bar" (Foo 42) === 42
    

    This provides compile-time extraction, although it will still need a bit of casting around to fit in with existing code in my project that manipulates standard generics-sop metadata.

    This only works on single-constructor types. @dfeuer's answer also supports sum types.

    Thank you @kosmikus, the coauthor of generics-sop and author of records-sop, for implementing this in response to this question!