nothunks-0.1.0.0: Examine values for unexpected thunks

Safe HaskellNone
LanguageHaskell2010

NoThunks.Class

Contents

Synopsis

Check a value for unexpected thunks

class NoThunks a where Source #

Check a value for unexpected thunks

Minimal complete definition

Nothing

Methods

noThunks :: Context -> a -> IO (Maybe ThunkInfo) Source #

Check if the argument does not contain any unexpected thunks

For most datatypes, we should have that

noThunks ctxt x == NoThunks

if and only if

containsThunks x

For some datatypes however, some thunks are expected. For example, the internal fingertree Sequence might contain thunks (this is important for the asymptotic complexity of this data structure). However, we should still check that the values in the sequence don't contain any unexpected thunks.

This means that we need to traverse the sequence, which might force some of the thunks in the tree. In general, it is acceptable for noThunks to force such "expected thunks", as long as it always reports the unexpected thunks.

The default implementation of noThunks checks that the argument is in WHNF, and if so, adds the type into the context (using showTypeOf), and calls wNoThunks. See UnexpectedThunkInfo for a detailed discussion of the type context.

See also discussion of caveats listed for containsThunks.

wNoThunks :: Context -> a -> IO (Maybe ThunkInfo) Source #

Check that the argument is in normal form, assuming it is in WHNF.

The context will already have been extended with the type we're looking at, so all that's left is to look at the thunks inside the type. The default implementation uses GHC Generics to do this.

