#if MIN_VERSION_base(4,9,0)
#endif
module Data.Parameterized.Nonce
(
NonceGenerator
, freshNonce
, Nonce
, indexValue
, newSTNonceGenerator
, newIONonceGenerator
, withIONonceGenerator
, withSTNonceGenerator
, withGlobalSTNonceGenerator
, GlobalNonceGenerator
, globalNonceGenerator
) where
import Control.Monad.ST
import Data.Hashable
import Data.IORef
import Data.STRef
import Data.Typeable
import Data.Word
import Unsafe.Coerce
import System.IO.Unsafe (unsafePerformIO)
import Data.Parameterized.Classes
import Data.Parameterized.Some
#if MIN_VERSION_base(4,9,0)
import Data.Kind
#endif
data NonceGenerator (m :: * -> *) (s :: *) = NonceGenerator {
#if MIN_VERSION_base(4,9,0)
freshNonce :: forall k (tp :: k) . m (Nonce s tp)
#else
freshNonce :: forall (tp :: k) . m (Nonce s tp)
#endif
}
withGlobalSTNonceGenerator :: (forall t . NonceGenerator (ST t) t -> ST t r) -> r
withGlobalSTNonceGenerator f = runST $ do
r <- newSTRef (toEnum 0)
f $! NonceGenerator {
freshNonce = do
i <- readSTRef r
writeSTRef r $! succ i
return $! Nonce i
}
newSTNonceGenerator :: ST t (Some (NonceGenerator (ST t)))
newSTNonceGenerator = g <$> newSTRef (toEnum 0)
where g r = Some $!
NonceGenerator {
freshNonce = do
i <- readSTRef r
writeSTRef r $! succ i
return $! Nonce i
}
newIONonceGenerator :: IO (Some (NonceGenerator IO))
newIONonceGenerator = g <$> newIORef (toEnum 0)
where g r = Some $!
NonceGenerator {
freshNonce = do
i <- readIORef r
writeIORef r $! succ i
return $! Nonce i
}
withSTNonceGenerator :: (forall s . NonceGenerator (ST t) s -> (ST t) r) -> ST t r
withSTNonceGenerator f = do
Some r <- newSTNonceGenerator
f r
withIONonceGenerator :: (forall s . NonceGenerator IO s -> IO r) -> IO r
withIONonceGenerator f = do
Some r <- newIONonceGenerator
f r
newtype Nonce (s :: *) (tp :: k) = Nonce { indexValue :: Word64 }
deriving (Eq, Ord, Hashable, Show)
type role Nonce nominal nominal
instance TestEquality (Nonce s) where
testEquality x y | indexValue x == indexValue y = unsafeCoerce (Just Refl)
| otherwise = Nothing
instance OrdF (Nonce s) where
compareF x y =
case compare (indexValue x) (indexValue y) of
LT -> LTF
EQ -> unsafeCoerce EQF
GT -> GTF
instance HashableF (Nonce s) where
hashWithSaltF s (Nonce x) = hashWithSalt s x
instance ShowF (Nonce s)
data GlobalNonceGenerator
globalNonceIORef :: IORef Word64
globalNonceIORef = unsafePerformIO (newIORef 0)
globalNonceGenerator :: NonceGenerator IO GlobalNonceGenerator
globalNonceGenerator =
NonceGenerator
{ freshNonce = Nonce <$> atomicModifyIORef' globalNonceIORef (\n -> (n+1, n))
}