deriving-compat 0.6 → 0.6.1
raw patch · 13 files changed
+155/−41 lines, 13 filesdep ~template-haskelldep ~transformersPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: template-haskell, transformers
API changes (from Hackage documentation)
+ Data.Deriving.Internal: neqIntHashValName :: Name
Files
- CHANGELOG.md +6/−0
- README.md +7/−5
- deriving-compat.cabal +30/−12
- src/Data/Deriving.hs +4/−0
- src/Data/Deriving/Internal.hs +3/−0
- src/Data/Eq/Deriving.hs +4/−0
- src/Data/Eq/Deriving/Internal.hs +63/−24
- src/Data/Ord/Deriving.hs +4/−0
- src/Text/Read/Deriving.hs +4/−0
- src/Text/Show/Deriving.hs +4/−0
- tests/FunctorSpec.hs +1/−0
- tests/ShowSpec.hs +1/−0
- tests/Types/EqOrd.hs +24/−0
CHANGELOG.md view
@@ -1,3 +1,9 @@+### 0.6.1 [2022.05.07]+* Backport [GHC!6955](https://gitlab.haskell.org/ghc/ghc/-/merge_requests/6955),+ which makes derived `Eq` instances check data constructor tags, which can+ improve runtime performance for data types with nullary constructors.+* Allow building the test suite with `transformers-0.6.*`.+ ## 0.6 [2021.08.29] * Allow building with `template-haskell-2.18.0.0` (GHC 9.2). * Using `deriveEnum` and `deriveIx` on poly-kinded data family instances may
README.md view
@@ -15,15 +15,15 @@ https://tldrlegal.com/license/bsd-3-clause-license-%28revised%29 "BSD 3-Clause License (Revised)" -Provides Template Haskell functions that mimic deriving extensions that were introduced or modified in recent versions of GHC. Currently, the following typeclasses/extensions are covered:+`deriving-compat` provides Template Haskell functions that mimic `deriving` extensions that were introduced or modified in recent versions of GHC. Currently, the following typeclasses/extensions are covered: * Deriving `Bounded` * Deriving `Enum` * Deriving `Ix`-* Deriving `Eq`, `Eq1`, and `Eq2`-* Deriving `Ord`, `Ord1`, and `Ord2`-* Deriving `Read`, `Read1`, and `Read2`-* Deriving `Show`, `Show1`, and `Show2`+* Deriving `Eq`+* Deriving `Ord`+* Deriving `Read`+* Deriving `Show` * `DeriveFoldable` * `DeriveFunctor` * `DeriveTraversable`@@ -31,6 +31,8 @@ * `DerivingVia` (with GHC 8.2 or later) See the `Data.Deriving` module for a full list of backported changes.++In addition, `deriving-compat` also provides some additional `deriving` functionality that has not yet been merged into upstream GHC. Aside from the GHC `deriving` extensions mentioned above, `deriving-compat` also permits deriving instances of classes in the `Data.Functor.Classes` module, covering the `Eq1`, `Eq2`, `Ord1`, `Ord2`, `Read1`, `Read2`, `Show1`, and `Show2` classes. This extra functionality is outside of the main scope of `deriving-compat`, as it does not backport extensions that exist in today's GHC. Nevertheless, the underlying Template Haskell machinery needed to derive `Eq` and friends extends very naturally to `Eq1` and friends, so this extra functionality is included in `deriving-compat` as a convenience. Note that some recent GHC typeclasses/extensions are not covered by this package:
deriving-compat.cabal view
@@ -1,9 +1,10 @@ name: deriving-compat-version: 0.6+version: 0.6.1 synopsis: Backports of GHC deriving extensions-description: Provides Template Haskell functions that mimic deriving- extensions that were introduced or modified in recent versions- of GHC. Currently, the following typeclasses/extensions are covered:+description: @deriving-compat@ provides Template Haskell functions that+ mimic @deriving@ extensions that were introduced or modified+ in recent versions of GHC. Currently, the following+ typeclasses/extensions are covered: . * Deriving @Bounded@ .@@ -11,13 +12,13 @@ . * Deriving @Ix@ .- * Deriving @Eq@, @Eq1@, and @Eq2@+ * Deriving @Eq@ .- * Deriving @Ord@, @Ord1@, and @Ord2@+ * Deriving @Ord@ .- * Deriving @Read@, @Read1@, and @Read2@+ * Deriving @Read@ .- * Deriving @Show@, @Show1@, and @Show2@+ * Deriving @Show@ . * @DeriveFoldable@ .@@ -31,6 +32,21 @@ . See the "Data.Deriving" module for a full list of backported changes. .+ In addition, @deriving-compat@ also provides some additional+ @deriving@ functionality that has not yet been merged into+ upstream GHC. Aside from the GHC @deriving@ extensions+ mentioned above, @deriving-compat@ also permits deriving+ instances of classes in the @Data.Functor.Classes@ module,+ covering the @Eq1@, @Eq2@, @Ord1@, @Ord2@, @Read1@,+ @Read2@, @Show1@, and @Show2@ classes. This extra+ functionality is outside of the main scope of+ @deriving-compat@, as it does not backport extensions that+ exist in today's GHC. Nevertheless, the underlying Template+ Haskell machinery needed to derive @Eq@ and friends+ extends very naturally to @Eq1@ and friends, so this extra+ functionality is included in @deriving-compat@ as a+ convenience.+ . Note that some recent GHC typeclasses/extensions are not covered by this package: . * @DeriveDataTypeable@@@ -74,8 +90,8 @@ , GHC == 8.6.5 , GHC == 8.8.4 , GHC == 8.10.7- , GHC == 9.0.1- , GHC == 9.2.*+ , GHC == 9.0.2+ , GHC == 9.2.2 cabal-version: >=1.10 source-repository head@@ -139,7 +155,7 @@ build-depends: template-haskell >= 2.5 && < 2.11 if flag(new-functor-classes)- build-depends: transformers (>= 0.2 && < 0.4) || >= 0.5+ build-depends: transformers (>= 0.2 && < 0.4) || (>= 0.5 && < 0.7) , transformers-compat >= 0.5 cpp-options: "-DNEW_FUNCTOR_CLASSES" else@@ -183,7 +199,7 @@ build-depends: base >= 4.3 && < 4.9 if flag(new-functor-classes)- build-depends: transformers (>= 0.2 && < 0.4) || (>= 0.5 && < 0.6)+ build-depends: transformers (>= 0.2 && < 0.4) || (>= 0.5 && < 0.7) , transformers-compat >= 0.5 cpp-options: "-DNEW_FUNCTOR_CLASSES" else@@ -194,3 +210,5 @@ ghc-options: -Wall -threaded -rtsopts if impl(ghc >= 8.6) ghc-options: -Wno-star-is-type+ if impl(ghc >= 9.0)+ ghc-options: -fenable-th-splice-warnings
src/Data/Deriving.hs view
@@ -123,6 +123,10 @@ * In GHC 9.0, @DeriveFunctor@ was changed so that it works on more constructors with rank-n field types.++* In GHC 9.4, deriving 'Eq' was changed so that it checks data constructor+ tags, which can improve runtime performance for data types with nullary+ constructors. -} {- $derive
src/Data/Deriving/Internal.hs view
@@ -1562,6 +1562,9 @@ minusIntHashValName :: Name minusIntHashValName = mkNameG_v "ghc-prim" "GHC.Prim" "-#" +neqIntHashValName :: Name+neqIntHashValName = mkNameG_v "ghc-prim" "GHC.Prim" "/=#"+ parenValName :: Name parenValName = mkNameG_v "base" "GHC.Read" "paren"
src/Data/Eq/Deriving.hs view
@@ -7,6 +7,10 @@ Portability: Template Haskell Exports functions to mechanically derive 'Eq', 'Eq1', and 'Eq2' instances.+Note that upstream GHC does not have the ability to derive 'Eq1' or 'Eq2'+instances, but since the functionality to derive 'Eq' extends very naturally+'Eq1' and 'Eq2', the ability to derive the latter two classes is provided as a+convenience. -} module Data.Eq.Deriving ( -- * 'Eq'
src/Data/Eq/Deriving/Internal.hs view
@@ -33,7 +33,7 @@ ) where import Data.Deriving.Internal-import Data.List (foldl1', partition)+import Data.List (foldl1') import qualified Data.Map as Map import Language.Haskell.TH.Datatype@@ -177,8 +177,7 @@ [value1, value2] ) . appsE $ [ varE $ eqConstName eClass- , letE [ funD eqDefn $ map (makeCaseForCon eClass tvMap) patMatchCons- ++ fallThroughCase+ , letE [ funD eqDefn [eqClause tvMap] ] $ varE eqDefn `appE` varE value1 `appE` varE value2 ] #if defined(NEW_FUNCTOR_CLASSES)@@ -186,40 +185,64 @@ #endif ++ [varE value1, varE value2] where- nullaryCons, nonNullaryCons :: [ConstructorInfo]- (nullaryCons, nonNullaryCons) = partition isNullaryCon cons+ nonNullaryCons :: [ConstructorInfo]+ nonNullaryCons = filter (not . isNullaryCon) cons - tagMatchCons, patMatchCons :: [ConstructorInfo]- (tagMatchCons, patMatchCons)- | length nullaryCons > 10 = (nullaryCons, nonNullaryCons)- | otherwise = ([], cons)+ numNonNullaryCons :: Int+ numNonNullaryCons = length nonNullaryCons - fallThroughCase :: [Q Clause]- fallThroughCase- | null tagMatchCons = case patMatchCons of- [] -> [makeFallThroughCaseTrue] -- No constructors: _ == _ = True- [_] -> [] -- One constructor: no fall-through case- _ -> [makeFallThroughCaseFalse] -- Two or more constructors:- -- _ == _ = False- | otherwise = [makeTagCase]+ eqClause :: TyVarMap1 -> Q Clause+ eqClause tvMap+ | null cons+ = makeFallThroughCaseTrue+ -- Tag checking is redundant when there is only one data constructor+ | [con] <- cons+ = makeCaseForCon eClass tvMap con+ -- This is an enum (all constructors are nullary) - just do a simple tag check+ | all isNullaryCon cons+ = makeTagCase+ | otherwise+ = do abNames@(a, _, b, _) <- newABNames+ clause (map varP [a,b])+ (normalB $ eqExprWithTagCheck tvMap abNames)+ [] -makeTagCase :: Q Clause-makeTagCase = do+ eqExprWithTagCheck :: TyVarMap1 -> (Name, Name, Name, Name) -> Q Exp+ eqExprWithTagCheck tvMap (a, aHash, b, bHash) =+ condE (untagExpr [(a, aHash), (b, bHash)]+ (primOpAppExpr (varE aHash) neqIntHashValName (varE bHash)))+ (conE falseDataName)+ (caseE (varE a)+ (map (mkNestedMatchesForCon eClass tvMap b) nonNullaryCons+ ++ [ makeFallThroughMatchTrue+ | 0 < numNonNullaryCons && numNonNullaryCons < length cons+ ]))++newABNames :: Q (Name, Name, Name, Name)+newABNames = do a <- newName "a" aHash <- newName "a#" b <- newName "b" bHash <- newName "b#"+ return (a, aHash, b, bHash)++makeTagCase :: Q Clause+makeTagCase = do+ (a, aHash, b, bHash) <- newABNames clause (map varP [a,b]) (normalB $ untagExpr [(a, aHash), (b, bHash)] $ primOpAppExpr (varE aHash) eqIntHashValName (varE bHash)) [] -makeFallThroughCaseFalse, makeFallThroughCaseTrue :: Q Clause-makeFallThroughCaseFalse = makeFallThroughCase falseDataName-makeFallThroughCaseTrue = makeFallThroughCase trueDataName+makeFallThroughCaseTrue :: Q Clause+makeFallThroughCaseTrue = clause [wildP, wildP] (normalB $ conE trueDataName) [] -makeFallThroughCase :: Name -> Q Clause-makeFallThroughCase dataName = clause [wildP, wildP] (normalB $ conE dataName) []+makeFallThroughMatchFalse, makeFallThroughMatchTrue :: Q Match+makeFallThroughMatchFalse = makeFallThroughMatch falseDataName+makeFallThroughMatchTrue = makeFallThroughMatch trueDataName +makeFallThroughMatch :: Name -> Q Match+makeFallThroughMatch dataName = match wildP (normalB $ conE dataName) []+ makeCaseForCon :: EqClass -> TyVarMap1 -> ConstructorInfo -> Q Clause makeCaseForCon eClass tvMap (ConstructorInfo { constructorName = conName, constructorFields = ts }) = do@@ -230,6 +253,22 @@ clause [conP conName (map varP as), conP conName (map varP bs)] (normalB $ makeCaseForArgs eClass tvMap conName ts' as bs) []++mkNestedMatchesForCon :: EqClass -> TyVarMap1 -> Name -> ConstructorInfo -> Q Match+mkNestedMatchesForCon eClass tvMap b+ (ConstructorInfo { constructorName = conName, constructorFields = ts }) = do+ ts' <- mapM resolveTypeSynonyms ts+ let tsLen = length ts'+ as <- newNameList "a" tsLen+ bs <- newNameList "b" tsLen+ match (conP conName (map varP as))+ (normalB $ caseE (varE b)+ [ match (conP conName (map varP bs))+ (normalB $ makeCaseForArgs eClass tvMap conName ts' as bs)+ []+ , makeFallThroughMatchFalse+ ])+ [] makeCaseForArgs :: EqClass -> TyVarMap1
src/Data/Ord/Deriving.hs view
@@ -7,6 +7,10 @@ Portability: Template Haskell Exports functions to mechanically derive 'Ord', 'Ord1', and 'Ord2' instances.+Note that upstream GHC does not have the ability to derive 'Ord1' or 'Ord2'+instances, but since the functionality to derive 'Ord' extends very naturally+'Ord1' and 'Ord2', the ability to derive the latter two classes is provided as a+convenience. -} module Data.Ord.Deriving ( -- * 'Ord'
src/Text/Read/Deriving.hs view
@@ -7,6 +7,10 @@ Portability: Template Haskell Exports functions to mechanically derive 'Read', 'Read1', and 'Read2' instances.+Note that upstream GHC does not have the ability to derive 'Read1' or 'Read2'+instances, but since the functionality to derive 'Read' extends very naturally+'Read1' and 'Read2', the ability to derive the latter two classes is provided as a+convenience. -} module Text.Read.Deriving ( -- * 'Read'
src/Text/Show/Deriving.hs view
@@ -7,6 +7,10 @@ Portability: Template Haskell Exports functions to mechanically derive 'Show', 'Show1', and 'Show2' instances.+Note that upstream GHC does not have the ability to derive 'Show1' or 'Show2'+instances, but since the functionality to derive 'Show' extends very naturally+'Show1' and 'Show2', the ability to derive the latter two classes is provided as a+convenience. -} module Text.Show.Deriving ( -- * 'Show'
tests/FunctorSpec.hs view
@@ -8,6 +8,7 @@ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-} {-# LANGUAGE UndecidableInstances #-} #if __GLASGOW_HASKELL__ >= 708 {-# LANGUAGE EmptyCase #-}
tests/ShowSpec.hs view
@@ -5,6 +5,7 @@ {-# LANGUAGE RankNTypes #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-} #if __GLASGOW_HASKELL__ >= 708 {-# LANGUAGE EmptyCase #-} #endif
tests/Types/EqOrd.hs view
@@ -5,6 +5,7 @@ {-# LANGUAGE MagicHash #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-} {-| Module: Types.EqOrd@@ -88,6 +89,11 @@ data Empty a b +data TyConNullary a b+ = TyConNullary1+ | TyConNullary2+ | TyConNullary3+ -- Data families data family TyFamily1 y z :: *@@ -150,6 +156,13 @@ | TyFamilyWrap2 (f (g a)) | TyFamilyWrap3 (f (g (h a))) +data family TyFamilyNullary x y :: *++data instance TyFamilyNullary a b+ = TyFamilyNullary1+ | TyFamilyNullary2+ | TyFamilyNullary3+ ------------------------------------------------------------------------------- -- Plain data types@@ -162,11 +175,13 @@ (==) = $(makeEq ''TyConWrap) (/=) = $(makeNotEq ''TyConWrap) $(deriveEq ''Empty)+$(deriveEq ''TyConNullary) $(deriveEq1 ''TyCon1) $(deriveEq1 ''TyCon#) $(deriveEq1 ''TyCon2) $(deriveEq1 ''Empty)+$(deriveEq1 ''TyConNullary) $(deriveOrd ''TyCon1) $(deriveOrd ''TyCon#)@@ -186,6 +201,7 @@ $(deriveOrd1 ''TyCon#) $(deriveOrd1 ''TyCon2) $(deriveOrd1 ''Empty)+$(deriveOrd1 ''TyConNullary) #if defined(NEW_FUNCTOR_CLASSES) $(deriveEq1 ''TyConWrap)@@ -206,11 +222,13 @@ $(deriveEq2 ''TyCon#) $(deriveEq2 ''TyCon2) $(deriveEq2 ''Empty)+$(deriveEq2 ''TyConNullary) $(deriveOrd2 ''TyCon1) $(deriveOrd2 ''TyCon#) $(deriveOrd2 ''TyCon2) $(deriveOrd2 ''Empty)+$(deriveOrd2 ''TyConNullary) #endif #if MIN_VERSION_template_haskell(2,7,0)@@ -223,10 +241,12 @@ => Eq (TyFamilyWrap f g h a) where (==) = $(makeEq 'TyFamilyWrap1) (/=) = $(makeNotEq 'TyFamilyWrap1)+$(deriveEq 'TyFamilyNullary1) $(deriveEq1 'TyFamily1B) $(deriveEq1 'TyFamily#) $(deriveEq1 'TyFamilyEqualityConstraints)+$(deriveEq1 'TyFamilyNullary1) $(deriveOrd 'TyFamily1A) $(deriveOrd 'TyFamily#)@@ -240,10 +260,12 @@ (<=) = $(makeGE 'TyFamilyWrap1) max = $(makeMax 'TyFamilyWrap1) min = $(makeMin 'TyFamilyWrap1)+$(deriveOrd 'TyFamilyNullary1) $(deriveOrd1 'TyFamily1B) $(deriveOrd1 'TyFamily#) $(deriveOrd1 'TyFamilyEqualityConstraints)+$(deriveOrd1 'TyFamilyNullary1) #if defined(NEW_FUNCTOR_CLASSES) $(deriveEq1 'TyFamilyWrap2)@@ -263,9 +285,11 @@ $(deriveEq2 'TyFamily1C) $(deriveEq2 'TyFamily#) $(deriveEq2 'TyFamilyTypeRefinement1)+$(deriveEq2 'TyFamilyNullary1) $(deriveOrd2 'TyFamily1C) $(deriveOrd2 'TyFamily#) $(deriveOrd2 'TyFamilyTypeRefinement1)+$(deriveOrd2 'TyFamilyNullary1) # endif #endif