primal-0.3.0.0: src/Control/Prim/Eval.hs
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE UnboxedTuples #-}
-- |
-- Module : Control.Prim.Eval
-- Copyright : (c) Alexey Kuleshevich 2020
-- License : BSD3
-- Maintainer : Alexey Kuleshevich <alexey@kuleshevi.ch>
-- Stability : experimental
-- Portability : non-portable
--
module Control.Prim.Eval
( -- * Liveness
touch
, touch#
, keepAlive
, keepAlive#
-- * Weak-Head Normal Form
, seq
, eval
, evalM
-- * Normal Form
, deepeval
, deepevalM
, module Control.DeepSeq
, BNF(..)
) where
import Control.DeepSeq
import Control.Prim.Monad.Internal
import qualified GHC.Exts as GHC
-- | Same as `GHC.Exts.touch#`, except it is not restricted to `RealWorld` state token.
touch# :: a -> GHC.State# s -> GHC.State# s
touch# a = GHC.unsafeCoerce# (GHC.touch# a)
{-# INLINE touch# #-}
------- Evaluation
-- | This is an action that ensures that the value is still available and garbage
-- collector has not cleaned it up.
--
-- Make sure not to use it after some computation that doesn't return, like after
-- `forever` for example, otherwise touch will simply be removed by ghc and bad things
-- will happen. If you have a case like that, make sure to use `withAlivePrimBase` or
-- `keepAlive` instead.
--
-- @since 0.1.0
touch :: MonadPrim s m => a -> m ()
touch x = prim_ (touch# x)
{-# INLINE touch #-}
-- | Forward compatible operator that might be introduced in some future ghc version.
--
-- See: [#17760](https://gitlab.haskell.org/ghc/ghc/-/issues/17760)
--
-- Current version is not as efficient as the version that will be introduced in the
-- future, because it works around the ghc bug by simply preventing inlining and relying
-- on the `touch` function.
--
-- @since 0.1.0
keepAlive# ::
a
-- ^ The value to preserve
-> (GHC.State# s -> (# GHC.State# s, r #))
-- ^ The continuation in which the value will be preserved
-> GHC.State# s
-> (# GHC.State# s, r #)
keepAlive# a m s =
case m s of
(# s', r #) -> (# touch# a s', r #)
{-# NOINLINE keepAlive# #-}
-- | Similar to `touch`. See `withAlive#` for more info.
--
-- @since 0.3.0
keepAlive ::
MonadUnliftPrim s m
=> a
-- ^ The value to preserve
-> m b
-- ^ Action to run in which the value will be preserved
-> m b
keepAlive a m = runInPrimBase m (keepAlive# a)
{-# INLINE keepAlive #-}
-- | An action that evaluates a value to Weak Head Normal Form (WHNF). Same as
-- `Control.Exception.evaluate`, except it works in `MonadPrim`. This function provides
-- stronger guarantees than `seq` with respect to ordering of operations, but it does have a
-- slightly higher overhead.
--
-- @since 0.3.0
eval :: MonadPrim s m => a -> m a
eval a = prim (GHC.seq# a)
{-# INLINE eval #-}
-- | Run the action and then use `eval` to ensure its result is evaluated to Weak Head
-- Normal Form (WHNF)
--
-- @since 0.3.0
evalM :: MonadPrim s m => m a -> m a
evalM m = eval =<< m
{-# INLINE evalM #-}
-- Normal Form
-- | An action that evaluates a value to Normal Form (NF). This function provides stronger
-- guarantees than `deepseq` with respect to ordering of operations.
--
-- @since 0.3.0
deepeval :: (MonadPrim s m, NFData a) => a -> m a
deepeval = eval . force
{-# INLINE deepeval #-}
-- | Run the action and the using `deepeval` ensure its result is evaluated to Normal Form
-- (NF)
--
-- @since 0.3.0
deepevalM :: (MonadPrim s m, NFData a) => m a -> m a
deepevalM m = eval . force =<< m
{-# INLINE deepevalM #-}
-- | Bogus Normal Form. This is useful in places where `NFData` constraint is required,
-- but an instance can't really be created in any meaningful way for the type at
-- hand. Creating environment in benchmarks is one such place where it may come in handy.
--
-- @since 0.3.0
newtype BNF a = BNF a
-- | Unlawful instance that only evaluates its contents to WHNF
--
-- @since 0.3.0
instance NFData (BNF a) where
rnf (BNF a) = a `seq` ()