packages feed

deepseq-th 0.0.0.0 → 0.1.0.0

raw patch · 2 files changed

+117/−27 lines, 2 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Control.DeepSeq.TH: whnfIsNf :: Type -> Maybe Bool
+ Control.DeepSeq.TH: decWhnfIsNf :: Dec -> Q (Maybe Bool)
+ Control.DeepSeq.TH: typeWhnfIsNf :: Type -> Q (Maybe Bool)

Files

Control/DeepSeq/TH.hs view
@@ -1,9 +1,19 @@ {-# LANGUAGE TemplateHaskell #-} +-- |Module providing Template Haskell based 'NFData' instance+-- generators and WHNF=NF type inspectors.+--+-- To use this module enable the @TemplateHaskell@ extension and+-- import "Control.DeepSeq.TH":+--+-- > {-# LANGUAGE TemplateHaskell #-}+-- > import Control.DeepSeq.TH+-- module Control.DeepSeq.TH     ( deriveNFData     , deriveNFDatas-    , whnfIsNf+    , typeWhnfIsNf+    , decWhnfIsNf     ) where  import Control.DeepSeq     (NFData(rnf),deepseq)@@ -12,26 +22,86 @@ import Data.Maybe          (fromMaybe, isJust, catMaybes) import Language.Haskell.TH --- |Try to infer whether type has the property that WHNF=NF for its+-- |Try to infer whether 'Type' has the property that WHNF=NF for its -- values. -- -- A result of @Nothing@ means it is not known whether the -- property holds for the given type. @Just True@ means that the -- property holds. ----- This function has currently a very limited knowledge and returns--- @Nothing@ most of the time except for some primitive types.-whnfIsNf :: Type -> Maybe Bool-whnfIsNf (ConT x)-    | x `elem` [''Int, ''Double, ''Float, ''Char, ''Bool, ''()] = Just True-whnfIsNf (AppT ListT _) = Just False -- [a]-whnfIsNf (AppT (TupleT _) _) = Just False -- [a]-whnfIsNf _ = Nothing+-- This function has currently a rather limited knowledge and returns+-- @Nothing@ most of the time except for some primitive types and+-- other simple cases.+--+-- See also 'decWhnfIsNf'+typeWhnfIsNf :: Type -> Q (Maybe Bool)+typeWhnfIsNf = typeWhnfIsNf2 [] --- Wrapper for 'whnfIsNf' defaulting to 'False'-whnfIsNf' :: Type -> Bool-whnfIsNf' = fromMaybe False . whnfIsNf+typeWhnfIsNf2 :: [Name] -> Type -> Q (Maybe Bool)+typeWhnfIsNf2 seen (ConT x)+    | x `elem` [''Int, ''Double, ''Float, ''Char, ''Bool, ''()] = return $ Just True+    | x `elem` seen = return $ Just True  -- FIXME: check whether this correct+                      -- e.g. it might break with parametrized types (which we don't handle yet anyway)+    | otherwise = do+        t <- reify x+        case t of+            TyConI dec -> decWhnfIsNf2 (x:seen) dec+            _          -> return Nothing +typeWhnfIsNf2 _ (AppT (AppT ArrowT _) _) = return $ Just True -- a -> b+typeWhnfIsNf2 _ (AppT ListT _)           = return $ Just False -- [a]+typeWhnfIsNf2 _ (AppT (TupleT _) _)      = return $ Just False -- (a,b,...)+typeWhnfIsNf2 _ _                        = return  Nothing++-- |Try to infer whether a 'Dec' which defines a type which has the+-- property that WHNF=NF for its values. This property is derived+-- statically via the following simple rules:+--+--  * @newtype@s are WHNF=NF if the wrapped type is WHNF=NF+--+--  * @type@s are WHNF=NF if the aliased type is WHNF=NF+--+--  * Types defined by @data@ are WHNF=NF if all constructors contain+--    only strict fields with WHNF=NF types+--+-- Known limitations:+--+--  * Doesn't work properly with parametrized declarations (in which+--    case @Nothing@ is returned) or existential types+--+-- See also 'typeWhnfIsNf'+decWhnfIsNf :: Dec -> Q (Maybe Bool)+decWhnfIsNf = decWhnfIsNf2 []++decWhnfIsNf2 :: [Name] -> Dec -> Q (Maybe Bool)+decWhnfIsNf2 seen (NewtypeD _ _ _ (NormalC _ [(NotStrict, t)]) _) = typeWhnfIsNf2 seen t+decWhnfIsNf2 seen (NewtypeD _ _ _ (RecC  _ [(_,NotStrict, t)]) _) = typeWhnfIsNf2 seen t+decWhnfIsNf2 seen (TySynD _ _ t)                                  = typeWhnfIsNf2 seen t+decWhnfIsNf2 _    (DataD _ _ _ [] _)                              = return Nothing+decWhnfIsNf2 seen (DataD _ _ _ cons _)                            = reduce `liftM` mapM conWhnfIsNf cons+  where+    conWhnfIsNf :: Con -> Q (Maybe Bool)+    conWhnfIsNf con+        | allStrict  = do+            ms <- mapM (typeWhnfIsNf2 seen) fTypes+            return $ reduce ms+        | otherwise  = return $ Just False+      where+        allStrict = all (== IsStrict) fStricts+        (fStricts, fTypes) = unzip $ con2types con++    con2types (NormalC _ ts)   = ts+    con2types (RecC _ vts)     = [ (tst,tt) | (_,tst,tt) <- vts ]+    con2types (InfixC tl _ tr) = [tl,tr]+    con2types (ForallC {})     = [] -- FIXME++    reduce :: [Maybe Bool] -> Maybe Bool+    reduce ms | all (==Just True)  ms  = Just True+              | any (==Just False) ms  = Just False+              | otherwise              = Nothing++decWhnfIsNf2 _    _                                               = return Nothing+ -- | Derive 'NFData' instance for simple @Data@-declarations -- -- Example usage for deriving 'NFData' instance for the type @TypeName@:@@ -39,14 +109,19 @@ -- > $(deriveNFData ''TypeName) -- -- The derivation tries to avoid evaluation of strict fields whose--- types have the WHNF=NF property (see also 'whnfIsNf'). For--- instance, consider the following type @Foo@:+-- types have the WHNF=NF property (see also 'typeWhnfIsNf' and+-- 'decWhnfIsNf'). For instance, consider the following types @Foo@+-- and @Bar@: -- -- > data Foo a = Foo1 -- >            | Foo2 !Int !String -- >            | Foo3 (Foo a) -- >            | Foo4 { fX :: Int, fY :: Char }+-- >            | Foo5 !Bar+-- >            | Foo6 !(String -> Int) -- >            | Foo a :--: !Bool+-- >+-- > data Bar = Bar0 | Bar1 !Char | Bar2 !Int !Int | Bar3 !Bar -- -- By invoking @$(deriveNFData ''Foo)@ the generated 'NFData' instance -- will be equivalent to:@@ -56,8 +131,15 @@ -- >     rnf (Foo2 _ x) = x `deepseq` () -- >     rnf (Foo3 x)   = x `deepseq` () -- >     rnf (Foo4 x y) = x `deepseq` y `deepseq` ()+-- >     rnf (Foo5 _)   = ()+-- >     rnf (Foo6 _)   = () -- >     rnf (x :--: _) = x `deepseq` () --+-- Whereas @$(deriveNFData ''Bar)@ generates the following default+-- 'NFData' instance since @Bar@ is inferred as a WHNF=NF type:+--+-- > instance NFData Bar+-- -- Known issues/limitations: -- --  * @TypeName@ must be a proper @data@ typename (use the@@ -75,15 +157,19 @@     dec <- reify tn      case dec of-        TyConI (DataD _ctx _tn tvs ctors _) -> do+        TyConI idec@(DataD _ctx _tn tvs ctors _) -> do             clauses_names <- mapM con2rnf ctors             let clauses = map fst clauses_names                 names   = nub $ concat $ map snd clauses_names                 ctxt    = [ClassP ''NFData [VarT n] | n <- names ]             let ity = foldl (\t tvn -> AppT t (VarT tvn)) (ConT tn) $ map tyvarname tvs -            return [ InstanceD ctxt (AppT (ConT ''NFData) ity) [FunD 'rnf clauses] ]+            isWhnfEqNf <- decWhnfIsNf idec +            return $ case isWhnfEqNf of -- short-cut if data-type is strict as a whole+                Just True -> [ InstanceD ctxt (AppT (ConT ''NFData) ity) [] ] -- default NFData instance+                _         -> [ InstanceD ctxt (AppT (ConT ''NFData) ity) [FunD 'rnf clauses] ]+         TyConI (NewtypeD {}) -> do             fail $ "deriveNFData ''" ++ show tn ++ ": please use GeneralizedNewtypeDeriving " ++                    "for deriving NFData instances for newtype"@@ -112,11 +198,15 @@     -- generic per-constructor function generator     genCon2Rnf :: Name -> [(Strict,Type)] -> Q (Clause, [Name])     genCon2Rnf n ts = do-      let vns = concatMap getFreeTyVars $ catMaybes ts'-          ts' = [ if tst == NotStrict || not (whnfIsNf' tt) then Just tt else Nothing | (tst,tt) <- ts ]-      vars <- tys2vars ts'-      return (Clause [ConP n vars] (NormalB $ mkDeepSeqExpr [ n' | VarP n' <- vars ]) [], vns)-+        ts' <- mapM hlp ts+        let vns = concatMap getFreeTyVars $ catMaybes ts'+        vars <- tys2vars ts'+        return (Clause [ConP n vars] (NormalB $ mkDeepSeqExpr [ n' | VarP n' <- vars ]) [], vns)+      where+        hlp (NotStrict, fieldType) = return $ Just fieldType+        hlp (IsStrict, fieldType) = do+            tmp <- typeWhnfIsNf fieldType+            return $ if fromMaybe False tmp then Nothing else Just fieldType  -- |Plural version of 'deriveNFData' --
deepseq-th.cabal view
@@ -1,15 +1,15 @@ name:         deepseq-th-version:      0.0.0.0+version:      0.1.0.0 license:      BSD3 license-file: LICENSE maintainer:   hvr@gnu.org bug-reports:  https://github.com/hvr/hs-deepseq-th/issues-synopsis:     Provides Template Haskell deriver for NFData instances+synopsis:     Template Haskell based deriver for optimised NFData instances category:     Control description:-    Provides a Template Haskell based mechanism for deriving NFData-    instances for custom data types. See documentation in-    "Control.DeepSeq.TH" for more information.+    This package provides a Template Haskell based mechanism for+    deriving optimised NFData instances for custom data types. See+    documentation in "Control.DeepSeq.TH" for more information.     . build-type:     Simple cabal-version:  >=1.10