Safe Haskell | None |
---|---|
Language | Haskell2010 |
Simple operations on generic representations, that only change the type-level metadata used by certain generic functions.
More complex ones can be found in generic-data-surgery and, surprisingly, in generic-lens (read more about this just below).
Synopsis
- data Data r p
- toData :: Generic a => a -> Data (Rep a) p
- fromData :: Generic a => Data (Rep a) p -> a
- onData :: (UnifyRep (Rep a) (Rep b), UnifyRep (Rep a) (Rep b)) => p a b -> p a b
- type family Derecordify (f :: k -> *) :: k -> *
- derecordify :: Coercible (Derecordify f) f => Data f p -> Data (Derecordify f) p
- underecordify :: Coercible f (Derecordify f) => Data (Derecordify f) p -> Data f p
- type family Typeage (f :: k -> *) :: k -> *
- typeage :: Coercible (Typeage f) f => Data f p -> Data (Typeage f) p
- untypeage :: Coercible f (Typeage f) => Data (Typeage f) p -> Data f p
- type family RenameFields (rnm :: *) (f :: k -> *) :: k -> *
- renameFields :: forall rnm f p. Coercible (RenameFields rnm f) f => Data f p -> Data (RenameFields rnm f) p
- unrenameFields :: forall rnm f p. Coercible (RenameFields rnm f) f => Data f p -> Data (RenameFields rnm f) p
- type family RenameConstrs (rnm :: *) (f :: k -> *) :: k -> *
- renameConstrs :: forall rnm f p. Coercible (RenameConstrs rnm f) f => Data f p -> Data (RenameConstrs rnm f) p
- unrenameConstrs :: forall rnm f p. Coercible (RenameConstrs rnm f) f => Data f p -> Data (RenameConstrs rnm f) p
- type family (f :: *) @@ (s :: Symbol) :: Symbol
- data SId
- data SError
- data SConst (s :: Symbol)
- data SRename (xs :: [(Symbol, Symbol)]) (f :: *)
Surgeries with generic-lens
One common and simple situation is to modify the type of some fields, for example wrapping them in a newtype.
We can leverage the generic-lens
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 generic-lens
-- 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 as-is. 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 = _}"
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.
onData :: (UnifyRep (Rep a) (Rep b), UnifyRep (Rep a) (Rep b)) => p a b -> p a b Source #
onData :: _ => (a -> b) -> (a -> b) -- possible specialization
Can be used with generic-lens
for type-changing field updates with field_
(and possibly other generic optics).
A specialization of the identity function to be used to fix types
of functions using Data
as input or output, 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
Derecordify
type family Derecordify (f :: k -> *) :: k -> * 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.
Instances
type Derecordify (U1 :: k -> Type) Source # | |
Defined in Generic.Data.Internal.Microsurgery | |
type Derecordify (V1 :: k -> Type) Source # | |
Defined in Generic.Data.Internal.Microsurgery | |
type Derecordify (f :*: g :: k -> Type) Source # | |
Defined in Generic.Data.Internal.Microsurgery | |
type Derecordify (f :+: g :: k -> Type) Source # | |
Defined in Generic.Data.Internal.Microsurgery | |
type Derecordify (M1 S (MetaSel _nm su ss ds) f :: k -> Type) Source # | |
type Derecordify (M1 C (MetaCons nm fx _isRecord) f :: k -> Type) Source # | |
Defined in Generic.Data.Internal.Microsurgery type Derecordify (M1 C (MetaCons nm fx _isRecord) f :: k -> Type) = M1 C (MetaCons nm fx False) (Derecordify f) | |
type Derecordify (M1 D m f :: k -> Type) Source # | |
Defined in Generic.Data.Internal.Microsurgery |
derecordify :: Coercible (Derecordify f) f => Data f p -> Data (Derecordify f) p Source #
underecordify :: Coercible f (Derecordify f) => Data (Derecordify f) p -> Data f p Source #
Type aging ("denewtypify")
type family Typeage (f :: k -> *) :: k -> * Source #
Forget that a type is a newtype
.
newtype Foo = Bar Baz -- becomes -- data Foo = Bar Baz
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
)
type family RenameFields (rnm :: *) (f :: k -> *) :: k -> * 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 }
Instances
type RenameFields rnm (f :*: g :: k -> Type) Source # | |
Defined in Generic.Data.Internal.Microsurgery | |
type RenameFields rnm (f :+: g :: k -> Type) Source # | |
Defined in Generic.Data.Internal.Microsurgery | |
type RenameFields rnm (M1 C m f :: k -> Type) Source # | |
Defined in Generic.Data.Internal.Microsurgery | |
type RenameFields rnm (M1 D m f :: k -> Type) Source # | |
Defined in Generic.Data.Internal.Microsurgery | |
type RenameFields rnm (M1 S (MetaSel (Just nm) su ss ds) f :: k -> Type) Source # | |
renameFields :: forall rnm f p. Coercible (RenameFields rnm f) f => Data f p -> Data (RenameFields rnm f) p Source #
unrenameFields :: forall rnm f p. Coercible (RenameFields rnm f) f => Data f p -> Data (RenameFields rnm f) p Source #
type family RenameConstrs (rnm :: *) (f :: k -> *) :: k -> * 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 }
Instances
type RenameConstrs rnm (f :*: g :: k -> Type) Source # | |
Defined in Generic.Data.Internal.Microsurgery | |
type RenameConstrs rnm (f :+: g :: k -> Type) Source # | |
Defined in Generic.Data.Internal.Microsurgery | |
type RenameConstrs rnm (M1 D m f :: k -> Type) Source # | |
Defined in Generic.Data.Internal.Microsurgery | |
type RenameConstrs rnm (M1 C (MetaCons nm fi ir) f :: k -> Type) Source # | |
Defined in Generic.Data.Internal.Microsurgery |
renameConstrs :: forall rnm f p. Coercible (RenameConstrs rnm f) f => Data f p -> Data (RenameConstrs rnm f) p Source #
unrenameConstrs :: forall rnm f p. Coercible (RenameConstrs rnm f) f => Data f p -> Data (RenameConstrs rnm f) p Source #
Renaming functions
type family (f :: *) @@ (s :: Symbol) :: Symbol Source #
f @@ s
is the application of a type-level 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 (compile-time error when applied).