-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | Examine values for unexpected thunks -- -- Long lived application data typically should not contain any thunks. -- This library can be used to examine values for unexpected thunks, -- which can then be used in assertions. This can be invaluable in -- avoiding memory leaks, or tracking down existing ones. @package nothunks @version 0.2.1.0 module NoThunks.Class -- | Check a value for unexpected thunks class NoThunks a -- | Check if the argument does not contain any unexpected thunks -- -- For most datatypes, we should have that -- --
--   noThunks ctxt x == Nothing
--   
-- -- if and only if -- --
--   checkContainsThunks 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 or whereFrom if available), and calls -- wNoThunks. See ThunkInfo for a detailed discussion of -- the type context. -- -- See also discussion of caveats listed for checkContainsThunks. noThunks :: NoThunks a => Context -> a -> IO (Maybe ThunkInfo) -- | 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 :: NoThunks a => Context -> a -> IO (Maybe ThunkInfo) -- | 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 :: (NoThunks a, Generic a, GWNoThunks '[] (Rep a)) => Context -> a -> IO (Maybe ThunkInfo) -- | 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 :: NoThunks a => Proxy a -> String -- | 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 :: (NoThunks a, Generic a, GShowTypeOf (Rep a)) => Proxy a -> String -- | Information about unexpected thunks -- -- ThunkInfo contains either precise Info about the thunk location -- or Context to make it easier to debug space leaks. Info -- is available if -- -- -- -- The Context argument is intended to give a clue to add -- debugging. For example, suppose we have something of type (Int, -- [Int]). The various contexts we might get are -- --
--   Context                  The thunk is..
--   ---------------------------------------------------------------------
--   ["(,)"]                  the pair itself
--   ["Int","(,)"]            the Int in the pair
--   ["List","(,)"]           the [Int] in the pair
--   ["Int","List","(,)"]     an Int in the [Int] in the pair
--   
-- -- Note: prior to `ghc-9.6` a list was indicated by `[]`. newtype ThunkInfo ThunkInfo :: Either Context Info -> ThunkInfo [thunkInfo] :: ThunkInfo -> Either Context Info -- | Context where a thunk was found -- -- This is intended to give a hint about which thunk was found. For -- example, a thunk might be reported with context -- --
--   ["Int", "(,)", "Map", "AppState"]
--   
-- -- telling you that you have an AppState containing a -- Map containing a pair, all of which weren't thunks (were in -- WHNF), but that pair contained an Int which was a thunk. type Context = [String] -- | Binding name, type and location information about the thunk, e.g. -- --
--   fromModel :: Int @ test/Test/NoThunks/Class.hs:198:53-84
--   
type Info = String -- | Call noThunks in a pure context (relies on -- unsafePerformIO). unsafeNoThunks :: NoThunks a => a -> Maybe ThunkInfo -- | Short-circuit a list of checks allNoThunks :: [IO (Maybe ThunkInfo)] -> IO (Maybe ThunkInfo) -- | 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 noThunksInValues :: NoThunks a => Context -> [a] -> IO (Maybe ThunkInfo) -- | Variant on noThunksInValues for keyed containers. -- -- Neither the list nor the tuples are checked for thunks. noThunksInKeysAndValues :: (NoThunks k, NoThunks v) => Context -> [(k, v)] -> IO (Maybe ThunkInfo) -- | 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
--   
newtype OnlyCheckWhnf a OnlyCheckWhnf :: a -> OnlyCheckWhnf a -- | Variant on OnlyCheckWhnf that does not depend on Generic -- -- Example: -- --
--   deriving via OnlyCheckWhnfNamed "T" T instance NoThunks T
--   
newtype OnlyCheckWhnfNamed (name :: Symbol) a OnlyCheckWhnfNamed :: a -> OnlyCheckWhnfNamed (name :: Symbol) a -- | 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
--   
newtype InspectHeap a InspectHeap :: a -> InspectHeap a -- | Variant on InspectHeap that does not depend on Typeable. -- --
--   deriving via InspectHeapNamed "T" T instance NoUnexpecedThunks T
--   
newtype InspectHeapNamed (name :: Symbol) a InspectHeapNamed :: a -> InspectHeapNamed (name :: Symbol) a -- | 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. newtype AllowThunk a AllowThunk :: a -> AllowThunk a -- | 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. newtype AllowThunksIn (fields :: [Symbol]) a AllowThunksIn :: a -> AllowThunksIn (fields :: [Symbol]) a -- | Generic infrastructure for checking for unexpected thunks -- -- The a argument records which record fields are allowed to -- contain thunks; see AllowThunksIn and GWRecordField, -- below. class GWNoThunks (a :: [Symbol]) f -- | Check that the argument does not contain any unexpected thunks -- -- Precondition: the argument is in WHNF. gwNoThunks :: GWNoThunks a f => proxy a -> Context -> f x -> IO (Maybe ThunkInfo) instance GHC.Show.Show NoThunks.Class.ThunkInfo instance NoThunks.Class.NoThunks GHC.Types.Bool instance NoThunks.Class.NoThunks GHC.Num.Natural.Natural instance NoThunks.Class.NoThunks GHC.Num.Integer.Integer instance NoThunks.Class.NoThunks GHC.Types.Float instance NoThunks.Class.NoThunks GHC.Types.Double instance NoThunks.Class.NoThunks GHC.Types.Char instance NoThunks.Class.NoThunks GHC.Types.Int instance NoThunks.Class.NoThunks GHC.Int.Int8 instance NoThunks.Class.NoThunks GHC.Int.Int16 instance NoThunks.Class.NoThunks GHC.Int.Int32 instance NoThunks.Class.NoThunks GHC.Int.Int64 instance NoThunks.Class.NoThunks GHC.Types.Word instance NoThunks.Class.NoThunks GHC.Word.Word8 instance NoThunks.Class.NoThunks GHC.Word.Word16 instance NoThunks.Class.NoThunks GHC.Word.Word32 instance NoThunks.Class.NoThunks GHC.Word.Word64 instance NoThunks.Class.NoThunks a => NoThunks.Class.NoThunks (Data.Semigroup.Min a) instance NoThunks.Class.NoThunks a => NoThunks.Class.NoThunks (Data.Semigroup.Max a) instance NoThunks.Class.NoThunks a => NoThunks.Class.NoThunks (Data.Semigroup.First a) instance NoThunks.Class.NoThunks a => NoThunks.Class.NoThunks (Data.Semigroup.Last a) instance NoThunks.Class.NoThunks a => NoThunks.Class.NoThunks (Data.Semigroup.Internal.Dual a) instance NoThunks.Class.NoThunks Data.Semigroup.Internal.All instance NoThunks.Class.NoThunks Data.Semigroup.Internal.Any instance NoThunks.Class.NoThunks a => NoThunks.Class.NoThunks (Data.Semigroup.Internal.Sum a) instance NoThunks.Class.NoThunks a => NoThunks.Class.NoThunks (Data.Semigroup.Internal.Product a) instance NoThunks.Class.NoThunks a => NoThunks.Class.NoThunks (Data.Semigroup.WrappedMonoid a) instance NoThunks.Class.NoThunks a => NoThunks.Class.NoThunks (Data.Monoid.First a) instance NoThunks.Class.NoThunks a => NoThunks.Class.NoThunks (Data.Monoid.Last a) instance NoThunks.Class.NoThunks (f a) => NoThunks.Class.NoThunks (Data.Semigroup.Internal.Alt f a) instance NoThunks.Class.NoThunks (f a) => NoThunks.Class.NoThunks (Data.Monoid.Ap f a) instance NoThunks.Class.NoThunks Data.Time.Calendar.Days.Day instance NoThunks.Class.NoThunks Data.Time.Clock.Internal.DiffTime.DiffTime instance NoThunks.Class.NoThunks Data.Time.LocalTime.Internal.LocalTime.LocalTime instance NoThunks.Class.NoThunks Data.Time.Clock.Internal.NominalDiffTime.NominalDiffTime instance NoThunks.Class.NoThunks Data.Time.Format.Locale.TimeLocale instance NoThunks.Class.NoThunks Data.Time.LocalTime.Internal.TimeOfDay.TimeOfDay instance NoThunks.Class.NoThunks Data.Time.LocalTime.Internal.TimeZone.TimeZone instance NoThunks.Class.NoThunks Data.Time.Clock.Internal.UniversalTime.UniversalTime instance NoThunks.Class.NoThunks Data.Time.Clock.Internal.UTCTime.UTCTime instance NoThunks.Class.NoThunks Data.Time.LocalTime.Internal.ZonedTime.ZonedTime instance NoThunks.Class.NoThunks Data.ByteString.Internal.Type.ByteString instance NoThunks.Class.NoThunks Data.ByteString.Short.Internal.ShortByteString instance NoThunks.Class.NoThunks Data.Text.Internal.Text instance NoThunks.Class.NoThunks GHC.Conc.Sync.ThreadId instance NoThunks.Class.NoThunks (a -> b) instance NoThunks.Class.NoThunks (GHC.Types.IO a) instance NoThunks.Class.NoThunks GHC.Stack.Types.CallStack instance (NoThunks.Class.HasFields s a, GHC.Generics.Generic a, Data.Typeable.Internal.Typeable a, NoThunks.Class.GWNoThunks s (GHC.Generics.Rep a)) => NoThunks.Class.NoThunks (NoThunks.Class.AllowThunksIn s a) instance NoThunks.Class.HasFields '[] a instance (GHC.Records.HasField x a t, NoThunks.Class.HasFields xs a) => NoThunks.Class.HasFields (x : xs) a instance (NoThunks.Class.GWRecordField f (NoThunks.Class.Elem fieldName a), GHC.TypeLits.KnownSymbol fieldName) => NoThunks.Class.GWNoThunks a (GHC.Generics.S1 ('GHC.Generics.MetaSel ('GHC.Maybe.Just fieldName) su ss ds) f) instance Data.Typeable.Internal.Typeable a => NoThunks.Class.NoThunks (NoThunks.Class.OnlyCheckWhnf a) instance GHC.TypeLits.KnownSymbol name => NoThunks.Class.NoThunks (NoThunks.Class.OnlyCheckWhnfNamed name a) instance NoThunks.Class.NoThunks (NoThunks.Class.AllowThunk a) instance Data.Typeable.Internal.Typeable a => NoThunks.Class.NoThunks (NoThunks.Class.InspectHeap a) instance GHC.TypeLits.KnownSymbol name => NoThunks.Class.NoThunks (NoThunks.Class.InspectHeapNamed name a) instance NoThunks.Class.NoThunks c => NoThunks.Class.GWNoThunks a (GHC.Generics.K1 i c) instance (NoThunks.Class.NoThunks a, NoThunks.Class.NoThunks b) => NoThunks.Class.NoThunks (Data.Semigroup.Arg a b) instance NoThunks.Class.NoThunks a => NoThunks.Class.NoThunks (Solo a) instance NoThunks.Class.NoThunks a => NoThunks.Class.NoThunks (GHC.IORef.IORef a) instance NoThunks.Class.NoThunks a => NoThunks.Class.NoThunks (GHC.MVar.MVar a) instance NoThunks.Class.NoThunks a => NoThunks.Class.NoThunks (GHC.Conc.Sync.TVar a) instance NoThunks.Class.NoThunks Data.ByteString.Lazy.Internal.ByteString instance NoThunks.Class.NoThunks Data.Text.Internal.Lazy.Text instance (NoThunks.Class.NoThunks a, NoThunks.Class.NoThunks b) => NoThunks.Class.NoThunks (a, b) instance (NoThunks.Class.NoThunks a, NoThunks.Class.NoThunks b, NoThunks.Class.NoThunks c) => NoThunks.Class.NoThunks (a, b, c) instance (NoThunks.Class.NoThunks a, NoThunks.Class.NoThunks b, NoThunks.Class.NoThunks c, NoThunks.Class.NoThunks d) => NoThunks.Class.NoThunks (a, b, c, d) instance (NoThunks.Class.NoThunks a, NoThunks.Class.NoThunks b, NoThunks.Class.NoThunks c, NoThunks.Class.NoThunks d, NoThunks.Class.NoThunks e) => NoThunks.Class.NoThunks (a, b, c, d, e) instance (NoThunks.Class.NoThunks a, NoThunks.Class.NoThunks b, NoThunks.Class.NoThunks c, NoThunks.Class.NoThunks d, NoThunks.Class.NoThunks e, NoThunks.Class.NoThunks f) => NoThunks.Class.NoThunks (a, b, c, d, e, f) instance (NoThunks.Class.NoThunks a, NoThunks.Class.NoThunks b, NoThunks.Class.NoThunks c, NoThunks.Class.NoThunks d, NoThunks.Class.NoThunks e, NoThunks.Class.NoThunks f, NoThunks.Class.NoThunks g) => NoThunks.Class.NoThunks (a, b, c, d, e, f, g) instance NoThunks.Class.NoThunks GHC.Base.Void instance NoThunks.Class.NoThunks () instance NoThunks.Class.NoThunks a => NoThunks.Class.NoThunks [a] instance NoThunks.Class.NoThunks a => NoThunks.Class.NoThunks (Data.Functor.Identity.Identity a) instance NoThunks.Class.NoThunks a => NoThunks.Class.NoThunks (GHC.Maybe.Maybe a) instance NoThunks.Class.NoThunks a => NoThunks.Class.NoThunks (GHC.Base.NonEmpty a) instance (NoThunks.Class.NoThunks a, NoThunks.Class.NoThunks b) => NoThunks.Class.NoThunks (Data.Either.Either a b) instance (NoThunks.Class.NoThunks k, NoThunks.Class.NoThunks v) => NoThunks.Class.NoThunks (Data.Map.Internal.Map k v) instance NoThunks.Class.NoThunks a => NoThunks.Class.NoThunks (Data.Set.Internal.Set a) instance NoThunks.Class.NoThunks a => NoThunks.Class.NoThunks (Data.IntMap.Internal.IntMap a) instance NoThunks.Class.NoThunks a => NoThunks.Class.NoThunks (Data.Vector.Vector a) instance NoThunks.Class.NoThunks (Data.Vector.Unboxed.Base.Vector a) instance NoThunks.Class.NoThunks a => NoThunks.Class.NoThunks (Data.Sequence.Internal.Seq a) instance NoThunks.Class.NoThunks a => NoThunks.Class.NoThunks (GHC.Real.Ratio a) instance GHC.Generics.Datatype c => NoThunks.Class.GShowTypeOf (GHC.Generics.D1 c f) instance NoThunks.Class.GWRecordField f 'GHC.Types.True instance NoThunks.Class.GWNoThunks '[] f => NoThunks.Class.GWRecordField f 'GHC.Types.False instance NoThunks.Class.GWNoThunks a f => NoThunks.Class.GWNoThunks a (GHC.Generics.D1 c f) instance NoThunks.Class.GWNoThunks a f => NoThunks.Class.GWNoThunks a (GHC.Generics.C1 c f) instance NoThunks.Class.GWNoThunks a f => NoThunks.Class.GWNoThunks a (GHC.Generics.S1 ('GHC.Generics.MetaSel 'GHC.Maybe.Nothing su ss ds) f) instance (NoThunks.Class.GWNoThunks a f, NoThunks.Class.GWNoThunks a g) => NoThunks.Class.GWNoThunks a (f GHC.Generics.:*: g) instance (NoThunks.Class.GWNoThunks a f, NoThunks.Class.GWNoThunks a g) => NoThunks.Class.GWNoThunks a (f GHC.Generics.:+: g) instance NoThunks.Class.GWNoThunks a GHC.Generics.U1 instance NoThunks.Class.GWNoThunks a GHC.Generics.V1