Generic programming using an approach inspired by GHC.Generics.
This library requires version 0.10 of the PureScript compiler, or later.
bower install purescript-generics-rep
PureScript's generics are supported by the Data.Generic.Rep.Generic type class:
class Generic a rep | a -> rep where
from :: a -> rep
to :: rep -> aThere are three interesting things here:
- The
reptype argument and associated functional dependency define a type function from user types (a) to their representations (rep). fromconverts a regular value into the representation type.toconverts a representation value back into a regular value.
This library provides standard representation types which can be used to represent any data types which can be expressed in PureScript code.
It is possible to write out Generic instances for our own types by hand, but doing so is very laborious. Instead, we can derive instances by using the derive keyword:
newtype Person = Person { name :: String, location :: String }
derive instance genericPerson :: Generic Person _Note that the second type argument, which represents the representation type, is specified as a type wildcard. This is useful, because representation types can get quite large, so it is inconvenient to type them out by hand in deriving declarations.
The key insight regarding generics is this: if we can write a function which works with any of the standard representation types, then we implement the same function for any instance of Generic. We can even exploit type information in our implementation by using additional type classes to reflect the type information at runtime.
purescript-generics-rep provides helper functions for implementing common type classes from the Prelude:
genericShowgives a default implementation ofshowfrom theShowclassgenericEqgives a default implementation ofeqfrom theEqclassgenericComparegives a default implementation ofcomparefrom theOrdclassgenericAppendgives a default implementation ofappendfrom theSemigroupclassgenericMemptygives a default implementation ofmemptyfrom theMonoidclass
Using these functions is as simple as dropping the generic implementation into your instances:
instance showPerson :: Show Person where
show = genericShow
instance eqPerson :: Eq Person where
eq = genericEq
instance ordPerson :: Ord Person where
compare = genericCompare
instance semigroupPerson :: Semigroup Person where
append = genericAppendGeneric deriving can be very convenient for code generation, but it comes with a performance penalty. Consider defining a Show instance using genericShow - instead of simply converting our data type directly to a String, we first convert it to the representation type, and then convert that representation into a String. Creating this intermediate structure comes with a cost.
Thankfully, the generics-rep approach means that we only need to perform a shallow copy of the data, up to the first data constructor or record, so in practice the performance cost is acceptable. In the case of foreign-generic, the benefits listed above usually outweight the performance cost, since we rarely need to parse or generate JSON in performance-critical sections of code in many applications.
API documentation is published on Pursuit.
- The documentation for the simple-json library includes a tutorial for using this library.
- Generic deriving of codecs for
Foreignvalues: purescript-foreign-generic - Generic deriving of codecs for purescript-argonaut: purescript-argonaut-generic