| Safe Haskell | None |
|---|---|
| Language | GHC2021 |
Stock.Derive
Description
The Stock extension SDK.
A synthesizer for a class Cls is a function Class -> Datatype
-> Synth EvTerm: given a structural view of the wrapped type,
build the class dictionary as Core — the same static, zero-cost
evidence the built-in synthesizers produce (no Generic, no
runtime Rep).
The only non-trivial primitive a synthesizer needs beyond the
structure is field: request the dictionary for a field's type and
continue with its evidence. This is the "continuation" that lets
a synthesizer pause, have GHC solve a sub-constraint, and resume —
so Eq (consumer), Functor (transformer) and Arbitrary
(producer) are all just monadic programs over the same structure.
Companion packages register support for a new class by writing an
instance of DeriveStock; the plugin discovers and runs it. This
module deliberately depends only on ghc + base, so companions
stay light.
Synopsis
- data Datatype = Datatype {}
- data Constructor = Constructor {
- conDataCon :: DataCon
- conFields :: [Type]
- conFixity :: Fixity
- conLabels :: Maybe [FieldLabel]
- conFieldCos :: [Coercion]
- data Synth a
- runSynth :: CtLoc -> Synth a -> TcPluginM (a, [Ct])
- synthTc :: (CtLoc -> TcPluginM (a, [Ct])) -> Synth a
- liftTc :: TcPluginM a -> Synth a
- field :: Class -> Type -> Synth CoreExpr
- fresh :: Type -> String -> Synth Id
- castInto :: CoreExpr -> Coercion -> CoreExpr
- classDict :: Class -> Type -> [CoreExpr] -> EvTerm
- classDictWith :: Class -> Type -> [CoreExpr] -> [(Int, CoreExpr)] -> Synth EvTerm
- classMethod :: String -> Class -> Id
- productCon :: Datatype -> Constructor
- matchSOP :: Datatype -> Type -> CoreExpr -> (Int -> Constructor -> [CoreExpr] -> Synth CoreExpr) -> Synth CoreExpr
- injectSOP :: Datatype -> Constructor -> [CoreExpr] -> CoreExpr
- fromProduct :: Datatype -> Type -> CoreExpr -> ([CoreExpr] -> Synth CoreExpr) -> Synth CoreExpr
- toProduct :: Datatype -> [CoreExpr] -> CoreExpr
- pureFields :: (Type -> Synth a) -> Constructor -> Synth [a]
- cpureFields :: Class -> (Type -> CoreExpr -> CoreExpr) -> Constructor -> Synth [CoreExpr]
- mapFields :: (Type -> CoreExpr -> Synth a) -> Constructor -> [CoreExpr] -> Synth [a]
- cmapFields :: Class -> (Type -> CoreExpr -> CoreExpr -> CoreExpr) -> Constructor -> [CoreExpr] -> Synth [CoreExpr]
- zipFields :: (Type -> CoreExpr -> CoreExpr -> Synth a) -> Constructor -> [CoreExpr] -> [CoreExpr] -> Synth [a]
- czipFields :: Class -> (Type -> CoreExpr -> CoreExpr -> CoreExpr -> CoreExpr) -> Constructor -> [CoreExpr] -> [CoreExpr] -> Synth [CoreExpr]
- foldlFields :: (a -> Type -> CoreExpr -> Synth a) -> a -> Constructor -> [CoreExpr] -> Synth a
- cfoldlFields :: Class -> (CoreExpr -> Type -> CoreExpr -> CoreExpr -> Synth CoreExpr) -> CoreExpr -> Constructor -> [CoreExpr] -> Synth CoreExpr
- traverseFields :: (Type -> CoreExpr -> Synth a) -> Constructor -> [CoreExpr] -> Synth [a]
- ctraverseFields :: Class -> (Type -> CoreExpr -> CoreExpr -> Synth CoreExpr) -> Constructor -> [CoreExpr] -> Synth [CoreExpr]
- newtype Deriver = Deriver {
- runDeriver :: Class -> Datatype -> Synth EvTerm
- class DeriveStock (cls :: Type -> Constraint) where
- newtype Deriver1 = Deriver1 {}
- class DeriveStock1 (cls :: (Type -> Type) -> Constraint) where
- newtype Deriver2 = Deriver2 {}
- class DeriveStock2 (cls :: (Type -> Type -> Type) -> Constraint) where
Structural view
What a synthesizer sees when solving C (Stock T): the via-target it is
building the instance for (Stock T), the underlying analysed type (T),
the newtype-unwrap coercion between them, and T's constructors. Field
types are already instantiated at T's actual arguments.
data Constructor Source #
One constructor of the analysed type: its DataCon, field types
(instantiated at T's arguments), fixity, and record labels if any.
Constructors
| Constructor | |
Fields
| |
The synthesis monad
A Core-building computation that may request sub-instances (emitting
wanted constraints) and allocate fresh binders. Structurally this is a
reader of the CtLoc over a writer of emitted Cts over TcPluginM, so we
derive the monad instances straight from that stack (the representations are
coercible).
runSynth :: CtLoc -> Synth a -> TcPluginM (a, [Ct]) Source #
Run a synthesizer at a constraint location, collecting the wanteds it emitted (to be returned to GHC alongside the solution).
synthTc :: (CtLoc -> TcPluginM (a, [Ct])) -> Synth a Source #
Build a Synth from a location-dependent, wanted-emitting action — the
inverse of runSynth. Lets a raw CtLoc -> TcPluginM (EvTerm, [Ct])
synthesizer be presented as a Deriver (see viaSynth in the plugin).
field :: Class -> Type -> Synth CoreExpr Source #
The continuation: request the dictionary for C ty and resume with its
evidence. Emits the wanted so GHC solves it (possibly via this very plugin,
enabling recursion into the field's own instance).
castInto :: CoreExpr -> Coercion -> CoreExpr Source #
x |> co, skipping the cast entirely when co is reflexive (the
not-overridden case) so the generated Core stays byte-identical.
classDict :: Class -> Type -> [CoreExpr] -> EvTerm Source #
Apply a class's dictionary constructor: C:Cls @ty m1 .. mn.
classDictWith :: Class -> Type -> [CoreExpr] -> [(Int, CoreExpr)] -> Synth EvTerm Source #
Build a (recursive) dictionary giving explicit superclass dictionaries and
implementations for the listed method indices; every other method is taken
from the class's own default (applied to the recursive dictionary). Lets a
synthesizer fill a many-method class from a few key methods — e.g.
Hashable from just hashWithSalt (its hash has a default), with its
Eq superclass supplied as [field eqCls ty].
classMethod :: String -> Class -> Id Source #
A class method selected by its source name — order-independent, unlike
indexing classMethods positionally (whose order can differ across GHC
versions). Panics if the class has no such method (a plugin bug).
SOP-style sum-of-products combinators (generics-sop flavour)
productCon :: Datatype -> Constructor Source #
The constructor of a product (the sole one). Exported so product
synthesizers can feed it to the per-Constructor NP combinators below.
matchSOP :: Datatype -> Type -> CoreExpr -> (Int -> Constructor -> [CoreExpr] -> Synth CoreExpr) -> Synth CoreExpr Source #
The SOP eliminator (from + case): scrutinise a value of the via-type
and dispatch on its constructor. k idx con fields builds the resTy-typed
branch body for constructor con (index idx in dtCons) with its bound
field expressions. One alternative per constructor — so this is the
sum-of-products generalisation of fromProduct.
injectSOP :: Datatype -> Constructor -> [CoreExpr] -> CoreExpr Source #
The SOP introducer (inj + to): build a value of the via-type from a
chosen constructor and one expression per its fields.
fromProduct :: Datatype -> Type -> CoreExpr -> ([CoreExpr] -> Synth CoreExpr) -> Synth CoreExpr Source #
productTypeFrom + a continuation: the single-constructor case of
matchSOP.
toProduct :: Datatype -> [CoreExpr] -> CoreExpr Source #
productTypeTo: the single-constructor case of injectSOP.
pureFields :: (Type -> Synth a) -> Constructor -> Synth [a] Source #
pure_NP / cpure_NP: one result per field, produced from its type
(cpure also from its cls dictionary, e.g. k ft d = mempty @ft d).
cpureFields :: Class -> (Type -> CoreExpr -> CoreExpr) -> Constructor -> Synth [CoreExpr] Source #
pureFields that also hands each field its own cls dictionary.
mapFields :: (Type -> CoreExpr -> Synth a) -> Constructor -> [CoreExpr] -> Synth [a] Source #
hmap / hcmap: map over the fields positionwise (the basic NP action;
cmap also hands each field's cls dictionary to the step).
cmapFields :: Class -> (Type -> CoreExpr -> CoreExpr -> CoreExpr) -> Constructor -> [CoreExpr] -> Synth [CoreExpr] Source #
mapFields that also hands each field its own cls dictionary.
zipFields :: (Type -> CoreExpr -> CoreExpr -> Synth a) -> Constructor -> [CoreExpr] -> [CoreExpr] -> Synth [a] Source #
liftA2_NP / cliftA2_NP: combine two field-lists positionwise (czip
via each field's cls dictionary, e.g. k ft d x y = (<>) @ft d x y).
czipFields :: Class -> (Type -> CoreExpr -> CoreExpr -> CoreExpr -> CoreExpr) -> Constructor -> [CoreExpr] -> [CoreExpr] -> Synth [CoreExpr] Source #
zipFields that also hands each field its own cls dictionary.
foldlFields :: (a -> Type -> CoreExpr -> Synth a) -> a -> Constructor -> [CoreExpr] -> Synth a Source #
hfoldl / cfoldl_NP: collapse the fields left-to-right through an
accumulator a (any host value — a CoreExpr, a list, …). The step of the
unconstrained foldlFields has full Synth access (request anynoseveral
dictionaries); cfoldlFields hands it each field's cls dictionary. Needed
by accumulating classes, e.g. Hashable's hashWithSalt threading the salt.
Arguments
| :: Class | |
| -> (CoreExpr -> Type -> CoreExpr -> CoreExpr -> Synth CoreExpr) | acc ft dict field |
| -> CoreExpr | initial accumulator |
| -> Constructor | |
| -> [CoreExpr] | |
| -> Synth CoreExpr |
foldlFields that also hands each field its own cls dictionary.
traverseFields :: (Type -> CoreExpr -> Synth a) -> Constructor -> [CoreExpr] -> Synth [a] Source #
htraverse over a list of fields: produce one a per field in Synth.
The most general traversal-shaped combinator — used for applicative-effectful
work like generating `Gen a` values (for Arbitrary). traverseFields has
full Synth access; ctraverseFields hands each field's cls dictionary
to the step.
Arguments
| :: Class | |
| -> (Type -> CoreExpr -> CoreExpr -> Synth CoreExpr) | ft dict field |
| -> Constructor | |
| -> [CoreExpr] | |
| -> Synth [CoreExpr] |
hctraverse: the constrained traverseFields — requests each field's
cls dictionary and hands it to the step (alongside the field value).
Used by Arbitrary (request `Arbitrary ft` per field, emit `Gen ft`),
CoArbitrary (request `CoArbitrary ft`, emit function generator),
and Shrink (request `Shrink ft`, emit shrink list).
The witness interface
A class's synthesizer, keyed by the wrapper arity it works through.
class DeriveStock (cls :: Type -> Constraint) where Source #
Register synthesis of a class cls derived via Stock. The method does
not mention cls, so the plugin selects the instance by looking it up in the
instance environment rather than by ordinary dispatch.
Methods
A Stock1 synthesizer for a (Type -> Type) class: given the class, the
constraint location, the via-target Stock1 F and the inner F, build the
dictionary — or Nothing if a field shape is unsupported. (Lifted classes
need the parameter-variance walk, so they get the raw form rather than the
Datatype-based Deriver; the Stock1 TyCon is recoverable as
tyConAppTyCon of the via-target.)
class DeriveStock1 (cls :: (Type -> Type) -> Constraint) where Source #
Register synthesis of a (Type -> Type) class derived via Stock1 (the
lifted counterpart of DeriveStock — e.g. NFData1, Hashable1).
Methods
The Stock2 analogue of Deriver1: given the class, the location, the
via-target Stock2 P and the inner P, build the dictionary.
class DeriveStock2 (cls :: (Type -> Type -> Type) -> Constraint) where Source #
Register synthesis of a (Type -> Type -> Type) class derived via Stock2
(e.g. NFData2).
Methods