Safe Haskell  None 

Language  Haskell2010 
Simple operations on generic representations:
modify Generic
instances to tweak the behavior of generic
implementations as if you had declared a slightly different type.
This module provides the following microsurgeries:
RenameFields
: rename the fields of a record type.RenameConstrs
: rename the constructors.OnFields
: apply a type constructorf :: Type > Type
to every field.CopyRep
: use the generic representation of another type of the same shape.Typeage
: treat anewtype
as adata
type.Derecordify
: treat a type as if it weren't a record.
More complex surgeries can be found in genericdatasurgery but also, perhaps surprisingly, in genericlens (read more about this just below) and oneliner.
Surgeries can be used:
 to derive type class instances with the
DerivingVia
extension, using theSurgery
orProductSurgery
type synonyms (for classes with instances forGenerically
orGenericProduct
);  with the
Data
"synthetic type" for more involved transformations, for example using lenses in the next section.
Synopsis
 type Surgery (s :: *) (a :: *) = Generically (Surgery' s a)
 type ProductSurgery (s :: *) (a :: *) = GenericProduct (Surgery' s a)
 newtype Surgery' (s :: *) (a :: *) = Surgery' {
 unSurgery' :: a
 type family GSurgery (s :: *) (f :: k > *) :: k > *
 newtype Generically a = Generically {
 unGenerically :: a
 newtype GenericProduct a = GenericProduct {
 unGenericProduct :: a
 data Data r p
 toData :: Generic a => a > Data (Rep a) p
 fromData :: Generic a => Data (Rep a) p > a
 onData :: (UnifyRep r s, UnifyRep s r) => p (Data r x) (Data s y) > p (Data r x) (Data s y)
 data RenameFields (rnm :: *) :: *
 renameFields :: forall rnm f p. Coercible (GSurgery (RenameFields rnm) f) f => Data f p > Data (GSurgery (RenameFields rnm) f) p
 unrenameFields :: forall rnm f p. Coercible (GSurgery (RenameFields rnm) f) f => Data f p > Data (GSurgery (RenameFields rnm) f) p
 data RenameConstrs (rnm :: *) :: *
 renameConstrs :: forall rnm f p. Coercible (GSurgery (RenameConstrs rnm) f) f => Data f p > Data (GSurgery (RenameConstrs rnm) f) p
 unrenameConstrs :: forall rnm f p. Coercible (GSurgery (RenameConstrs rnm) f) f => Data f p > Data (GSurgery (RenameConstrs rnm) f) p
 type family (f :: *) @@ (s :: Symbol) :: Symbol
 data SId
 data SError
 data SConst (s :: Symbol)
 data SRename (xs :: [(Symbol, Symbol)]) (f :: *)
 data OnFields (f :: * > *) :: *
 type DOnFields (f :: * > *) (a :: *) = Data (GSurgery (OnFields f) (Rep a)) ()
 data CopyRep (a :: *) :: *
 copyRep :: forall a f p. Coercible (GSurgery (CopyRep a) f) f => Data f p > Data (GSurgery (CopyRep a) f) p
 uncopyRep :: forall a f p. Coercible f (GSurgery (CopyRep a) f) => Data (GSurgery (CopyRep a) f) p > Data f p
 data Typeage :: *
 typeage :: Coercible (GSurgery Typeage f) f => Data f p > Data (GSurgery Typeage f) p
 untypeage :: Coercible f (GSurgery Typeage f) => Data (GSurgery Typeage f) p > Data f p
 data Derecordify :: *
 derecordify :: Coercible (GSurgery Derecordify f) f => Data f p > Data (GSurgery Derecordify f) p
 underecordify :: Coercible f (GSurgery Derecordify f) => Data (GSurgery Derecordify f) p > Data f p
Surgeries with genericlens
One common and simple situation is to modify the type of some fields, for example wrapping them in a newtype.
We can leverage the genericlens
library, with the two functions below.
 Lens to a field named fd
in a Generic record.
field_ :: HasField_ fd s t a b => Lens s t a b  from genericlens
 Update a value through a lens (ASetter is a specialization of Lens).
over :: ASetter s t a b > (a > b) > s > t  from lens or microlens
For example, here is a record type:
data R = R { myField :: Int } deriving Generic
The function over (field_ @"myField")
applies the newtype constructor Opaque
Opaque
to the field
"myField"
, but this actually doesn't typecheck asis. With a bit of help
from this module, we can wrap that function as follows:
onData
(over (field_ @"myField")Opaque
) .toData
:: R >Data
_ _  type arguments hidden
The result has a type
, that from the point of view of GHC.Generics
looks just like Data
_ _R
but with the field "myField"
wrapped in
Opaque
, as if we had defined:
data R = R { myField ::Opaque
Int } derivingGeneric
Example usage
We derive an instance of Show
that hides the "myField"
field,
whatever its type.
instanceShow
R whereshowsPrec
n =gshowsPrec
n .onData
(over (field_ @"myField")Opaque
) .toData
show
(R 3) = "R {myField = _}"
Deriving via
type Surgery (s :: *) (a :: *) = Generically (Surgery' s a) Source #
Apply a microsurgery s
to a type a
for DerivingVia
.
For the Monoid
class, see ProductSurgery
.
Example
{# LANGUAGE DerivingVia #}  The constructors must be visible. import Generic.Data.Microsurgery (Surgery
,Surgery'
(..),Generically
(..),Derecordify
) data T = T { unT :: Int } derivingShow
via (Surgery
Derecordify
T)  T won't be shown as a record:  show (T {unT = 3}) == "T 3"
type ProductSurgery (s :: *) (a :: *) = GenericProduct (Surgery' s a) Source #
Apply a microsurgery s
to a type a
for DerivingVia
for the
Monoid
class.
newtype Surgery' (s :: *) (a :: *) Source #
See Surgery
.
Surgery'  

type family GSurgery (s :: *) (f :: k > *) :: k > * Source #
Apply a microsurgery represented by a symbol s
(declared as a dummy data
type) to a generic representation f
.
Instances
type GSurgery Derecordify (f :: k > Type) Source #  
Defined in Generic.Data.Internal.Microsurgery  
type GSurgery Typeage (M1 D (MetaData nm md pk _nt) f :: k > Type) Source #  
type GSurgery (CopyRep a) (_ :: Type > Type) Source #  
type GSurgery (RenameFields rnm) (f :: k > Type) Source #  
Defined in Generic.Data.Internal.Microsurgery  
type GSurgery (RenameConstrs rnm) (f :: k > Type) Source #  
Defined in Generic.Data.Internal.Microsurgery  
type GSurgery (OnFields f) (g :: k > Type) Source #  
Defined in Generic.Data.Internal.Microsurgery 
newtype Generically a Source #
Type with instances derived via Generic
.
Generically  

Instances
newtype GenericProduct a Source #
Product type with generic instances of Semigroup
and Monoid
.
This is similar to Generically
in most cases, but
GenericProduct
also works for types T
with deriving
via
, where GenericProduct
UU
is a generic product type coercible to,
but distinct from T
. In particular, U
may not have an instance of
Semigroup
, which Generically
requires.
Example
>>>
:set XDeriveGeneric XDerivingVia
>>>
data Point a = Point a a deriving Generic
>>>
:{
newtype Vector a = Vector (Point a) deriving (Semigroup, Monoid) via GenericProduct (Point (Sum a)) :}
If it were via
instead, then
Generically
(Point (Sum a))Vector
's mappend
(the Monoid
method) would be defined as Point
's
(
(the <>
)Semigroup
method), which might not exist, or might not be
equivalent to Vector
's generic Semigroup
instance, which would be
unlawful.
Instances
Generic a => Generic (GenericProduct a) Source #  
Defined in Generic.Data.Internal.Generically type Rep (GenericProduct a) :: Type > Type # from :: GenericProduct a > Rep (GenericProduct a) x # to :: Rep (GenericProduct a) x > GenericProduct a #  
(AssertNoSum Semigroup a, Generic a, Semigroup (Rep a ())) => Semigroup (GenericProduct a) Source #  
Defined in Generic.Data.Internal.Generically (<>) :: GenericProduct a > GenericProduct a > GenericProduct a # sconcat :: NonEmpty (GenericProduct a) > GenericProduct a # stimes :: Integral b => b > GenericProduct a > GenericProduct a #  
(AssertNoSum Semigroup a, Generic a, Monoid (Rep a ())) => Monoid (GenericProduct a) Source #  
Defined in Generic.Data.Internal.Generically mempty :: GenericProduct a # mappend :: GenericProduct a > GenericProduct a > GenericProduct a # mconcat :: [GenericProduct a] > GenericProduct a #  
type Rep (GenericProduct a) Source #  
Defined in Generic.Data.Internal.Generically 
Synthetic types
Synthetic data type.
A wrapper to view a generic Rep
as the datatype it's supposed to
represent, without needing a declaration.
Instances
toData :: Generic a => a > Data (Rep a) p Source #
Conversion between a generic type and the synthetic type made using its
representation. Inverse of fromData
.
onData :: (UnifyRep r s, UnifyRep s r) => p (Data r x) (Data s y) > p (Data r x) (Data s y) Source #
onData :: _ => (Data r x > Data s y) > (Data r x > Data s y)  possible specialization
Can be used with genericlens
for typechanging field updates with field_
(and possibly other generic optics).
A specialization of the identity function to be used to fix types
of functions on Data
, unifying the "spines" of input and output generic
representations (the "spine" is everything except field types, which may
thus change).
Microsurgeries
Each microsurgery consists of a type family F
to modify metadata in
GHC Generic representations, and two mappings (that are just
coerce
):
f ::Data
(Rep
a) p >Data
(F (Rep
a)) p unf ::Data
(F (Rep
a)) p >Data
(Rep
a) p
Use f
with toData
for generic functions that consume generic values,
and unf
with fromData
for generic functions that produce generic
values. Abstract example:
genericSerialize . f .toData
fromData
. unf . genericDeserialize
Renaming of fields and constructors
These surgeries require DataKinds
and TypeApplications
.
Examples
{# LANGUAGE DataKinds, TypeApplications #}  Rename all fields to "foo"renameFields
@(SConst
"foo")  Rename constructor "Bar" to "Baz", and leave all others the samerenameConstrs
@(SRename
'[ '("Bar", "Baz") ]SId
)
data RenameFields (rnm :: *) :: * Source #
Rename fields using the function rnm
given as a parameter.
data Foo = Bar { baz :: Zap }  becomes, renaming "baz" to "bag"  data Foo = Bar { bag :: Zap }
This is a defunctionalized symbol, applied using GSurgery
or Surgery
.
Instances
type GSurgery (RenameFields rnm) (f :: k > Type) Source #  
Defined in Generic.Data.Internal.Microsurgery 
renameFields :: forall rnm f p. Coercible (GSurgery (RenameFields rnm) f) f => Data f p > Data (GSurgery (RenameFields rnm) f) p Source #
unrenameFields :: forall rnm f p. Coercible (GSurgery (RenameFields rnm) f) f => Data f p > Data (GSurgery (RenameFields rnm) f) p Source #
data RenameConstrs (rnm :: *) :: * Source #
Rename constructors using the function rnm
given as a parameter.
data Foo = Bar { baz :: Zap }  becomes, renaming "Bar" to "Car"  data Foo = Car { baz :: Zap }
This is a defunctionalized symbol, applied using GSurgery
or Surgery
.
Instances
type GSurgery (RenameConstrs rnm) (f :: k > Type) Source #  
Defined in Generic.Data.Internal.Microsurgery 
renameConstrs :: forall rnm f p. Coercible (GSurgery (RenameConstrs rnm) f) f => Data f p > Data (GSurgery (RenameConstrs rnm) f) p Source #
unrenameConstrs :: forall rnm f p. Coercible (GSurgery (RenameConstrs rnm) f) f => Data f p > Data (GSurgery (RenameConstrs rnm) f) p Source #
Renaming functions
type family (f :: *) @@ (s :: Symbol) :: Symbol Source #
f @@ s
is the application of a typelevel function symbolized by f
to a s ::
.Symbol
A function FooToBar
can be defined as follows:
data FooToBar
type instance FooToBar @@
"foo" = "bar"
Empty function (compiletime error when applied).
data SRename (xs :: [(Symbol, Symbol)]) (f :: *) Source #
Define a function for a fixed set of strings, and fall back to f
for the others.
Wrap every field in a type constructor
Give every field a type f FieldType
(where f
is a parameter), to
obtain a family of types with a shared structure. This
"higherkindification" technique is presented in the following
blogposts:
 https://www.benjamin.pizza/posts/20171215functorfunctors.html
 https://reasonablypolymorphic.com/blog/higherkindeddata/
See also the file test/onelinersurgery.hs
in this package for an
example of using oneliner and genericlens with a synthetic type
constructed with DOnFields
.
Example
Derive Semigroup
and Monoid
for
a product of Num
types:
{# LANGUAGE DeriveGeneric, DerivingVia #} import Data.Monoid (Sum
(..))  Constructors must be in scope import GHC.Generics (Generic
) import Generic.Data.Microsurgery (ProductSurgery
,OnFields
,GenericProduct
(..)  Constructors must be in scope ,Surgery'
(..)  ) data TwoCounters = MkTwoCounters { c1 :: Int, c2 :: Int } derivingGeneric
deriving (Semigroup
,Monoid
) via (ProductSurgery
(OnFields
Sum
) TwoCounters)  Surgery here
type DOnFields (f :: * > *) (a :: *) = Data (GSurgery (OnFields f) (Rep a)) () Source #
Apply a type constructor to every field type of a type a
to make a
synthetic type.
Substitute a generic representation from another type
Example
Derive Semigroup
and Monoid
for
a product of Num
types, but using Sum
for one
field and Product
for the other.
In other words, we use the fact that Polar a
below is isomorphic to
the monoid (
.Product
a, Sum
a)
{# LANGUAGE DeriveGeneric, DerivingVia #} import Data.Monoid (Sum
(..),Product
(..))  Constructors must be in scope import GHC.Generics (Generic
) import Generic.Data.Microsurgery (ProductSurgery
,CopyRep
,GenericProduct
(..)  Constructors must be in scope ,Surgery'
(..)  ) data Polar a = Exp { modulus :: a, argument :: a } derivingGeneric
deriving (Semigroup
,Monoid
) via (ProductSurgery
(CopyRep
(Product
a,Sum
a)) (Polar a))  Surgery here
That is the polar representation of a complex number:
z = modulus * exp(i * argument)
The product of complex numbers defines a monoid isomorphic to
the monoid product (Product Double, Sum Double)
(multiply the moduli, add the arguments).
z1<>
z2 = z1*
z2 = Exp (modulus z1*
modulus z2) (argument z1+
argument z2)mempty
= 1 = Exp 1 0
copyRep :: forall a f p. Coercible (GSurgery (CopyRep a) f) f => Data f p > Data (GSurgery (CopyRep a) f) p Source #
uncopyRep :: forall a f p. Coercible f (GSurgery (CopyRep a) f) => Data (GSurgery (CopyRep a) f) p > Data f p Source #
Type aging ("denewtypify")
Derecordify
data Derecordify :: * Source #
Forget that a type was declared using record syntax.
data Foo = Bar { baz :: Zap }  becomes  data Foo = Bar Zap
Concretely, set the last field of MetaCons
to False
and forget field
names.
This is a defunctionalized symbol, applied using GSurgery
or Surgery
.
Instances
type GSurgery Derecordify (f :: k > Type) Source #  
Defined in Generic.Data.Internal.Microsurgery 
derecordify :: Coercible (GSurgery Derecordify f) f => Data f p > Data (GSurgery Derecordify f) p Source #
underecordify :: Coercible f (GSurgery Derecordify f) => Data (GSurgery Derecordify f) p > Data f p Source #