{-# 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__: Starting with @deepseq-1.4.0.0@, 'NFData' gained support -- for generic derivation via @DefaultSignatures@. The new default -- 'rnf' method implementation is then equivalent to -- -- @ -- instance NFData MyType where -- 'rnf' = 'genericRnfV1' -- @ -- -- See documentation of 'rnf' for more details on how to use the new -- built-in 'Generic' support. module Control.DeepSeq.Generics ( genericRnf , genericRnfV1 ) 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. genericRnf :: (Generic a, GNFData (Rep a)) => a -> () genericRnf = grnf_ . from {-# INLINE genericRnf #-} -- | Hidden internal type-class -- -- __NOTE__: the 'V1' instance is not provided for 'GNFData' in order to -- trigger a compile-time error; see 'GNFDataV1' which defers this to -- a runtime error. class GNFData f where grnf_ :: f a -> () instance GNFData U1 where grnf_ U1 = () instance NFData a => GNFData (K1 i a) where grnf_ = rnf . unK1 {-# INLINEABLE grnf_ #-} instance GNFData a => GNFData (M1 i c a) where grnf_ = grnf_ . unM1 {-# INLINEABLE grnf_ #-} instance (GNFData a, GNFData b) => GNFData (a :*: b) where grnf_ (x :*: y) = grnf_ x `seq` grnf_ y {-# INLINEABLE grnf_ #-} instance (GNFData a, GNFData b) => GNFData (a :+: b) where grnf_ (L1 x) = grnf_ x grnf_ (R1 x) = grnf_ x {-# INLINEABLE grnf_ #-} -- | Variant of 'genericRnf' which supports derivation for uninhabited types. -- -- For instance, the type -- -- > data TagFoo deriving Generic -- -- would cause a compile-time error with 'genericRnf', but with -- 'genericRnfV1' the error is deferred to run-time: -- -- > Prelude> genericRnf (undefined :: TagFoo) -- > -- > <interactive>:1:1: -- > No instance for (GNFData V1) arising from a use of `genericRnf' -- > Possible fix: add an instance declaration for (GNFData V1) -- > In the expression: genericRnf (undefined :: TagFoo) -- > In an equation for `it': it = genericRnf (undefined :: TagFoo) -- > -- > Prelude> genericRnfV1 (undefined :: TagFoo) -- > *** Exception: Control.DeepSeq.Generics.genericRnfV1: uninhabited type -- -- 'genericRnfV1' corresponds to @deepseq-1.4.0.0@'s default @rnf@ -- method implementation. -- -- __NOTE__: The 'GNFDataV1' type-class showing up in the type-signature is -- used internally and not exported. -- -- @since 0.1.1.0 genericRnfV1 :: (Generic a, GNFDataV1 (Rep a)) => a -> () genericRnfV1 = grnfV1_ . from {-# INLINE genericRnfV1 #-} -- | Variant of 'GNFData' supporting 'V1' class GNFDataV1 f where grnfV1_ :: f a -> () instance GNFDataV1 V1 where grnfV1_ = error "Control.DeepSeq.Generics.genericRnfV1: uninhabited type" instance GNFDataV1 U1 where grnfV1_ U1 = () instance NFData a => GNFDataV1 (K1 i a) where grnfV1_ = rnf . unK1 {-# INLINEABLE grnfV1_ #-} instance GNFDataV1 a => GNFDataV1 (M1 i c a) where grnfV1_ = grnfV1_ . unM1 {-# INLINEABLE grnfV1_ #-} instance (GNFDataV1 a, GNFDataV1 b) => GNFDataV1 (a :*: b) where grnfV1_ (x :*: y) = grnfV1_ x `seq` grnfV1_ y {-# INLINEABLE grnfV1_ #-} instance (GNFDataV1 a, GNFDataV1 b) => GNFDataV1 (a :+: b) where grnfV1_ (L1 x) = grnfV1_ x grnfV1_ (R1 x) = grnfV1_ x {-# INLINEABLE grnfV1_ #-}