random-fu-0.1.0.0: src/Data/Random/Source.hs
{-
- ``Data/Random/Source''
-}
{-# LANGUAGE
MultiParamTypeClasses, FlexibleInstances, GADTs
#-}
module Data.Random.Source
( MonadRandom(..)
, RandomSource(..)
, Prim(..)
) where
import Data.Word
import Control.Monad.Prompt
import Data.Tagged
import Data.Random.Internal.Primitives
-- |A typeclass for monads with a chosen source of entropy. For example,
-- 'RVar' is such a monad - the source from which it is (eventually) sampled
-- is the only source from which a random variable is permitted to draw, so
-- when directly requesting entropy for a random variable these functions
-- are used.
--
-- The minimal definition is 'supportedPrims' and 'getSupportedRandomPrim'
-- with cases for those primitives where 'supportedPrims' returns 'True'.
--
-- It is recommended (despite the warnings it generates) that, even when
-- all primitives are supported, a final wildcard case of 'supportedPrims' is
-- specified, as:
--
-- > supportedPrims _ _ = False
--
-- The overlapping pattern warnings can be suppressed (without suppressing
-- other, genuine, overlapping-pattern warnings) by the GHC flag
-- @-fno-warn-simple-patterns@. This is not actually the documented behavior
-- of that flag as far as I can find in 3 google-minutes, but it works with
-- GHC 6.12.1 anyway, and that's good enough for me.
--
-- Note that it is very important that at least 'supportedPrims' (and preferably
-- 'getSupportedRandomPrim' as well) gets inlined into the default implementation
-- of 'getRandomPrim'. If your 'supportedPrims' is more than about 2 or 3
-- cases, add an INLINE pragma so that it can be optimized out of 'getRandomPrim'.
class Monad m => MonadRandom m where
-- |Predicate indicating whether a given primitive is supported by the
-- instance. The first parameter is a phantom used to select the instance.
supportedPrims :: m () -> Prim t -> Bool
-- |Generate a random value corresponding to the specified primitive. Will
-- not be called unless supportedPrims returns true for that primitive.
getSupportedRandomPrim :: Prim t -> m t
-- This could just be a function, but placing it in a dictionary gives
-- GHC a place to optimize it separately for each instance, which is
-- kinda the whole point of the 'Prim' machinery:
--
-- |Generate a random value corresponding to the specified primitive. The
-- default implementation makes use of 'supportedPrims' and 'getSupportedRandomPrim'
-- to construct any required Prim out of the supported ones.
{-# NOINLINE getRandomPrim #-}
getRandomPrim :: Prim t -> m t
getRandomPrim prim = val
where
val = runPromptM getSupportedRandomPrim (decomposePrimWhere (supportedPrims mPhantom) prim)
mPhantom = error "supportedPrims tried to evaluate a phantom parameter" `asTypeOf` (val >> return ())
-- |A source of entropy which can be used in the given monad.
--
-- The minimal definition is 'supportedPrimsFrom' and 'getSupportedRandomPrimFrom'
-- with cases for those primitives where 'supportedPrimsFrom' returns 'True'.
--
-- Note that it is very important that at least 'supportedPrimsFrom' (and preferably
-- 'getSupportedRandomPrimFrom' as well) gets inlined into the default implementation
-- of 'getRandomPrimFrom'. If your 'supportedPrimsFrom' is more than about 2 or 3
-- cases, add an INLINE pragma so that it can be optimized out of 'getRandomPrimFrom'.
--
-- See also 'MonadRandom'.
class Monad m => RandomSource m s where
-- |Predicate indicating whether a given primitive is supported by the
-- instance. The tag on the first parameter is a phantom used only to
-- select the instance, but the value itself may be inspected.
supportedPrimsFrom :: Tagged (m ()) s -> Prim t -> Bool
-- |Generate a random value corresponding to the specified primitive
getSupportedRandomPrimFrom :: s -> Prim t -> m t
-- This could just be a function, but placing it in a dictionary gives
-- GHC a place to optimize it separately for each instance, which is
-- kinda the whole point of the 'Prim' machinery:
--
-- |Generate a random value corresponding to the specified primitive. The
-- default implementation makes use of 'supportedPrimsFrom' and
-- 'getSupportedRandomPrimFrom' to construct any required Prim out of
-- the supported ones.
{-# NOINLINE getRandomPrimFrom #-}
getRandomPrimFrom :: s -> Prim t -> m t
getRandomPrimFrom src prim = val
where
val = runPromptM (getSupportedRandomPrimFrom src) (decomposePrimWhere supported prim)
supported :: Prim t -> Bool
supported = supportedPrimsFrom (tagIt (val >> return ()) src)
tagIt :: a -> b -> Tagged a b
tagIt _ it = Tagged it
instance Monad m => RandomSource m (m Word8) where
supportedPrimsFrom _ PrimWord8 = True
supportedPrimsFrom _ _ = False
getSupportedRandomPrimFrom f PrimWord8 = f
getSupportedRandomPrimFrom _ p = error ("getSupportedRandomPrimFrom/RandomSource m (m Word8): unsupported prim requested: " ++ show p)
instance Monad m => RandomSource m (m Word16) where
supportedPrimsFrom _ PrimWord16 = True
supportedPrimsFrom _ _ = False
getSupportedRandomPrimFrom f PrimWord16 = f
getSupportedRandomPrimFrom _ p = error ("getSupportedRandomPrimFrom/RandomSource m (m Word16): unsupported prim requested: " ++ show p)
instance Monad m => RandomSource m (m Word32) where
supportedPrimsFrom _ PrimWord32 = True
supportedPrimsFrom _ _ = False
getSupportedRandomPrimFrom f PrimWord32 = f
getSupportedRandomPrimFrom _ p = error ("getSupportedRandomPrimFrom/RandomSource m (m Word32): unsupported prim requested: " ++ show p)
instance Monad m => RandomSource m (m Word64) where
supportedPrimsFrom _ PrimWord64 = True
supportedPrimsFrom _ _ = False
getSupportedRandomPrimFrom f PrimWord64 = f
getSupportedRandomPrimFrom _ p = error ("getSupportedRandomPrimFrom/RandomSource m (m Word64): unsupported prim requested: " ++ show p)
instance Monad m => RandomSource m (m Double) where
supportedPrimsFrom _ PrimDouble = True
supportedPrimsFrom _ _ = False
getSupportedRandomPrimFrom f PrimDouble = f
getSupportedRandomPrimFrom _ p = error ("getSupportedRandomPrimFrom/RandomSource m (m Double): unsupported prim requested: " ++ show p)