wNoThunks :: (Generic a, GWNoThunks '[] (Rep a)) => Context -> a -> IO (Maybe ThunkInfo) Source #

Check that the argument is in normal form, assuming it is in WHNF.

The context will already have been extended with the type we're looking at, so all that's left is to look at the thunks inside the type. The default implementation uses GHC Generics to do this.

showTypeOf :: Proxy a -> String Source #

Show type a (to add to the context)

We try hard to avoid Typeable constraints in this module: there are types with no Typeable instance but with a NoThunks instance (most important example are types such as ST s which rely on parametric polymorphism). By default we should therefore only show the "outer layer"; for example, if we have a type

Seq (ST s ())

then showTypeOf should just give Seq, leaving it up to the instance for ST to decide how to implement showTypeOf; this keeps things compositional. The default implementation does precisely this using the metadata that GHC Generics provides.

For convenience, however, some of the deriving via newtype wrappers we provide do depend on Typeable; see below.

showTypeOf :: (Generic a, GShowTypeOf (Rep a)) => Proxy a -> String Source #

Show type a (to add to the context)

We try hard to avoid Typeable constraints in this module: there are types with no Typeable instance but with a NoThunks instance (most important example are types such as ST s which rely on parametric polymorphism). By default we should therefore only show the "outer layer"; for example, if we have a type

Seq (ST s ())

then showTypeOf should just give Seq, leaving it up to the instance for ST to decide how to implement showTypeOf; this keeps things compositional. The default implementation does precisely this using the metadata that GHC Generics provides.

For convenience, however, some of the deriving via newtype wrappers we provide do depend on Typeable; see below.

Instances
NoThunks Bool Source # 
Instance details

Defined in NoThunks.Class

NoThunks Char Source # 
Instance details

Defined in NoThunks.Class

NoThunks Double Source # 
Instance details

Defined in NoThunks.Class

NoThunks Float Source # 
Instance details

Defined in NoThunks.Class

NoThunks Int Source # 
Instance details

Defined in NoThunks.Class

Methods

noThunks :: Context -> Int -> IO (Maybe ThunkInfo) Source #

wNoThunks :: Context -> Int -> IO (Maybe ThunkInfo) Source #

showTypeOf :: Proxy Int -> String Source #

NoThunks Int8 Source # 
Instance details

Defined in NoThunks.Class

NoThunks Int16 Source # 
Instance details

Defined in NoThunks.Class

NoThunks Int32 Source # 
Instance details

Defined in NoThunks.Class

NoThunks Int64 Source # 
Instance details

Defined in NoThunks.Class

NoThunks Integer Source # 
Instance details

Defined in NoThunks.Class

NoThunks Natural Source # 
Instance details

Defined in NoThunks.Class

NoThunks Word Source # 
Instance details

Defined in NoThunks.Class

NoThunks Word8 Source # 
Instance details

Defined in NoThunks.Class

NoThunks Word16 Source # 
Instance details

Defined in NoThunks.Class

NoThunks Word32 Source # 
Instance details

Defined in NoThunks.Class

NoThunks Word64 Source # 
Instance details

Defined in NoThunks.Class

NoThunks CallStack Source #

Since CallStacks can't retain application data, we don't want to check them for thunks at all

Instance details

Defined in NoThunks.Class

NoThunks () Source # 
Instance details

Defined in NoThunks.Class

Methods

noThunks :: Context -> () -> IO (Maybe ThunkInfo) Source #

wNoThunks :: Context -> () -> IO (Maybe ThunkInfo) Source #

showTypeOf :: Proxy () -> String Source #

NoThunks ShortByteString Source #

Instance for short bytestrings

We have

data ShortByteString = SBS ByteArray#

Values of this type consist of a tag followed by an _unboxed_ byte array, which can't contain thunks. Therefore we only check WHNF.

Instance details

Defined in NoThunks.Class

NoThunks ByteString Source #

Instance for lazy bytestrings

Defined manually so that it piggy-backs on the one for strict bytestrings.

Instance details

Defined in NoThunks.Class

NoThunks ByteString Source #

Instance for string bytestrings

Strict bytestrings shouldn't contain any thunks, but could, due to https://gitlab.haskell.org/ghc/ghc/issues/17290. However, such thunks can't retain any data that they shouldn't, and so it's safe to ignore such thunks.

Instance details

Defined in NoThunks.Class

NoThunks Text Source # 
Instance details

Defined in NoThunks.Class

NoThunks Text Source # 
Instance details

Defined in NoThunks.Class

NoThunks ZonedTime Source # 
Instance details

Defined in NoThunks.Class

NoThunks TimeLocale Source # 
Instance details

Defined in NoThunks.Class

NoThunks LocalTime Source # 
Instance details

Defined in NoThunks.Class

NoThunks TimeOfDay Source # 
Instance details

Defined in NoThunks.Class

NoThunks TimeZone Source # 
Instance details

Defined in NoThunks.Class

NoThunks UniversalTime Source # 
Instance details

Defined in NoThunks.Class

NoThunks UTCTime Source # 
Instance details

Defined in NoThunks.Class

NoThunks NominalDiffTime Source # 
Instance details

Defined in NoThunks.Class

NoThunks DiffTime Source # 
Instance details

Defined in NoThunks.Class

NoThunks Day Source # 
Instance details

Defined in NoThunks.Class

Methods

noThunks :: Context -> Day -> IO (Maybe ThunkInfo) Source #

wNoThunks :: Context -> Day -> IO (Maybe ThunkInfo) Source #

showTypeOf :: Proxy Day -> String Source #

NoThunks a => NoThunks [a] Source # 
Instance details

Defined in NoThunks.Class

Methods

noThunks :: Context -> [a] -> IO (Maybe ThunkInfo) Source #

wNoThunks :: Context -> [a] -> IO (Maybe ThunkInfo) Source #

showTypeOf :: Proxy [a] -> String Source #

NoThunks a => NoThunks (Maybe a) Source # 
Instance details

Defined in NoThunks.Class

Methods

noThunks :: Context -> Maybe a -> IO (Maybe ThunkInfo) Source #

wNoThunks :: Context -> Maybe a -> IO (Maybe ThunkInfo) Source #

showTypeOf :: Proxy (Maybe a) -> String Source #

NoThunks a => NoThunks (Ratio a) Source # 
Instance details

Defined in NoThunks.Class

Methods

noThunks :: Context -> Ratio a -> IO (Maybe ThunkInfo) Source #

wNoThunks :: Context -> Ratio a -> IO (Maybe ThunkInfo) Source #

showTypeOf :: Proxy (Ratio a) -> String Source #

NoThunks (IO a) Source #

We do not check IO actions for captured thunks by default

See instance for (a -> b) for detailed discussion.

Instance details

Defined in NoThunks.Class

Methods

noThunks :: Context -> IO a -> IO (Maybe ThunkInfo) Source #

wNoThunks :: Context -> IO a -> IO (Maybe ThunkInfo) Source #

showTypeOf :: Proxy (IO a) -> String Source #

NoThunks a => NoThunks (NonEmpty a) Source # 
Instance details

Defined in NoThunks.Class

NoThunks a => NoThunks (IntMap a) Source # 
Instance details

Defined in NoThunks.Class

Methods

noThunks :: Context -> IntMap a -> IO (Maybe ThunkInfo) Source #

wNoThunks :: Context -> IntMap a -> IO (Maybe ThunkInfo) Source #

showTypeOf :: Proxy (IntMap a) -> String Source #

NoThunks a => NoThunks (Seq a) Source #

Instance for Seq checks elements only

The internal fingertree in Seq might have thunks, which is essential for its asymptotic complexity.

Instance details

Defined in NoThunks.Class

Methods

noThunks :: Context -> Seq a -> IO (Maybe ThunkInfo) Source #

wNoThunks :: Context -> Seq a -> IO (Maybe ThunkInfo) Source #

showTypeOf :: Proxy (Seq a) -> String Source #

NoThunks a => NoThunks (Set a) Source # 
Instance details

Defined in NoThunks.Class

Methods

noThunks :: Context -> Set a -> IO (Maybe ThunkInfo) Source #

wNoThunks :: Context -> Set a -> IO (Maybe ThunkInfo) Source #

showTypeOf :: Proxy (Set a) -> String Source #

NoThunks (Vector a) Source #

Unboxed vectors can't contain thunks

Implementation note: defined manually rather than using OnlyCheckWhnf due to ghc limitation in deriving via, making it impossible to use with it with data families.

Instance details

Defined in NoThunks.Class

Methods

noThunks :: Context -> Vector a -> IO (Maybe ThunkInfo) Source #

wNoThunks :: Context -> Vector a -> IO (Maybe ThunkInfo) Source #

showTypeOf :: Proxy (Vector a) -> String Source #

NoThunks a => NoThunks (Vector a) Source # 
Instance details

Defined in NoThunks.Class

Methods

noThunks :: Context -> Vector a -> IO (Maybe ThunkInfo) Source #

wNoThunks :: Context -> Vector a -> IO (Maybe ThunkInfo) Source #

showTypeOf :: Proxy (Vector a) -> String Source #

Typeable a => NoThunks (InspectHeap a) Source # 
Instance details

Defined in NoThunks.Class

NoThunks (AllowThunk a) Source # 
Instance details

Defined in NoThunks.Class

Typeable a => NoThunks (OnlyCheckWhnf a) Source # 
Instance details

Defined in NoThunks.Class

NoThunks (a -> b) Source #

We do NOT check function closures for captured thunks by default

Since we have no type information about the values captured in a thunk, the only check we could possibly do is containsThunks: we can't recursively call noThunks on those captured values, which is problematic if any of those captured values requires a custom instance (for example, data types that depend on laziness, such as Seq).

By default we therefore only check if the function is in WHNF, and don't check the captured values at all. If you want a stronger check, you can use IsNormalForm (a -> b) instead.

Instance details

Defined in NoThunks.Class

Methods

noThunks :: Context -> (a -> b) -> IO (Maybe ThunkInfo) Source #

wNoThunks :: Context -> (a -> b) -> IO (Maybe ThunkInfo) Source #

showTypeOf :: Proxy (a -> b) -> String Source #

(NoThunks a, NoThunks b) => NoThunks (Either a b) Source # 
Instance details

Defined in NoThunks.Class

Methods

noThunks :: Context -> Either a b -> IO (Maybe ThunkInfo) Source #

wNoThunks :: Context -> Either a b -> IO (Maybe ThunkInfo) Source #

showTypeOf :: Proxy (Either a b) -> String Source #

(NoThunks a, NoThunks b) => NoThunks (a, b) Source # 
Instance details

Defined in NoThunks.Class

Methods

noThunks :: Context -> (a, b) -> IO (Maybe ThunkInfo) Source #

wNoThunks :: Context -> (a, b) -> IO (Maybe ThunkInfo) Source #

showTypeOf :: Proxy (a, b) -> String Source #

(NoThunks k, NoThunks v) => NoThunks (Map k v) Source # 
Instance details

Defined in NoThunks.Class

Methods

noThunks :: Context -> Map k v -> IO (Maybe ThunkInfo) Source #

wNoThunks :: Context -> Map k v -> IO (Maybe ThunkInfo) Source #

showTypeOf :: Proxy (Map k v) -> String Source #

KnownSymbol name => NoThunks (InspectHeapNamed name a) Source # 
Instance details

Defined in NoThunks.Class

(HasFields s a, Generic a, Typeable a, GWNoThunks s (Rep a)) => NoThunks (AllowThunksIn s a) Source # 
Instance details

Defined in NoThunks.Class

KnownSymbol name => NoThunks (OnlyCheckWhnfNamed name a) Source # 
Instance details

Defined in NoThunks.Class

(NoThunks a, NoThunks b, NoThunks c) => NoThunks (a, b, c) Source # 
Instance details

Defined in NoThunks.Class

Methods

noThunks :: Context -> (a, b, c) -> IO (Maybe ThunkInfo) Source #

wNoThunks :: Context -> (a, b, c) -> IO (Maybe ThunkInfo) Source #

showTypeOf :: Proxy (a, b, c) -> String Source #

(NoThunks a, NoThunks b, NoThunks c, NoThunks d) => NoThunks (a, b, c, d) Source # 
Instance details

Defined in NoThunks.Class

Methods

noThunks :: Context -> (a, b, c, d) -> IO (Maybe ThunkInfo) Source #

wNoThunks :: Context -> (a, b, c, d) -> IO (Maybe ThunkInfo) Source #

showTypeOf :: Proxy (a, b, c, d) -> String Source #

(NoThunks a, NoThunks b, NoThunks c, NoThunks d, NoThunks e) => NoThunks (a, b, c, d, e) Source # 
Instance details

Defined in NoThunks.Class

Methods

noThunks :: Context -> (a, b, c, d, e) -> IO (Maybe ThunkInfo) Source #

wNoThunks :: Context -> (a, b, c, d, e) -> IO (Maybe ThunkInfo) Source #

showTypeOf :: Proxy (a, b, c, d, e) -> String Source #

(NoThunks a, NoThunks b, NoThunks c, NoThunks d, NoThunks e, NoThunks f) => NoThunks (a, b, c, d, e, f) Source # 
Instance details

Defined in NoThunks.Class

Methods

noThunks :: Context -> (a, b, c, d, e, f) -> IO (Maybe ThunkInfo) Source #

wNoThunks :: Context -> (a, b, c, d, e, f) -> IO (Maybe ThunkInfo) Source #

showTypeOf :: Proxy (a, b, c, d, e, f) -> String Source #

(NoThunks a, NoThunks b, NoThunks c, NoThunks d, NoThunks e, NoThunks f, NoThunks g) => NoThunks (a, b, c, d, e, f, g) Source # 
Instance details

Defined in NoThunks.Class

Methods

noThunks :: Context -> (a, b, c, d, e, f, g) -> IO (Maybe ThunkInfo) Source #

wNoThunks :: Context -> (a, b, c, d, e, f, g) -> IO (Maybe ThunkInfo) Source #

showTypeOf :: Proxy (a, b, c, d, e, f, g) -> String Source #

data ThunkInfo Source #

Information about unexpected thunks

TODO: The ghc-debug work by Matthew Pickering includes some work that allows to get source spans from closures. If we could take advantage of that, we could not only show the type of the unexpected thunk, but also where it got allocated.

Constructors

ThunkInfo 

Fields

Instances
Show ThunkInfo Source # 
Instance details

Defined in NoThunks.Class

Helpders for defining instances

allNoThunks :: [IO (Maybe ThunkInfo)] -> IO (Maybe ThunkInfo) Source #

Short-circuit a list of checks

noThunksInValues :: NoThunks a => Context -> [a] -> IO (Maybe ThunkInfo) Source #

Check that all elements in the list are thunk-free

Does not check the list itself. Useful for checking the elements of a container.

See also noThunksInKeysAndValues

noThunksInKeysAndValues :: (NoThunks k, NoThunks v) => Context -> [(k, v)] -> IO (Maybe ThunkInfo) Source #

Variant on noThunksInValues for keyed containers.

Neither the list nor the tuples are checked for thunks.

Deriving-via wrappers

newtype OnlyCheckWhnf a Source #

Newtype wrapper for use with deriving via to check for WHNF only

For some types we don't want to check for nested thunks, and we only want check if the argument is in WHNF, not in NF. A typical example are functions; see the instance of (a -> b) for detailed discussion. This should be used sparingly.

Example:

deriving via OnlyCheckWhnf T instance NoThunks T

Constructors

OnlyCheckWhnf a 
Instances
Typeable a => NoThunks (OnlyCheckWhnf a) Source # 
Instance details

Defined in NoThunks.Class

newtype OnlyCheckWhnfNamed (name :: Symbol) a Source #

Variant on OnlyCheckWhnf that does not depend on Generic

Example:

deriving via OnlyCheckWhnfNamed "T" T instance NoThunks T

Constructors

OnlyCheckWhnfNamed a 
Instances
KnownSymbol name => NoThunks (OnlyCheckWhnfNamed name a) Source # 
Instance details

Defined in NoThunks.Class

newtype InspectHeap a Source #

Newtype wrapper for use with deriving via to inspect the heap directly

This bypasses the class instances altogether, and inspects the GHC heap directly, checking that the value does not contain any thunks _anywhere_. Since we can do this without any type classes instances, this is useful for types that contain fields for which NoThunks instances are not available.

Since the primary use case for InspectHeap then is to give instances for NoThunks from third party libraries, we also don't want to rely on a Generic instance, which may likewise not be available. Instead, we will rely on Typeable, which is available for all types. However, as showTypeOf explains, requiring Typeable may not always be suitable; if it isn't, InspectHeapNamed can be used.

Example:

deriving via InspectHeap T instance NoThunks T

Constructors

InspectHeap a 
Instances
Typeable a => NoThunks (InspectHeap a) Source # 
Instance details

Defined in NoThunks.Class

newtype InspectHeapNamed (name :: Symbol) a Source #

Variant on InspectHeap that does not depend on Typeable.

deriving via InspectHeapNamed "T" T instance NoUnexpecedThunks T

Constructors

InspectHeapNamed a 
Instances
KnownSymbol name => NoThunks (InspectHeapNamed name a) Source # 
Instance details

Defined in NoThunks.Class

newtype AllowThunk a Source #

Newtype wrapper for values that should be allowed to be a thunk

This should be used VERY sparingly, and should ONLY be used on values (or, even rarer, types) which you are SURE cannot retain any data that they shouldn't. Bear in mind allowing a value of type T to be a thunk might cause a value of type S to be retained if T was computed from S.

Constructors

AllowThunk a 
Instances
NoThunks (AllowThunk a) Source # 
Instance details

Defined in NoThunks.Class

newtype AllowThunksIn (fields :: [Symbol]) a Source #

Newtype wrapper for records where some of the fields are allowed to be thunks.

Example:

deriving via AllowThunksIn '["foo","bar"] T instance NoThunks T

This will create an instance that skips the thunk checks for the "foo" and "bar" fields.

Constructors

AllowThunksIn a 
Instances
(HasFields s a, Generic a, Typeable a, GWNoThunks s (Rep a)) => NoThunks (AllowThunksIn s a) Source # 
Instance details

Defined in NoThunks.Class