stock-0.1.0.0: Stock-style deriving via coercion, with no Generic
Safe HaskellNone
LanguageGHC2021

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

Structural view

data Datatype Source #

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.

Constructors

Datatype 

Fields

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

data Synth a Source #

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).

Instances

Instances details
Applicative Synth Source # 
Instance details

Defined in Stock.Derive

Methods

pure :: a -> Synth a #

(<*>) :: Synth (a -> b) -> Synth a -> Synth b #

liftA2 :: (a -> b -> c) -> Synth a -> Synth b -> Synth c #

(*>) :: Synth a -> Synth b -> Synth b #

(<*) :: Synth a -> Synth b -> Synth a #

Functor Synth Source # 
Instance details

Defined in Stock.Derive

Methods

fmap :: (a -> b) -> Synth a -> Synth b #

(<$) :: a -> Synth b -> Synth a #

Monad Synth Source # 
Instance details

Defined in Stock.Derive

Methods

(>>=) :: Synth a -> (a -> Synth b) -> Synth b #

(>>) :: Synth a -> Synth b -> Synth b #

return :: a -> Synth a #

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).

liftTc :: TcPluginM a -> Synth a Source #

Lift a plugin action.

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).

fresh :: Type -> String -> Synth Id Source #

A fresh local binder of the given type.

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.

cfoldlFields Source #

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.

ctraverseFields Source #

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

newtype Deriver Source #

A class's synthesizer, keyed by the wrapper arity it works through.

Constructors

Deriver 

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.

newtype Deriver1 Source #

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.)

Constructors

Deriver1 

Fields

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).

newtype Deriver2 Source #

The Stock2 analogue of Deriver1: given the class, the location, the via-target Stock2 P and the inner P, build the dictionary.

Constructors

Deriver2 

Fields

class DeriveStock2 (cls :: (Type -> Type -> Type) -> Constraint) where Source #

Register synthesis of a (Type -> Type -> Type) class derived via Stock2 (e.g. NFData2).