stock 0.1.0.0 → 0.1.0.1
raw patch · 8 files changed
+150/−101 lines, 8 filesPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
API changes (from Hackage documentation)
- Stock.Derive: instance GHC.Base.Applicative Stock.Derive.Synth
- Stock.Derive: instance GHC.Base.Functor Stock.Derive.Synth
- Stock.Derive: instance GHC.Base.Monad Stock.Derive.Synth
- Stock.Internal: instance GHC.Base.Monoid Stock.Internal.Solver
- Stock.Internal: instance GHC.Base.Semigroup Stock.Internal.Solver
+ Stock.Derive: instance GHC.Internal.Base.Applicative Stock.Derive.Synth
+ Stock.Derive: instance GHC.Internal.Base.Functor Stock.Derive.Synth
+ Stock.Derive: instance GHC.Internal.Base.Monad Stock.Derive.Synth
+ Stock.Internal: instance GHC.Internal.Base.Monoid Stock.Internal.Solver
+ Stock.Internal: instance GHC.Internal.Base.Semigroup Stock.Internal.Solver
+ Stock.Override: data family Keep :: forall k. () => k
- Stock.Override: data family Keep :: k
+ Stock.Override: data family At (con :: kc) (pos :: Nat) :: sel
Files
- CHANGELOG.md +16/−0
- README.md +14/−4
- plugin/Stock.hs +19/−12
- plugin/Stock/Applicative.hs +2/−1
- plugin/Stock/Bifunctor.hs +28/−9
- plugin/Stock/Functor.hs +6/−2
- plugin/Stock/Traversable.hs +2/−1
- stock.cabal +63/−72
CHANGELOG.md view
@@ -1,5 +1,21 @@ # Changelog +## 0.1.0.1++* `Category` via `Overriding2`: accept the field-keyed override forms+ (`Con at i via M`, `name via M`), not only the dense `'[ '[ .. ] ]`+ positional list; and fix a kind bug where a modifier's phantom+ parameters (e.g. `Basic m a b`) were left at skolem kinds.+* Per-field override reshapes are now validated: an override that maps a+ field to a non-coercible modifier (e.g. `Maybe via []`, `Int via Op`)+ is rejected with a clear error instead of silently emitting an unsound+ coercion. Applies to `Functor`, `Foldable`, `Traversable`,+ `Applicative` (`Stock1`) and `Category` (`Stock2`); the other classes+ already validated.+* Documentation: fixed the package-description rendering (cabal-version+ 3.0 needs real blank lines, not the `.` convention) and made the class+ / wrapper / companion links resolve.+ ## 0.1.0.0 Initial release.
README.md view
@@ -11,7 +11,7 @@ The classes each wrapper synthesises: + `Stock`: `Eq`, `Ord`, `Show`, `Read`, `Semigroup`, `Monoid`, `Enum`, `Bounded`, `Ix`, `Generic` (that's right)-+ `Stock1`: `Functor` / `Contravariant`, `Foldable`, `Applicative`, `Generic1`, `Eq1`, `Ord1`, `Show1`, `Read1`, `Traversable`†++ `Stock1`: `Functor` / `Contravariant`, `Foldable`, `Applicative`, `Generic1`, `Eq1`, `Ord1`, `Show1`, `Read1`, `Traversable`†, `TestEquality`, `TestCoercion` + `Stock2`: `Bifunctor`, `Bifoldable`, `Eq2`, `Ord2`, `Show2`, `Read2`, `Category`, `Bitraversable`† (`unpack` / unboxed-strict fields are rejected, with a clear error)@@ -162,9 +162,19 @@ ## Conclusions / realizations -Using _inspection-testing_ shows that the code we generate is-byte-identical to stock (`==-`). `Eq`, `Ord`, `Enum`, `Functor`-optimise to the *same Core* as GHC's own `deriving` on a twin type.+Every claim here is machine-checked with _inspection-testing_ (it+compares optimised Core, not behaviour):+++ `Eq`, `Ord`, `Enum`, `Functor`, `Bounded`, `Foldable` optimise to+ Core *byte-identical* to GHC's own `deriving` on a twin type.++ `Traversable` / `Bitraversable` — which GHC can't stock-derive at all+ — produce a `traverse` / `bitraverse` byte-identical to the natural+ hand-written definition.++ every other class provably erases the `Stock` wrapper and its+ coercions completely (so it is exactly as fast as hand-written).++In short: where GHC derives the class, you get the same Core GHC emits;+where it doesn't, you get the Core you would have written by hand. `Read` (and `Read1`) build `readPrec` exactly as GHC's derived `Read` does — the same `ReadPrec` combinators — and let `readsPrec` come from
plugin/Stock.hs view
@@ -9,8 +9,8 @@ {-# OPTIONS_GHC -Wno-x-partial -Wno-incomplete-uni-patterns #-} {-# OPTIONS_GHC -Wno-unused-imports #-} --- | Synthesize class instances for the @Stock@ \/ @Stock1@ \/--- @Stock2@ newtype wrappers directly from a datatype's structure,+-- | Synthesize class instances for the 'Stock' \/ 'Stock1' \/+-- 'Stock2' newtype wrappers directly from a datatype's structure, -- same as hand-written without @Generic@. This one module both -- provides the wrappers and /is/ the plugin, so a single name does -- everything:@@ -25,12 +25,13 @@ -- -- Supported classes: ----- * @Stock@: @Eq@, @Ord@, @Show@, @Read@, @Semigroup@, @Monoid@, @Enum@,--- @Bounded@, @Ix@, @Generic@.--- * @Stock1@: @Functor@ \/ @Contravariant@, @Foldable@, @Applicative@,--- @Generic1@, @Eq1@, @Ord1@, @Show1@, @Read1@, @Traversable@.--- * @Stock2@: @Bifunctor@, @Bifoldable@, @Eq2@, @Ord2@, @Show2@, @Read2@,--- @Category@, @Bitraversable@.+-- * 'Stock': 'Eq', 'Ord', 'Show', 'Read', 'Semigroup', 'Monoid', 'Enum',+-- 'Bounded', 'Ix', 'Generic'.+-- * 'Stock1': 'Functor' \/ 'Contravariant', 'Foldable', 'Applicative',+-- 'Generic1', 'Eq1', 'Ord1', 'Show1', 'Read1', 'Traversable',+-- 'TestEquality', 'TestCoercion'.+-- * 'Stock2': 'Bifunctor', 'Bifoldable', 'Eq2', 'Ord2', 'Show2', 'Read2',+-- 'Category', 'Bitraversable'. -- -- @Traversable@\/@Bitraversable@ are synthesized at the wrapper (@Stock1 -- F@\/@Stock2 P@) and used directly, or put on your type with the one-liner@@ -53,7 +54,7 @@ -- there is no @Rep@, no reflection, no instance lookup; you pay -- exactly the usual dictionary plumbing (including any dictionaries -- that polymorphic or polymorphically-recursive code builds at--- runtime), and never anything extra for having used @Stock@.+-- runtime), and never anything extra for having used 'Stock'. module Stock ( Stock(..), Stock1(..), Stock2(..), plugin@@ -129,6 +130,12 @@ import Control.Category (Category) import Data.Ix (Ix) import GHC.Generics (Generic, Generic1)+-- Imported only so Haddock can resolve the @'TestEquality'@ etc. identifier+-- links in this module's documentation (the plugin itself looks these classes+-- up by name via the GHC API). '-Wno-unused-imports' (above) silences them.+import Data.Bitraversable (Bitraversable)+import Data.Type.Equality (TestEquality)+import Data.Type.Coercion (TestCoercion) import Stock.Surface (lowerOverrides) import Stock.Internal import Stock.Bounded@@ -158,7 +165,7 @@ } -- | Present a raw @CtLoc -> TcPluginM (EvTerm, [Ct])@ synthesizer (@Ord@,--- @Show@, @Read@, @Enum@, @Ix@) as a @Deriver@, so every built-in @Stock@+-- @Show@, @Read@, @Enum@, @Ix@) as a @Deriver@, so every built-in 'Stock' -- class dispatches uniformly through 'runDeriverAttempt' — exactly like the -- SDK-native ones (@Eq@, @Bounded@, @Semigroup@, …). -- Each constructor is paired with its per-field override coercions@@ -212,7 +219,7 @@ -- | Look up a 'TyCon' by module and name, returning 'Nothing' if the module -- is not in scope — so the plugin stays inert instead of erroring when our--- @Stock@ wrapper isn't imported.+-- 'Stock' wrapper isn't imported. solveStock :: PluginState -> EvBindsVar -> [Ct] -> [Ct] -> TcPluginM TcPluginSolveResult solveStock st _ev _given wanted = do results <- for wanted (trySolve st)@@ -256,7 +263,7 @@ addCts :: [Ct] -> Attempt -> Attempt addCts extra (sol, ws, ins) = (sol, extra ++ ws, ins) --- | Dispatch a recognised @Stock@(-@Override@) representation to the right+-- | Dispatch a recognised 'Stock'(-@Override@) representation to the right -- built-in deriver (or a discovered companion via 'tryWitness'). dispatchStock :: PluginState -> Ct -> Class -> Type -> Repr -> TcPluginM Attempt dispatchStock st ct cls wrappedTy repr
plugin/Stock/Applicative.hs view
@@ -88,9 +88,10 @@ -- Override1: reshape the field functor @h a -> m a@ (e.g. ZipList), -- with a @h t ~R m t@ coercion threaded through pure/<*>/liftA2. Just m -> do ev <- newWanted loc (mkClassPred applicativeCls [m])+ vw <- newWanted loc (mkStockReprEq (mkAppTy h ctvTy) (mkAppTy m ctvTy)) -- validate reshape let coFn t = mkStockCo (PluginProv "stock") Representational (mkAppTy h t) (mkAppTy m t)- pure (Just (FsApp m (ctEvExpr ev) (Just coFn), [mkNonCanonical ev]))+ pure (Just (FsApp m (ctEvExpr ev) (Just coFn), [mkNonCanonical ev, mkNonCanonical vw])) Nothing -> do ev <- newWanted loc (mkClassPred applicativeCls [h]) pure (Just (FsApp h (ctEvExpr ev) Nothing, [mkNonCanonical ev])) Just FConst -> do ev <- newWanted loc (mkClassPred monoidCls [ft])
plugin/Stock/Bifunctor.hs view
@@ -129,12 +129,11 @@ case geStock2 gen of Just st2Tc -- peel an optional @Override2 cfg P@: @realP@ is the genuine constructor,- -- @mMods@ the per-field positional modifiers (single inner list — one ctor).- | let (realP, mMods) = case geOverride2 gen of- Just ov2Tc- | Just (tc, [_, rp, cfg]) <- splitTyConApp_maybe p0, tc == ov2Tc- -> (rp, listToMaybe =<< decodePositional cfg)- _ -> (p0, Nothing)+ -- @mMods@ the per-field modifiers (Keep-filled). Use the shared decoder+ -- ('peelOverride2With' -> 'decodeOvCfg') so it accepts the field-keyed+ -- forms (@Con at i via M@, @name via M@) and not only the dense+ -- positional @'[ '[ .. ] ]@ list.+ | let (realP, mMods) = peelOverride2With (ovTcsGen "Override2" gen) p0 , Just pTc <- tyConAppTyCon_maybe realP , [dc] <- tyConDataCons pTc, not (isNewTyCon pTc) -> do monoidCls <- tcLookupClass monoidClassName@@ -164,8 +163,15 @@ -- per field: a Category @h@ (+ coercion), or a constant @m@ handled -- Const-style via its Monoid (the automatic, @Basic@-free path). resolve i ftPQ = case mMods of- Just mods | Just m <- safeIdx mods i, not (isKeep m) ->- Just (CatF m (\t1 t2 -> mkStockCo (PluginProv "stock") Representational+ Just mods | Just m0 <- safeIdx mods i, not (isKeep m0) ->+ -- A modifier decoded from the field-keyed @At Con i := M@ form+ -- can carry skolem /kind/ variables for phantom parameters+ -- (e.g. @Basic m a b@'s @a@\/@b@), unlike the dense list form+ -- which pins them to the datatype's param kind. Re-kind those+ -- free variables to @k@ so @Category (m a b)@ is solvable.+ let fvs = nonDetEltsUniqSet (tyCoVarsOfType m0)+ m = substTyWith fvs (map (const kTy) fvs) m0+ in Just (CatF m (\t1 t2 -> mkStockCo (PluginProv "stock") Representational (instAt t1 t2 !! i) (app2 m t1 t2))) _ -> case classifyCatField pTv qTv ftPQ of Just h -> Just (CatF h (\t1 t2 -> mkRepReflCo (instAt t1 t2 !! i)))@@ -183,6 +189,19 @@ MonF m -> do ev <- newWanted loc (mkClassPred monoidCls [m]) pure (ctEvExpr ev, mkNonCanonical ev)) flds let (dEs, dWs) = unzip dws+ -- validate each override reshape (@realField ~R m a b@) with a GHC+ -- wanted, so the unchecked @mkStockCo@ axioms can't smuggle in an+ -- unsound coercion (reject @Int via Op@, @a->b via Op@, …).+ ovWs <- case mMods of+ Nothing -> pure []+ Just mods -> fmap concat $ forM (zip [0 :: Int ..] realFtsPQ) \(i, ftPQ) ->+ case safeIdx mods i of+ Just m0 | not (isKeep m0) -> do+ let fvs = nonDetEltsUniqSet (tyCoVarsOfType m0)+ m = substTyWith fvs (map (const kTy) fvs) m0+ vw <- newWanted loc (mkStockReprEq ftPQ (app2 m (mkTyVarTy pTv) (mkTyVarTy qTv)))+ pure [mkNonCanonical vw]+ _ -> pure [] -- id = /\a. (P <id of each field>..) |> sym (Stock2(..) a a ~ P a a) aTv <- freshTyVar "a" let aTy = mkTyVarTy aTv@@ -216,7 +235,7 @@ body = Case (Cast (Var gId) (coDown bTy cTy)) gCb resTy [Alt (DataAlt dc) gIds inner] compImpl = mkLams [bTv, cTv, a2Tv, gId, hId] body dict = mkApps (Var dictCon) [Type kTy, Type wrappedTy, idImpl, compImpl]- pure (Just (EvExpr dict, dWs))+ pure (Just (EvExpr dict, dWs ++ ovWs)) _ -> pure Nothing -- | Total list indexing.
plugin/Stock/Functor.hs view
@@ -113,8 +113,11 @@ let effFt = mkAppTy modf svTy -- m a coS = mkStockCo (PluginProv "stock") Representational ftA effFt coR = mkStockCo (PluginProv "stock") Representational rvFt (mkAppTy modf rvTy)+ -- validate the reshape: GHC must agree @field a ~R m a@, else the+ -- unchecked @coS@\/@coR@ axioms would be unsound (reject bad overrides).+ vw <- newWanted loc (mkStockReprEq ftA effFt) m <- varMap functorCls mContra loc svTv rvTy covFwd conFwd Cov effFt- pure (fmap (\(e, ws) -> (Cast (App e (Cast (Var x) coS)) (mkSymCo coR), ws)) m)+ pure (fmap (\(e, ws) -> (Cast (App e (Cast (Var x) coS)) (mkSymCo coR), mkNonCanonical vw : ws)) m) binders = if isCov then [svTv, rvTv] else [rvTv, svTv] malts <- forM dcons \dc -> do@@ -212,8 +215,9 @@ contrib i x ftA = case override1Mod gen mMods i of -- Override1 reshapes the field's (one-level) functor @h a -> m a@. Just m -> do ev <- newWanted loc (mkClassPred foldableCls [m])+ vw <- newWanted loc (mkStockReprEq ftA (mkAppTy m aTy)) -- validate reshape let co = mkStockCo (PluginProv "stock") Representational ftA (mkAppTy m aTy)- pure (Just (Just (foldMapOf m (ctEvExpr ev) (Cast (Var x) co), [mkNonCanonical ev])))+ pure (Just (Just (foldMapOf m (ctEvExpr ev) (Cast (Var x) co), [mkNonCanonical ev, mkNonCanonical vw]))) Nothing -> foldField ftA (Var x) malts <- forM dcons \dc -> do let ftsA = map scaledThing (dataConInstOrigArgTys dc (fixed ++ [aTy]))
plugin/Stock/Traversable.hs view
@@ -122,6 +122,7 @@ fieldOf i x ftA rvFt = case override1Mod gen mMods i of Just m -> do -- Override1: traverse through @m@, re-wrap @m b -> h b@ ev <- newWanted loc (mkClassPred travCls [m])+ vw <- newWanted loc (mkStockReprEq ftA (mkAppTy m aTy)) -- validate reshape let coS = mkStockCo (PluginProv "stock") Representational ftA (mkAppTy m aTy) coRb = mkStockCo (PluginProv "stock") Representational (mkAppTy m bTy) rvFt trav = mkApps (Var traverseSel)@@ -131,7 +132,7 @@ let coerceFn = Lam mb (Cast (Var mb) coRb) -- m b -> h b pure (Just ( apE (mkAppTy m bTy) rvFt (pureE (mkVisFunTyMany (mkAppTy m bTy) rvFt) coerceFn) trav- , [mkNonCanonical ev] ))+ , [mkNonCanonical ev, mkNonCanonical vw] )) Nothing -> traverseField ftA (Var x) malts <- forM dcons \dc -> do let fts = map scaledThing (dataConInstOrigArgTys dc (fixed ++ [aTy]))
stock.cabal view
@@ -1,97 +1,88 @@ cabal-version: 3.0 name: stock-version: 0.1.0.0+version: 0.1.0.1 synopsis: Stock-style deriving via coercion, with no Generic description:- A GHC type-checker plugin that derives class instances for @Stock T@- and higher-kinded variants at /compile time/, straight from the- structure of /T/ without a @Generic@ representation or runtime cost.- .+ A GHC type-checker plugin that derives class instances for+ __@<https://hackage-content.haskell.org/package/stock-0.1.0.0/docs/Stock.html#t:Stock Stock T>@__ and higher-kinded variants at /compile time/, straight+ from the structure of /T/ without a @Generic@ representation or+ runtime cost. Synthesized instances emulate GHC's /stock/ deriving.+ Supported classes:- .- * @Stock@: Eq, Ord, Show, Read, Semigroup, Monoid, Enum, Bounded, Ix, Generic- * @Stock1@: Functor, Foldable, Traversable, Contravariant, Applicative, Eq1, Ord1, Show1, Read1, Generic1, TestEquality, TestCoercion- * @Stock2@: Bifunctor, Bifoldable, Bitraversable, Eq2, Ord2, Show2, Read2, Category- .- Every claim below is machine-checked with @inspection-testing@ (it- compares optimised Core, not behaviour):- .- * For @Eq@, @Ord@, @Enum@, @Functor@, @Bounded@ and @Foldable@ the- emitted Core is /byte-identical/ to GHC's own stock deriving.- * @Traversable@ and @Bitraversable@ — which GHC cannot stock-derive- at all — produce a @traverse@\/@bitraverse@ /byte-identical/ to the- natural hand-written definition.- * Every remaining class is proven to erase the @Stock@ wrapper and- its coercions /completely/, so the instance is exactly as fast as a- hand-written one and behaves identically to stock deriving wherever- GHC has it.- .- In short: where GHC derives the class, the result is the same Core- GHC emits; where it does not, the result is the Core you would have- written by hand.- .- @Traversable@\/@Bitraversable@ are synthesised as genuine instances- but cannot be reached by a bare @deriving via@ (the coercion is- blocked by the abstract applicative's nominal role; see @"Stock"@).- .++ * __@<https://hackage-content.haskell.org/package/stock-0.1.0.0/docs/Stock.html#t:Stock Stock>@__: @<https://hackage-content.haskell.org/package/base-4.22.0.0/docs/Data-Eq.html#t:Eq Eq>@, @<https://hackage-content.haskell.org/package/base-4.22.0.0/docs/Data-Ord.html#t:Ord Ord>@, @<https://hackage-content.haskell.org/package/base-4.22.0.0/docs/Text-Show.html#t:Show Show>@, @<https://hackage-content.haskell.org/package/base-4.22.0.0/docs/Text-Read.html#t:Read Read>@, @<https://hackage-content.haskell.org/package/base-4.22.0.0/docs/Data-Semigroup.html#t:Semigroup Semigroup>@, @<https://hackage-content.haskell.org/package/base-4.22.0.0/docs/Data-Monoid.html#t:Monoid Monoid>@,+ @<https://hackage-content.haskell.org/package/base-4.22.0.0/docs/Prelude.html#t:Enum Enum>@, @<https://hackage-content.haskell.org/package/base-4.22.0.0/docs/Prelude.html#t:Bounded Bounded>@, @<https://hackage-content.haskell.org/package/base-4.22.0.0/docs/Data-Ix.html#t:Ix Ix>@, @<https://hackage-content.haskell.org/package/base-4.22.0.0/docs/GHC-Generics.html#t:Generic Generic>@+ * __@<https://hackage-content.haskell.org/package/stock-0.1.0.0/docs/Stock.html#t:Stock1 Stock1>@__: @<https://hackage-content.haskell.org/package/base-4.22.0.0/docs/Data-Functor.html#t:Functor Functor>@, @<https://hackage-content.haskell.org/package/base-4.22.0.0/docs/Data-Foldable.html#t:Foldable Foldable>@, @<https://hackage-content.haskell.org/package/base-4.22.0.0/docs/Data-Traversable.html#t:Traversable Traversable>@,† @<https://hackage-content.haskell.org/package/base-4.22.0.0/docs/Data-Functor-Contravariant.html#t:Contravariant Contravariant>@,+ @<https://hackage-content.haskell.org/package/base-4.22.0.0/docs/Control-Applicative.html#t:Applicative Applicative>@, @<https://hackage-content.haskell.org/package/base-4.22.0.0/docs/Data-Functor-Classes.html#t:Eq1 Eq1>@, @<https://hackage-content.haskell.org/package/base-4.22.0.0/docs/Data-Functor-Classes.html#t:Ord1 Ord1>@, @<https://hackage-content.haskell.org/package/base-4.22.0.0/docs/Data-Functor-Classes.html#t:Show1 Show1>@, @<https://hackage-content.haskell.org/package/base-4.22.0.0/docs/Data-Functor-Classes.html#t:Read1 Read1>@, @<https://hackage-content.haskell.org/package/base-4.22.0.0/docs/GHC-Generics.html#t:Generic1 Generic1>@,+ @<https://hackage-content.haskell.org/package/base-4.22.0.0/docs/Data-Type-Equality.html#t:TestEquality TestEquality>@, @<https://hackage-content.haskell.org/package/base-4.22.0.0/docs/Data-Type-Coercion.html#t:TestCoercion TestCoercion>@+ * __@<https://hackage-content.haskell.org/package/stock-0.1.0.0/docs/Stock.html#t:Stock2 Stock2>@__: @<https://hackage-content.haskell.org/package/base-4.22.0.0/docs/Data-Bifunctor.html#t:Bifunctor Bifunctor>@, @<https://hackage-content.haskell.org/package/base-4.22.0.0/docs/Data-Bifoldable.html#t:Bifoldable Bifoldable>@, @<https://hackage-content.haskell.org/package/base-4.22.0.0/docs/Data-Bitraversable.html#t:Bitraversable Bitraversable>@,† @<https://hackage-content.haskell.org/package/base-4.22.0.0/docs/Data-Functor-Classes.html#t:Eq2 Eq2>@,+ @<https://hackage-content.haskell.org/package/base-4.22.0.0/docs/Data-Functor-Classes.html#t:Ord2 Ord2>@, @<https://hackage-content.haskell.org/package/base-4.22.0.0/docs/Data-Functor-Classes.html#t:Show2 Show2>@, @<https://hackage-content.haskell.org/package/base-4.22.0.0/docs/Data-Functor-Classes.html#t:Read2 Read2>@, @<https://hackage-content.haskell.org/package/base-4.22.0.0/docs/Control-Category.html#t:Category Category>@++ † @<https://hackage-content.haskell.org/package/base-4.22.0.0/docs/Data-Traversable.html#t:Traversable Traversable>@, + @<https://hackage-content.haskell.org/package/base-4.22.0.0/docs/Data-Bitraversable.html#t:Bitraversable Bitraversable>@ + are synthesisable but cannot be used with @DerivingVia@ due to role+ issues.+ Companion packages add more classes through @DeriveStock@ instances- (see @"Stock.Derive"@), discovered automatically without an extra- @-fplugin@ flag :- .- * @stock-deepseq@: NFData, NFData1, NFData2- * @stock-hashable@: Hashable, Hashable1, Hashable2- * @stock-aeson@: ToJSON, ToJSON1, ToJSON2; FromJSON, FromJSON1, FromJSON2- * @stock-quickcheck@: Arbitrary, Arbitrary1, Arbitrary2; CoArbitrary- * @stock-profunctors@: Profunctor- .- Ordinary @DerivingVia@ modifiers compose with @Stock@:- @Down (Stock T)@ reverses ordering, enumeration and bounds;- @Backwards (Stock1 F)@ reverses @Applicative@ effects; @Reverse- (Stock1 F)@ reverses @Foldable@\/@Traversable@.- .+ (see "Stock.Derive"), discovered automatically without an extra+ @-fplugin@ flag:++ * __@<https://hackage.haskell.org/package/stock-deepseq stock-deepseq>@__: @<https://hackage-content.haskell.org/package/deepseq-1.5.2.0/docs/Control-DeepSeq.html#t:NFData NFData>@, @<https://hackage-content.haskell.org/package/deepseq-1.5.2.0/docs/Control-DeepSeq.html#t:NFData1 NFData1>@, @<https://hackage-content.haskell.org/package/deepseq-1.5.2.0/docs/Control-DeepSeq.html#t:NFData2 NFData2>@+ * __@<https://hackage.haskell.org/package/stock-hashable stock-hashable>@__: @<https://hackage-content.haskell.org/package/hashable-1.5.1.0/docs/Data-Hashable.html#t:Hashable Hashable>@, @<https://hackage-content.haskell.org/package/hashable-1.5.1.0/docs/Data-Hashable-Lifted.html#t:Hashable1 Hashable1>@, @<https://hackage-content.haskell.org/package/hashable-1.5.1.0/docs/Data-Hashable-Lifted.html#t:Hashable2 Hashable2>@+ * __@<https://hackage.haskell.org/package/stock-aeson stock-aeson>@__: @<https://hackage-content.haskell.org/package/aeson-2.3.0.0/docs/Data-Aeson.html#t:ToJSON ToJSON>@, @<https://hackage-content.haskell.org/package/aeson-2.3.0.0/docs/Data-Aeson.html#t:ToJSON1 ToJSON1>@, @<https://hackage-content.haskell.org/package/aeson-2.3.0.0/docs/Data-Aeson.html#t:ToJSON2 ToJSON2>@; @<https://hackage-content.haskell.org/package/aeson-2.3.0.0/docs/Data-Aeson.html#t:FromJSON FromJSON>@, @<https://hackage-content.haskell.org/package/aeson-2.3.0.0/docs/Data-Aeson.html#t:FromJSON1 FromJSON1>@, @<https://hackage-content.haskell.org/package/aeson-2.3.0.0/docs/Data-Aeson.html#t:FromJSON2 FromJSON2>@+ * __@<https://hackage.haskell.org/package/stock-quickcheck stock-quickcheck>@__: @<https://hackage-content.haskell.org/package/QuickCheck-2.18.0.0/docs/Test-QuickCheck.html#t:Arbitrary Arbitrary>@, @<https://hackage-content.haskell.org/package/QuickCheck-2.18.0.0/docs/Test-QuickCheck.html#t:Arbitrary1 Arbitrary1>@, @<https://hackage-content.haskell.org/package/QuickCheck-2.18.0.0/docs/Test-QuickCheck.html#t:Arbitrary2 Arbitrary2>@; @<https://hackage-content.haskell.org/package/QuickCheck-2.18.0.0/docs/Test-QuickCheck.html#t:CoArbitrary CoArbitrary>@+ * __@<https://hackage.haskell.org/package/stock-profunctors stock-profunctors>@__: @<https://hackage-content.haskell.org/package/profunctors-5.6.3/docs/Data-Profunctor.html#t:Profunctor Profunctor>@++ Ordinary @DerivingVia@ modifiers compose with+ <https://hackage-content.haskell.org/package/stock-0.1.0.0/docs/Stock.html#t:Stock Stock>:++ * @<https://hackage-content.haskell.org/package/base-4.22.0.0/docs/Data-Ord.html#t:Down Down (Stock T)>@ reverses ordering, enumeration and bounds+ * @<https://hackage-content.haskell.org/package/transformers-0.6.3.0/docs/Control-Applicative-Backwards.html#t:Backwards Backwards (Stock1 F)>@ reverses @<https://hackage-content.haskell.org/package/base-4.22.0.0/docs/Control-Applicative.html#t:Applicative Applicative>@ effects+ * @<https://hackage-content.haskell.org/package/transformers-0.6.3.0/docs/Data-Functor-Reverse.html#t:Reverse Reverse (Stock1 F)>@ reverses @<https://hackage-content.haskell.org/package/base-4.22.0.0/docs/Data-Foldable.html#t:Foldable Foldable>@ \/ @<https://hackage-content.haskell.org/package/base-4.22.0.0/docs/Data-Traversable.html#t:Traversable Traversable>@+ > {-# options_ghc -fplugin Stock #-} > {-# language DerivingVia #-}- > + > > import Stock > import Data.Ord (Down(..)) >- > -- >>> sort [Bronze, Silver, Gold]+ > -- >>> sort [Bronze,Silver,Gold] > -- [Gold,Silver,Bronze] > data Place = Bronze | Silver | Gold- > deriving (Eq, Show) via Stock Place+ > deriving (Eq, Show) via Stock Place > deriving (Ord, Bounded, Enum) via Down (Stock Place)- .- Per-field modifiers (@Override@, @"Stock.Override"@, re-exported by- @"Stock"@) customise individual fields by name, type, or position; @_@++ Per-field modifiers (@Override@, "Stock.Override", re-exported by+ "Stock") customise individual fields by name, type, or position; @_@ leaves a field unchanged. A modifier is any newtype with the relevant instance.- . - Hit points and coins accumulate with addition, poisoning- contaminates by disjunction (or), @items@, and @weapons@ union with- addition to product a multiset.- .++ This game example shows hit points and coins accumulate with+ addition, poisoning contaminates by disjunction (or), @items@, and+ @weapons@ union with addition to produce a multiset.+ > import Data.Map.Monoidal (MonoidalMap(..))- > ..+ > > type MultiSet key = MonoidalMap key (Sum Int)- > + > > data Inventory = Inventory- > { hp :: Int- > , coins :: Int- > , poisoned :: Bool- > , items :: Map Item Int -- these unfortunately default- > , weapons :: Map Weapon Int -- to left-biased union+ > { hp :: Int -- (+)+ > , coins :: Int -- (+)+ > , poisoned :: Bool -- (||)+ > , items :: Map Item Int -- unionWith (+)+ > , weapons :: Map Weapon Int -- unionWith (+) > }- > deriving (Eq, Ord, Show, Read) via+ > deriving (Eq, Ord, Show, Read) via > Stock Inventory- > deriving (Semigroup, Monoid) via + > deriving (Semigroup, Monoid) via > Overriding Inventory- > '[ hp via Sum - > , coins via Sum - > , poisoned via Any- > , items via MultiSet Item- > , weapons via MultiSet Weapon- > ]- .+ > '[ hp via Sum+ > , coins via Sum+ > , poisoned via Any+ > , items via MultiSet Item+ > , weapons via MultiSet Weapon+ > ]+ Synthesis runs once per instance (not per use): @deriving Cls via Stock T@ produces a single shared @instance Cls T@ that every call reuses. license: BSD-3-Clause