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

Stock.Override

Description

Per-field deriving modifiers for the Stock plugin.

Override a cfg wraps a with a type-level configuration cfg that the plugin reads while it synthesizes the instance: each entry names a field and the modifier to run on it (per-field DerivingVia). At runtime Override is just a (a newtype), so there is no cost.

data Coord = Coord { x :: Int, y :: Int }
  deriving Semigroup
    via Stock (Override Coord '[ "x" ':= Sum, "y" ':= Product ])

The config is an uninterpreted, poly-kinded marker the solver decodes off the type — never reduced. See docs/override-design.md.

Addressing a field

A field is addressed by name ("x" ':= m), by type (Int ':= m, every Int field), or by position (At Coord 0 ':= m). A modifier is pinned (Sum Int) or broadcast to the field's own type (Sum). A whole entry may instead be positional — one inner list per constructor, one cell per field — where Keep (written _) leaves a field untouched:

deriving Semigroup via Stock (Override Coord '[ [Sum, Keep] ])  -- field 0 via Sum

Surface sugar (the -fplugin Stock source pass, Stock.Surface)

The honest marker form is verbose, so the same plugin lowers a quote-free surface at parse time, scoped to Override applications:

Override Coord [ x via Sum, Coord at 0 via Sum, _ ]     -- what you write
Override Coord '[ "x" := Sum, At Coord 0 := Sum, Keep ] -- what the solver reads

namely: a bare lowercase selector becomes a Symbol (x"x"), via becomes :=, at becomes At, and a wildcard _ becomes Keep.

Higher order

Override1 / Override2 reshape the functor of a field rather than its element type (an h a field becomes m a), so the lifted instance (Functor, Eq1, Applicative, …) uses m's method. See Override1.

Synopsis

Documentation

newtype Override a (cfg :: k) Source #

a with a per-field override configuration cfg. A newtype, so Coercible (Override a cfg) a; cfg is phantom (read by the plugin only). Poly-kinded in cfg so it accepts both config shapes (see docs/override-design.md §5a): the entry list '[ "x" ':= Sum, … ] (cfg :: [Type]) and the positional '[ '[Sum Int, Keep, Keep] ] — one inner list per constructor, one element per field (cfg :: [[Type]]).

Constructors

Override a 

type Overriding a (cfg :: k) = Stock (Override a cfg) Source #

Overriding a cfg = Stock (Override a cfg) — the per-field wrapper read through Generically. Because the plugin makes Generic honour Override (the Rep carries the modifier field types), deriving C via Generically (Overriding A cfg) derives any Generic-based class over A with the per-field modifiers applied. The Generic-facing twin of using Stock + Override directly with the built-in synthesizers.

newtype Override1 (f :: j -> Type) (cfg :: k) (a :: j) Source #

The one-parameter analogue of Override: Override1 f cfg wraps a one-parameter constructor f for use through Stock1. Each positional modifier m (a k -> Type) reshapes the functor of an h a field to m a — so e.g. a [a] field becomes ZipList a and the derived Applicative zips instead of taking the cartesian product. A newtype, so Coercible (Override1 f cfg a) (f a).

Constructors

Override1 (f a) 

type Overriding1 (f :: j -> Type) (cfg :: k) = Stock1 (Override1 f cfg) Source #

Overriding1 f cfg = Stock1 (Override1 f cfg) — the Stock1-facing per-field wrapper. deriving Applicative via Overriding1 F '[ '[ZipList] ] reshapes F's [a] field into ZipList a before deriving.

newtype Override2 (p :: Type -> Type -> Type) (cfg :: k) a b Source #

The two-parameter analogue of Override: Override2 p cfg wraps a two-parameter constructor p for use through Stock2. Each positional modifier m (a Type -> Type -> Type) reshapes its field to m a b — the modifier applied to both datatype parameters — turning the field into a per-field Category. A newtype, so Coercible (Override2 p cfg a b) (p a b).

Constructors

Override2 (p a b) 

type Overriding2 (p :: Type -> Type -> Type) (cfg :: k) = Stock2 (Override2 p cfg) Source #

Overriding2 p cfg = Stock2 (Override2 p cfg) — the Stock2-facing per-field wrapper. deriving Category via Overriding2 '[ '[Basic (Sum Int), Basic String, Kleisli Maybe] ] Foo reshapes each field of Foo a b into a Category and derives Category pointwise over them.

data family (sel1 :: sel) := (m :: k) Source #

A single config entry: the field name (a Symbol) gets modifier m. Poly-kinded in m, so a saturated modifier (Sum Int :: Type) and an unsaturated one (Sum :: Type -> Type) both fit; the plugin dispatches on m's kind (pin vs. broadcast). An uninterpreted 'data family' — generative, injective, never reduced — so the solver reads it back verbatim.

data family (a :: k1) --> (b :: k2) :: j infixr 5 Source #

A path hop: h '--> rest. Each non-terminal hop selects a node — a promoted constructor (that constructor), a Nat (field by position) or a Symbol (field by label) — and the terminal hop is the modifier; the modifier applies to every field under the prefix. So 'P '--> m overrides every field of P and 'P '--> 0 '--> m overrides only its first field (design §4). Poly-kinded, uninterpreted, never reduced.

data family At (con :: kc) (pos :: Nat) :: sel Source #

A positional selector: field pos of constructor con. Used prefix on the left of (:=)At Con 0 := m — so the surface keeps a single infix operator. Like (:=) it is an uninterpreted, poly-kinded marker.

data family Keep :: k Source #

The positional no-op modifier: a field whose slot is Keep is left at its own type. Written _ in source (the -fplugin Stock surface pass lowers the type wildcard to Keep), so '[ '[Sum Int, _, _] ] overrides only the first field. Poly-kinded (a free-result-kind 'data family') so it sits in a list beside modifiers of any kind — Sum Int :: Type or Sum :: Type -> Type — without breaking the list's kind homogeneity. An uninterpreted marker the plugin reads; never reduced.