generic-data-1.1.0.2: src/Generic/Data/Microsurgery.hs
{-# LANGUAGE ExplicitNamespaces #-}
-- | Simple operations on generic representations:
-- modify 'GHC.Generics.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 constructor @f :: Type -> Type@ to every field.
-- - 'CopyRep': use the generic representation of another type of the same shape.
-- - 'Typeage': treat a @newtype@ as a @data@ type.
-- - 'Derecordify': treat a type as if it weren't a record.
--
-- More complex surgeries can be found in
-- <https://hackage.haskell.org/package/generic-data-surgery generic-data-surgery>
-- but also, perhaps surprisingly,
-- in <https://hackage.haskell.org/package/generic-lens generic-lens>
-- (read more about this just below) and
-- <https://hackage.haskell.org/package/one-liner one-liner>.
--
-- Surgeries can be used:
--
-- - to derive type class instances with the @DerivingVia@ extension,
-- using the 'Surgery' or 'ProductSurgery' type synonyms
-- (for classes with instances for 'Generically' or 'GenericProduct');
-- - with the 'Data' \"synthetic type\" for more involved transformations,
-- for example using lenses in the next section.
module Generic.Data.Microsurgery
( -- * Surgeries with generic-lens
-- $lens-surgery
-- * Deriving via
Surgery
, ProductSurgery
, Surgeries
, ProductSurgeries
, Surgery'(..)
, GSurgery
, Generically(..)
, GenericProduct(..)
-- * Synthetic types
, Data
, toData
, fromData
, onData
-- * Microsurgeries
--
-- | Each microsurgery consists of a type family @F@ to modify metadata in
-- GHC Generic representations, and two mappings (that are just
-- 'Data.Coerce.coerce'):
--
-- @
-- f :: 'Data' ('GHC.Generics.Rep' a) p -> 'Data' (F ('GHC.Generics.Rep' a)) p
-- unf :: 'Data' (F ('GHC.Generics.Rep' a)) p -> 'Data' ('GHC.Generics.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 same
-- 'renameConstrs' \@('SRename' '[ '(\"Bar\", \"Baz\") ] 'SId')
-- @
, RenameFields()
, renameFields
, unrenameFields
, RenameConstrs()
, renameConstrs
, unrenameConstrs
-- *** Renaming functions
, type (@@)
, SId
, SError
, SConst
, SRename
-- ** 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. Some applications of
-- this \"higher-kindification\" technique may be found in the following
-- blogposts:
--
-- - https://www.benjamin.pizza/posts/2017-12-15-functor-functors.html
-- - https://reasonablypolymorphic.com/blog/higher-kinded-data/
--
-- See also the file @test/one-liner-surgery.hs@ in this package for an
-- example of using one-liner and generic-lens with a synthetic type
-- constructed with 'DOnFields'.
--
-- === Example
--
-- Derive 'Data.Semigroup.Semigroup' and 'Data.Monoid.Monoid' for
-- a product of 'Prelude.Num' types:
--
-- @
-- data TwoCounters = MkTwoCounters { c1 :: Int, c2 :: Int }
-- deriving 'GHC.Generics.Generic'
-- deriving ('Data.Semigroup.Semigroup', 'Data.Monoid.Monoid')
-- via ('ProductSurgery' ('OnFields' 'Data.Monoid.Sum') TwoCounters) -- Surgery here
-- @
--
-- ==== __Extensions and imports__
--
-- @
-- {-\# LANGUAGE DeriveGeneric, DerivingVia \#-}
-- import "Data.Monoid" ('Data.Monoid.Sum'(..)) -- Constructors must be in scope
-- import "GHC.Generics" ('GHC.Generics.Generic')
-- import "Generic.Data.Microsurgery"
-- ( 'ProductSurgery'
-- , 'OnFields'
-- , 'GenericProduct'(..) -- Constructors must be in scope
-- , 'Surgery''(..) --
-- )
-- @
, OnFields()
, DOnFields
, OnField()
, type (%~)
, Cat()
, DCat()
-- ** Substitute a generic representation from another type
-- |
-- === Example
--
-- Derive 'Data.Semigroup.Semigroup' and 'Data.Monoid.Monoid' for
-- a product of 'Prelude.Num' types, but using 'Data.Monoid.Sum' for one
-- field and 'Data.Monoid.Product' for the other.
-- In other words, we use the fact that @Polar a@ below is isomorphic to
-- the monoid @('Data.Monoid.Product' a, 'Data.Monoid.Sum' a)@.
--
-- @
-- {-\# LANGUAGE DeriveGeneric, DerivingVia \#-}
-- import "Data.Monoid" ('Data.Monoid.Sum'(..), 'Data.Monoid.Product'(..)) -- Constructors must be in scope
-- import "GHC.Generics" ('GHC.Generics.Generic')
-- import "Generic.Data.Microsurgery"
-- ( 'ProductSurgery'
-- , 'CopyRep'
-- , 'GenericProduct'(..) -- Constructors must be in scope
-- , 'Surgery''(..) --
-- )
--
-- data Polar a = Exp { modulus :: a, argument :: a }
-- deriving 'GHC.Generics.Generic'
-- deriving ('Data.Semigroup.Semigroup', 'Data.Monoid.Monoid')
-- via ('ProductSurgery' ('CopyRep' ('Data.Monoid.Product' a, 'Data.Monoid.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 'Data.Semigroup.<>' z2
-- = z1 'Prelude.*' z2
-- = Exp (modulus z1 'Prelude.*' modulus z2) (argument z1 'Prelude.+' argument z2)
--
-- 'Data.Monoid.mempty' = 1 = Exp 1 0
-- @
, CopyRep
, copyRep
, uncopyRep
-- ** Type aging ("denewtypify")
, Typeage()
, typeage
, untypeage
-- ** Derecordify
, Derecordify()
, derecordify
, underecordify
) where
import Generic.Data.Internal.Data
import Generic.Data.Internal.Generically
import Generic.Data.Internal.Microsurgery
-- $lens-surgery
-- 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 'GHC.Generics.Generic'
-- @
--
-- The function @over (field_ \@\"myField\") 'Generic.Data.Opaque'@
-- applies the newtype constructor 'Generic.Data.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\") 'Generic.Data.Opaque') . 'toData'
-- :: R -> 'Data' _ _ -- type arguments hidden
-- @
--
-- The result has a type @'Data' _ _@, that from the point of view of "GHC.Generics"
-- looks just like @R@ but with the field @\"myField\"@ wrapped in
-- 'Generic.Data.Opaque', as if we had defined:
--
-- @
-- data R = R { myField :: 'Generic.Data.Opaque' Int } deriving 'GHC.Generics.Generic'
-- @
--
-- ==== Example usage
--
-- We derive an instance of 'Show' that hides the @\"myField\"@ field,
-- whatever its type.
--
-- @
-- instance 'Show' R where
-- 'showsPrec' n = 'Generic.Data.gshowsPrec' n
-- . 'onData' (over (field_ \@\"myField\") 'Generic.Data.Opaque')
-- . 'toData'
--
-- 'show' (R 3) = \"R {myField = _}\"
-- @