{- |
  Monadic functions for random number generation.

  Because manually threading the correct 'Seed' value around is
  tedious and error-prone, one common approach is to use some
  kind of state monad to hide it. This module provides the
  convenience functions to make this easy; just write a
  'RandomM' instance for your particular monad, and then you
  can easily and conveniently generate random numbers.
-}

module Random.MWC.Monadic
    (
      -- * Random seed
      Seed (),

      -- * Random monads
      RandomM (..),

      -- * Monadic operations
      bounded_randomM, unit_randomM, range_randomM,
    )
  where

import Random.MWC.Pure

{- |
  The class of monads holding a single random 'Seed' within their
  state.
-}
class Monad m => RandomM m where
  -- | Fetch the current 'Seed' value.
  get_random_seed :: m Seed

  -- | Replace the current 'Seed' value.
  set_random_seed :: Seed -> m ()

{- |
  The monadic analogue of 'bounded_random'.

  Return a value randomly chosen between 'minBound' and 'maxBound'.
  Uses the current 'Seed' value from within the monad, automatically
  updating said seed value in the process. Thus, repeatedly calling
  this function will yield different successive values.
-}
bounded_randomM :: (RandomM m, BoundedRandom x) => m x
bounded_randomM = do
  s0 <- get_random_seed
  let (x, s1) = bounded_random s0
  set_random_seed s1
  return x

{- |
  The monadic analogue of 'unit_random'.

  Returns a value randomly chosen between \"zero\" and \"one\". Uses
  the current 'Seed' value from within the monad, automatically
  updating said seed value in the process. Thus, repeatedly calling
  this function will yield different successive values.
-}
unit_randomM :: (RandomM m, UnitRandom x) => m x
unit_randomM = do
  s0 <- get_random_seed
  let (x, s1) = unit_random s0
  set_random_seed s1
  return x

{- |
  The monadic analogue of 'range_random'.

  Returns a value randomly chosen from a user-specified range
  (inclusive). Uses the current 'Seed' value from within the monad,
  automatically updating said seed value in the process. Thus,
  repeatedly calling this function will yield different successive
  values.
-}
range_randomM :: (RandomM m, RangeRandom x) => (x, x) -> m x
range_randomM xr = do
  s0 <- get_random_seed
  let (x, s1) = range_random xr s0
  set_random_seed s1
  return x