on master help sign in
Use more expressive notation for deriveSerializeWith
commit60a71dcbb519dcfe006520be89e2cf57452f8b68 authorAdithya Kumar <adithya@composewell.com> dateThu 14 Sep 2023, 18:44 (3 years ago) parent4f00d1f
5 files changed+72−64
core/src/Streamly/Internal/Data/Serialize.hs+2−2 core/src/Streamly/Internal/Data/Serialize/TH.hs+48−36 core/src/Streamly/Internal/Data/Unbox/TH.hs+3−12 test/Streamly/Test/Data/Serialize.hs+17−12 test/Streamly/Test/Data/Unbox.hs+2−2
core/src/Streamly/Internal/Data/Serialize.hs+2−2
@@ -61,8 +61,8 @@ import Streamly.Internal.Data.Serialize.Type
6161$(Serialize.deriveSerialize ''Maybe)
6262$(Serialize.deriveSerialize ''Either)
6363$(Serialize.deriveSerializeWith
64− (Serialize.defaultConfig {Serialize.unconstrained = ["t"]})
65− ''Proxy)
64+ Serialize.defaultConfig
65+ [d|instance Serialize (Proxy a)|])
6666
6767--------------------------------------------------------------------------------
6868-- Integer
core/src/Streamly/Internal/Data/Serialize/TH.hs+48−36
@@ -30,7 +30,6 @@ import Streamly.Internal.Data.Unbox.TH
3030 ( DataCon(..)
3131 , DataType(..)
3232 , appsT
33− , plainInstanceD
3433 , reifyDataType
3534 )
3635
@@ -381,44 +380,47 @@ mkSerializeDec (Config {..}) headTy cons = do
381380-- ])
382381-- @
383382deriveSerializeInternal ::
384− Config -> Cxt -> Type -> [DataCon] -> Q [Dec]
385−deriveSerializeInternal conf preds headTy cons = do
383+ Config -> Type -> [DataCon] -> ([Dec] -> Q [Dec]) -> Q [Dec]
384+deriveSerializeInternal conf headTy cons next = do
386385 sizeDec <- mkSizeDec conf headTy cons
387386 peekDec <- mkDeserializeDec conf headTy cons
388387 pokeDec <- mkSerializeDec conf headTy cons
389388 let methods = concat [sizeDec, peekDec, pokeDec]
390− return [plainInstanceD preds (AppT (ConT ''Serialize) headTy) methods]
389+ next methods
391390
392−-- | Similar to 'deriveSerialize,' but take a 'Config' to control how
393−-- the instance is generated.
391+-- | Similar to 'deriveSerialize' but take a 'Config' to control how the
392+-- instance is generated.
394393--
395−-- Usage: @$(deriveSerializeWith config ''CustomDataType)@
396−deriveSerializeWith :: Config -> Name -> Q [Dec]
397−deriveSerializeWith conf@(Config {..}) name = do
398− dt <- reifyDataType name
399− let preds = map (unboxPred . VarT) (filterOutVars (dtTvs dt))
400− headTy = appsT (ConT name) (map substituteVar (dtTvs dt))
401− cons = dtCons dt
402− deriveSerializeInternal conf preds headTy cons
394+-- Usage:
395+-- @
396+-- $(deriveSerializeWith
397+-- config
398+-- [d|instance Serialize a => Serialize (Maybe a)|])
399+-- @
400+deriveSerializeWith :: Config -> Q [Dec] -> Q [Dec]
401+deriveSerializeWith conf mDecs = do
402+ dec <- mDecs
403+ case dec of
404+ [inst@(InstanceD _ _ headTy [])] -> do
405+ dt <- reifyDataType (getMainTypeName headTy)
406+ let cons = dtCons dt
407+ deriveSerializeInternal conf headTy cons (next inst)
408+ _ -> errorUnsupported
409+ where
410+
411+ next (InstanceD mo preds headTy []) methods =
412+ pure [InstanceD mo preds headTy methods]
413+ next _ _ = errorUnsupported
414+
415+ getMainTypeName = go . unwrap
416+
417+ unwrap (AppT (ConT _) r) = r
418+ unwrap _ = error "getMainTypeName.unwrap: Undefined instance declaration."
419+
420+ go (ConT nm) = nm
421+ go (AppT l _) = go l
422+ go _ = error "getMainTypeName.go: Undefined instance declaration."
403423
404− where
405− allUnconstrainedTypeVars =
406− unconstrained ++ map fst specializations
407− filterOutVars vs =
408− map mkName
409− $ filter (not . flip elem allUnconstrainedTypeVars)
410− $ map nameBase vs
411− substituteVar v =
412− case lookup (nameBase v) specializations of
413− Nothing -> VarT v
414− Just ty -> ty
415−
416− unboxPred ty =
417−#if MIN_VERSION_template_haskell(2,10,0)
418− AppT (ConT ''Serialize) ty
419−#else
420− ClassP ''Serialize [ty]
421−#endif
422424
423425-- | Template haskell helper to create instances of 'Serialize' automatically.
424426--
@@ -438,8 +440,18 @@ deriveSerializeWith conf@(Config {..}) name = do
438440--
439441-- To control which type variables don't get the Serialize constraint, use
440442-- 'deriveSerializeWith'.
441−--
442−-- >>> import qualified Streamly.Internal.Data.Serialize.TH as Serialize
443−-- >>> deriveSerialize = Serialize.deriveSerializeWith Serialize.defaultConfig
444443deriveSerialize :: Name -> Q [Dec]
445−deriveSerialize name = deriveSerializeWith defaultConfig name
444+deriveSerialize name = do
445+ dt <- reifyDataType name
446+ let preds = map (unboxPred . VarT) (dtTvs dt)
447+ headTy = appsT (ConT name) (map VarT (dtTvs dt))
448+ cons = dtCons dt
449+ deriveSerializeInternal defaultConfig headTy cons (next preds headTy)
450+
451+
452+ where
453+
454+ next preds headTy methods =
455+ pure [InstanceD Nothing preds (AppT (ConT ''Serialize) headTy) methods]
456+
457+ unboxPred ty = AppT (ConT ''Serialize) ty
core/src/Streamly/Internal/Data/Unbox/TH.hs+3−12
@@ -17,7 +17,6 @@ module Streamly.Internal.Data.Unbox.TH
1717 , DataType(..)
1818 , reifyDataType
1919 , appsT
20− , plainInstanceD
2120 ) where
2221
2322--------------------------------------------------------------------------------
@@ -35,6 +34,8 @@ import Streamly.Internal.Data.Unbox
3534-- th-utilities
3635--------------------------------------------------------------------------------
3736
37+-- Note: We don't support template-haskell < 2.14 (GHC < 8.6)
38+
3839-- The following are copied to remove the dependency on th-utilities.
3940-- The code has been copied from th-abstraction and th-utilities.
4041
@@ -72,16 +73,6 @@ appsT :: Type -> [Type] -> Type
7273appsT x [] = x
7374appsT x (y:xs) = appsT (AppT x y) xs
7475
75−-- | Utility to conveniently handle change to 'InstanceD' API in
76−-- template-haskell-2.11.0
77−plainInstanceD :: Cxt -> Type -> [Dec] -> Dec
78−plainInstanceD =
79−#if MIN_VERSION_template_haskell(2,11,0)
80− InstanceD Nothing
81−#else
82− InstanceD
83−#endif
84−
8576-- | Simplified info about a 'DataD'. Omits deriving, strictness,
8677-- kind info, and whether it's @data@ or @newtype@.
8778data DataType = DataType
@@ -437,7 +428,7 @@ deriveUnboxInternal preds headTy cons = do
437428 []
438429 ]
439430 ]
440− return [plainInstanceD preds (AppT (ConT ''Unbox) headTy) methods]
431+ return [InstanceD Nothing preds (AppT (ConT ''Unbox) headTy) methods]
441432
442433-- | Template haskell helper to create instances of 'Unbox' automatically.
443434--
test/Streamly/Test/Data/Serialize.hs+17−12
@@ -24,18 +24,22 @@ module Streamly.Test.Data.Serialize (main) where
2424import System.Random (randomRIO)
2525import Streamly.Internal.Data.Unbox (MutableByteArray, newBytes)
2626import GHC.Generics (Generic)
27+import Streamly.Data.Serialize (Serialize)
2728import Streamly.Test.Data.Serialize.TH (genDatatype)
29+
2830import qualified Streamly.Internal.Data.Serialize.TH as Serialize
29− ( Config(..)
31+ ( deriveSerializeWith
32+#ifdef ENABLE_constructorTagAsString
33+ , Config(..)
34+#endif
3035 , defaultConfig
31− , deriveSerializeWith
3236 )
37+
3338import Data.Functor.Identity (Identity (..))
3439
3540import qualified Streamly.Internal.Data.Array as Array
3641import qualified Streamly.Internal.Data.Serialize as Serialize
3742
38−import Language.Haskell.TH
3943import Test.Hspec.QuickCheck
4044import Test.QuickCheck
4145import Test.Hspec as H
@@ -59,19 +63,19 @@ import Test.Hspec as H
5963data Unit =
6064 Unit
6165 deriving (Eq, Show)
62−$(Serialize.deriveSerializeWith CONF ''Unit)
66+$(Serialize.deriveSerializeWith CONF [d|instance Serialize Unit|])
6367
6468data The =
6569 The Unit Int Char
6670 deriving (Eq, Show)
67−$(Serialize.deriveSerializeWith CONF ''The)
71+$(Serialize.deriveSerializeWith CONF [d|instance Serialize The|])
6872
6973--------------------------------------------------------------------------------
7074-- Generated types
7175--------------------------------------------------------------------------------
7276
7377$(genDatatype "CustomDatatype" 15)
74−$(Serialize.deriveSerializeWith CONF ''CustomDatatype)
78+$(Serialize.deriveSerializeWith CONF [d|instance Serialize CustomDatatype|])
7579
7680--------------------------------------------------------------------------------
7781-- Types with functional parameters
@@ -89,11 +93,10 @@ instance (Eq (f Int), Eq (f Char)) => Eq (HigherOrderType f) where
8993instance (Show (f Int), Show (f Char)) => Show (HigherOrderType f) where
9094 show a = "HigherOrderType " ++ show (field0 a) ++ " " ++ show (field1 a)
9195
92−$(Serialize.deriveSerializeWith CONF ''Identity)
93−$(Serialize.deriveSerializeWith
94− (CONF
95− {Serialize.specializations = [("f", ConT ''Identity)]})
96− ''HigherOrderType)
96+$(Serialize.deriveSerializeWith CONF
97+ [d|instance Serialize a => Serialize (Identity a)|])
98+$(Serialize.deriveSerializeWith CONF
99+ [d|instance Serialize (HigherOrderType Identity)|])
97100
98101--------------------------------------------------------------------------------
99102-- Recursive type
@@ -105,7 +108,9 @@ data BinTree a
105108 | Leaf a
106109 deriving (Show, Read, Eq, Generic)
107110
108−$(Serialize.deriveSerializeWith CONF ''BinTree)
111+$(Serialize.deriveSerializeWith
112+ CONF
113+ [d|instance Serialize a => Serialize (BinTree a)|])
109114
110115instance Arbitrary a => Arbitrary (BinTree a) where
111116 arbitrary = oneof [Leaf <$> arbitrary, Tree <$> arbitrary <*> arbitrary]
test/Streamly/Test/Data/Unbox.hs+2−2
@@ -193,8 +193,8 @@ deriving instance Generic (Ratio Int)
193193$(Serialize.deriveSerialize ''Complex)
194194$(Serialize.deriveSerialize ''Ratio)
195195$(Serialize.deriveSerializeWith
196− (Serialize.defaultConfig {Serialize.unconstrained = ["b"]})
197− ''Const)
196+ Serialize.defaultConfig
197+ [d|instance Serialize a => Serialize (Const a b)|])
198198$(Serialize.deriveSerialize ''Identity)
199199#endif
200200