deepseq-generics (empty) → 0.1.0.0
raw patch · 5 files changed
+335/−0 lines, 5 filesdep +HUnitdep +basedep +deepseqsetup-changed
Dependencies added: HUnit, base, deepseq, deepseq-generics, ghc-prim, test-framework, test-framework-hunit
Files
- Control/DeepSeq/Generics.hs +104/−0
- LICENSE +30/−0
- Setup.hs +2/−0
- deepseq-generics.cabal +61/−0
- test/Suite.hs +138/−0
+ Control/DeepSeq/Generics.hs view
@@ -0,0 +1,104 @@+{-# LANGUAGE BangPatterns, TypeOperators, FlexibleContexts #-}++-- |+-- Module: Control.DeepSeq.Generics+-- Copyright: (c) 2012, Herbert Valerio Riedel+-- License: BSD-style (see the LICENSE file)+--+-- Maintainer: Herbert Valerio Riedel <hvr@gnu.org>+-- Stability: stable+-- Portability: GHC+--+-- Note: Beyond the primary scope of providing the 'genericRnf'+-- helper, this module also re-exports the definitions from+-- "Control.DeepSeq" for convenience. If this poses any+-- problems, just use qualified or explicit import statements+-- (see code usage example in the 'genericRnf' description)++module Control.DeepSeq.Generics+ ( genericRnf+ -- * "Control.DeepSeq" re-exports+ , deepseq+ , force+ , NFData(rnf)+ , ($!!)+ ) where++import Control.DeepSeq+import GHC.Generics++-- | "GHC.Generics"-based 'rnf' implementation+--+-- This provides a generic `rnf` implementation for one type at a+-- time. If the type of the value 'genericRnf' is asked to reduce to+-- NF contains values of other types, those types have to provide+-- 'NFData' instances. This also means that recursive types can only+-- be used with 'genericRnf' if a 'NFData' instance has been defined+-- as well (see examples below).+--+-- The typical usage for 'genericRnf' is for reducing boilerplate code+-- when defining 'NFData' instances for ordinary algebraic+-- datatypes. See the code below for some simple usage examples:+--+-- > {-# LANGUAGE DeriveGeneric #-}+-- >+-- > import Control.DeepSeq+-- > import Control.DeepSeq.Generics (genericRnf)+-- > import GHC.Generics+-- >+-- > -- simple record+-- > data Foo = Foo AccountId Name Address+-- > deriving Generic+-- >+-- > type Address = [String]+-- > type Name = String+-- > newtype AccountId = AccountId Int+-- >+-- > instance NFData AccountId+-- > instance NFData Foo where rnf = genericRnf+-- >+-- > -- recursive list-like type+-- > data N = Z | S N deriving Generic+-- >+-- > instance NFData N where rnf = genericRnf+-- >+-- > -- parametric & recursive type+-- > data Bar a = Bar0 | Bar1 a | Bar2 (Bar a)+-- > deriving Generic+-- >+-- > instance NFData a => NFData (Bar a) where rnf = genericRnf+--+-- Note: The 'GNFData' type-class showing up in the type-signature is+-- used internally and not exported on purpose currently.++genericRnf :: (Generic a, GNFData (Rep a)) => a -> ()+genericRnf = grnf_ . from+{-# INLINE genericRnf #-}++-- | Hidden internal type-class+class GNFData f where+ grnf_ :: f a -> ()++-- note: the V1 instance is not provided, as uninhabited types can't+-- be reduced to NF anyway++instance GNFData U1 where+ grnf_ !U1 = ()+ {-# INLINE grnf_ #-}++instance NFData a => GNFData (K1 i a) where+ grnf_ = rnf . unK1+ {-# INLINE grnf_ #-}++instance GNFData a => GNFData (M1 i c a) where+ grnf_ = grnf_ . unM1+ {-# INLINE grnf_ #-}++instance (GNFData a, GNFData b) => GNFData (a :*: b) where+ grnf_ (x :*: y) = grnf_ x `seq` grnf_ y+ {-# INLINE grnf_ #-}++instance (GNFData a, GNFData b) => GNFData (a :+: b) where+ grnf_ (L1 x) = grnf_ x+ grnf_ (R1 x) = grnf_ x+ {-# INLINE grnf_ #-}
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2012, Herbert Valerio Riedel++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * Redistributions in binary form must reproduce the above+ copyright notice, this list of conditions and the following+ disclaimer in the documentation and/or other materials provided+ with the distribution.++ * Neither the name of Herbert Valerio Riedel nor the names of other+ contributors may be used to endorse or promote products derived+ from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ deepseq-generics.cabal view
@@ -0,0 +1,61 @@+-- Initial deepseq-generics.cabal generated by cabal init. For further +-- documentation, see http://haskell.org/cabal/users-guide/++name: deepseq-generics+version: 0.1.0.0+synopsis: GHC.Generics-based Control.DeepSeq.rnf implementation+homepage: https://github.com/hvr/deepseq-generics+license: BSD3+license-file: LICENSE+author: Herbert Valerio Riedel+maintainer: hvr@gnu.org+copyright: 2012, Herbert Valerio Riedel+category: Control+build-type: Simple+cabal-version: >=1.10+tested-with: GHC >=7.4.1+description: + This package provides a "GHC.Generics"-based+ 'Control.DeepSeq.Generics.genericRnf' function which can be used+ for providing a 'rnf' implementation. See the documentation for+ the 'genericRnf' function in the "Control.DeepSeq.Generics" module+ to get started.+ .+ The original idea was pioneered in the @generic-deepseq@ package+ (see <http://www.haskell.org/pipermail/haskell-cafe/2012-February/099551.html>+ for more information). + .+ This package differs from the @generic-deepseq@ package by working+ in combination with the existing @deepseq@ package as opposed to defining a+ conflicting drop-in replacement for @deepseq@'s @Control.Deepseq@ module.++source-repository head+ type: git+ location: https://github.com/hvr/deepseq-generics.git++library+ default-language: Haskell2010+ exposed-modules: Control.DeepSeq.Generics+ build-depends: base >= 4.5 && < 4.7, ghc-prim >= 0.2 && < 0.4, deepseq >= 1.2.0.1 && < 1.4+ other-extensions: BangPatterns, TypeOperators, FlexibleContexts+ ghc-options: -Wall++test-suite tests+ default-language: Haskell2010+ type: exitcode-stdio-1.0+ main-is: Suite.hs+ hs-source-dirs: test+ other-extensions: CPP, TupleSections, DeriveDataTypeable+ ghc-options: -Wall++ build-depends:+ base,+ deepseq,+ deepseq-generics,+ ghc-prim,+ -- end of packages with inherited version constraints+ test-framework, + test-framework-hunit,+ HUnit++
+ test/Suite.hs view
@@ -0,0 +1,138 @@+{-# LANGUAGE CPP, TupleSections, DeriveDataTypeable, DeriveGeneric #-}++module Main (main) where++import Control.Concurrent.MVar+import Control.DeepSeq+import Control.Exception+import Control.Monad+import Data.Bits+import Data.IORef+import Data.Typeable+import Data.Word+import GHC.Generics+import System.IO.Unsafe (unsafePerformIO)++-- import Test.Framework (defaultMain, testGroup, testCase)+import Test.Framework+import Test.Framework.Providers.HUnit+import Test.HUnit++-- IUT+import Control.DeepSeq.Generics++-- needed for GHC-7.4 compatibility+#if !MIN_VERSION_base(4,6,0)+atomicModifyIORef' :: IORef a -> (a -> (a,b)) -> IO b+atomicModifyIORef' ref f = do+ b <- atomicModifyIORef ref+ (\x -> let (a, b) = f x+ in (a, a `seq` b))+ b `seq` return b+#endif++----------------------------------------------------------------------------+-- simple hacky abstraction for testing forced evaluation via `rnf`-like functions++seqStateLock :: MVar ()+seqStateLock = unsafePerformIO $ newMVar ()+{-# NOINLINE seqStateLock #-}++withSeqState :: Word64 -> IO () -> IO ()+withSeqState expectedState act = withMVar seqStateLock $ \() -> do+ 0 <- resetSeqState+ () <- act+ st <- resetSeqState+ unless (st == expectedState) $+ assertFailure ("withSeqState: actual seq-state ("++show st++") doesn't match expected value ("+++ show expectedState++")")++seqState :: IORef Word64+seqState = unsafePerformIO $ newIORef 0+{-# NOINLINE seqState #-}++resetSeqState :: IO Word64+resetSeqState = atomicModifyIORef' seqState (0,)++-- |Set flag and raise exception is flag already set+setSeqState :: Int -> IO ()+setSeqState i | 0 <= i && i < 64 = atomicModifyIORef' seqState go+ | otherwise = error "seqSeqState: flag index must be in [0..63]"+ where+ go x | testBit x i = error ("setSeqState: flag #"++show i++" already set")+ | otherwise = (setBit x i, ())++-- weird type whose NFData instacne calls 'setSeqState' when rnf-ed+data SeqSet = SeqSet !Int | SeqIgnore+ deriving Show++instance NFData SeqSet where+ rnf (SeqSet i) = unsafePerformIO $ setSeqState i+ rnf (SeqIgnore) = ()+ {-# NOINLINE rnf #-}++-- |Exception to be thrown for testing 'seq'/'rnf'+data RnfEx = RnfEx deriving (Eq, Show, Typeable)++instance Exception RnfEx++instance NFData RnfEx where rnf e = throw e++assertRnfEx :: () -> IO ()+assertRnfEx v = handleJust isWanted (const $ return ()) $ do+ () <- evaluate v+ assertFailure "failed to trigger expected RnfEx exception"+ where isWanted = guard . (== RnfEx)++----------------------------------------------------------------------------++case_1, case_2, case_3, case_4_1, case_4_2, case_4_3, case_4_4 :: Test.Framework.Test++newtype Case1 = Case1 Int+ deriving Generic+case_1 = testCase "Case1" $ do+ assertRnfEx $ genericRnf $ (Case1 (throw RnfEx))++----++data Case2 = Case2 Int+ deriving Generic+case_2 = testCase "Case2" $ do+ assertRnfEx $ genericRnf $ (Case2 (throw RnfEx))++----++data Case3 = Case3 RnfEx+ deriving Generic+case_3 = testCase "Case3" $ do+ assertRnfEx $ genericRnf $ Case3 RnfEx++----++data Case4 a = Case4a+ | Case4b a a+ | Case4c a (Case4 a)+ deriving Generic+instance NFData a => NFData (Case4 a) where rnf = genericRnf++case_4_1 = testCase "Case4.1" $ withSeqState 0x0 $ do+ evaluate $ rnf $ (Case4a :: Case4 SeqSet)++case_4_2 = testCase "Case4.2" $ withSeqState 0x3 $ do+ evaluate $ rnf $ (Case4b (SeqSet 0) (SeqSet 1) :: Case4 SeqSet)++case_4_3 = testCase "Case4.3" $ withSeqState (bit 55) $ do+ evaluate $ rnf $ (Case4b SeqIgnore (SeqSet 55) :: Case4 SeqSet)++case_4_4 = testCase "Case4.4" $ withSeqState 0xffffffffffffffff $ do+ evaluate $ rnf $ (genCase 63)+ where+ genCase n | n > 1 = Case4c (SeqSet n) (genCase (n-1))+ | otherwise = Case4b (SeqSet 0) (SeqSet 1)++----------------------------------------------------------------------------++main :: IO ()+main = defaultMain [tests]+ where+ tests = testGroup "" [case_1, case_2, case_3, case_4_1, case_4_2, case_4_3, case_4_4]