Utilities for GHC.Generics
.
Semi-automatic method using gmappend
data Foo a = Bar [a] [a] deriving Generic
instance Semigroup (Foo a) where
(<>) = gmappend
This library also synergizes with the DerivingVia
extension
(introduced in GHC 8.6), thanks to the Generically
newtype.
data Foo a = Bar [a] [a]
deriving Generic
deriving Semigroup via (Generically (Foo a))
These examples can be found in test/example.hs
.
Note for completeness, the first example uses the following extensions and imports:
{-# LANGUAGE DeriveGeneric #-}
-- base
import Data.Semigroup (Semigroup(..))
-- generic-data
import Generic.Data (Generic, gmappend)
import Generic.Data.Orphans ()
The second example makes these additions on top:
{-# LANGUAGE
DerivingStrategies,
DerivingVia #-} -- since GHC 8.6.1
-- In addition to the previous imports
import Generic.Data (Generically(..))
Supported classes that GHC currently can't derive: Semigroup
, Monoid
,
Applicative
, Alternative
, Eq1
, Ord1
, Show1
.
Other classes from base are also supported, even though GHC can already derive them:
Eq
,Ord
,Enum
,Bounded
,Show
,Read
(derivable by the standard);Functor
,Foldable
,Traversable
(derivable via extensions,DeriveFunctor
, etc.).
To derive type classes outside of the standard library, it might be worth taking a look at one-liner.
Extract type names, constructor names, number and arities of constructors, etc..
generic-data offers simple operations (microsurgeries) on generic representations.
More surgeries can be found in generic-data-surgery, and suprisingly, in generic-lens and one-liner.
For more details, see also:
-
the module
Generic.Data.Microsurgery
; -
the files
test/lens-surgery.hs
andone-liner-surgery.hs
.
Derive an instance of Show
generically for a record type,
but as if it were not a record.
{-# LANGUAGE DeriveGeneric #-}
import Generic.Data (Generic, gshowsPrec)
import Generic.Data.Microsurgery (toData, derecordify)
-- An example record type
newtype T = T { unT :: Int } deriving Generic
-- Naively deriving Show would result in this being shown:
--
-- show (T 3) = "T {unT = 3}"
--
-- But instead, with a simple surgery, unrecordify, we can forget T was
-- declared as a record:
--
-- show (T 3) = "T 3"
instance Show T where
showsPrec n = gshowsPrec n . derecordify . toData
-- This example can be found in test/microsurgery.hs
Alternatively, using DerivingVia
:
{-# LANGUAGE DeriveGeneric, DerivingVia #-}
import Generic.Data (Generic) -- Reexported from GHC.Generics
-- Constructors must be visible to use DerivingVia
import Generic.Data.Microsurgery (Surgery, Surgery'(..), Generically(..), Derecordify)
data V = V { v1 :: Int, v2 :: Int }
deriving Generic
deriving Show via (Surgery Derecordify V)
-- show (V {v1 = 3, v2 = 4}) = "V 3 4"
generic-data aims to subsume generic deriving features of the following packages:
- semigroups: generic
Semigroup
,Monoid
, but with a heavier dependency footprint. - transformers-compat:
generic
Eq1
,Ord1
,Show1
. - generic-deriving:
doesn't derive the classes in base (defines clones of these classes as a toy
example); has Template Haskell code to derive
Generic
(not in generic-data).
Other relevant links.
- deriving-compat: deriving with Template Haskell.
- one-liner: another approach
to using
GHC.Generics
to derive instances of many type classes, including but not restricted to the above classes (this is done in one-liner-instances). - singletons, first-class-families (second one written by me) libraries for dependently-typed programming in Haskell.
- coercible-utils: utilities for coercible types.
Modules under Generic.Data.Internal
are not subject to any versioning policy.
Breaking changes may apply to them at any time.
If something in those modules seems useful, please report it or create a pull request to export it from an external module.
All contributions are welcome. Open an issue or a pull request on Github!