| Safe Haskell | None |
|---|---|
| Language | Haskell2010 |
Generic.Data.Microsurgery
Contents
Description
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 OpaqueOpaque 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 } deriving Generic
Example usage
We derive an instance of Show that hides the "myField" field,
whatever its type.
instanceShowR whereshowsPrecn =gshowsPrecn .onData(over (field_ @"myField")Opaque) .toDatashow(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(Repa) p ->Data(F (Repa)) p unf ::Data(F (Repa)) p ->Data(Repa) 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 .toDatafromData. 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 ZapConcretely, 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 same
renameConstrs @(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).