MonadRandom 0.3.0.2 → 0.6.2.1
raw patch · 10 files changed
Files
- CHANGES.markdown +175/−9
- Control/Monad/Random.hs +12/−283
- Control/Monad/Random/Class.hs +388/−41
- Control/Monad/Random/Lazy.hs +61/−0
- Control/Monad/Random/Strict.hs +59/−0
- Control/Monad/Trans/Random.hs +22/−0
- Control/Monad/Trans/Random/Lazy.hs +353/−0
- Control/Monad/Trans/Random/Strict.hs +360/−0
- LICENSE +27/−31
- MonadRandom.cabal +20/−5
CHANGES.markdown view
@@ -1,7 +1,173 @@+0.6.2.1 (19 January 2026)+-------------------------++- Test with GHC 9.14+- Drop support for GHC < 8+- Drop `transformers-compat` dependency++0.6.2 (5 March 2025)+--------------------++- Support `random-1.3`. Thanks to @bgamari and @lehins for help+ making the necessary updates.++0.6.1 (22 October 2024)+-----------------------++- Fix the potential for a crash in `fromListMay` (and hence also in+ functions that use it, namely `fromList`, `weighted`, `weightedMay`,+ `uniform`, and `uniformMay`). Thanks to @Flupp for pointing out the+ issue and suggesting a fix. The fix may in theory cause these+ functions, extremely rarely, to output values different from the old+ values for the same seed. See+ https://byorgey.github.io/blog/posts/2024/10/14/MonadRandom-version-bump.html+ for more information and discussion.++0.6 (5 Nov 2022)+----------------++- Remove instances for deprecated `ErrorT` and `ListT` transformers+- Allow building with `transformers-0.6` and `mtl-2.3.1`+- Drop support for GHC 7.6 and 7.8++- r1 (18 Dec 2022): require `base >= 4.8`+- r2 (9 Jan 2023): require `random >= 1.0.1`+- r3 (22 Feb 2023): allow `primitive-0.8`+- r4 (12 Oct 2023): allow `primitive-0.9`, test on GHC 9.6 and 9.8++0.5.3 (8 April 2021)+--------------------++- `StatefulGen` instances for `RandT`+- Addition of `RandGen`+- Addition of `withRandGen` and `withRandGen_`++- r1 (28 April 2021): require `base >= 4.6`+- r2 (9 Aug 2021): allow `transformers-compat-0.7`.+- r3 (9 Aug 2022): Test with GHC 9.0-9.4++0.5.2 (24 June 2020)+--------------------++- Support for `random-1.2`.+- Drop support for GHC 7.4, test with GHC 8.10.++0.5.1.2 (18 Jan 2020)+---------------------++- Better error message when total probability sum is negative in+ fromListMay, weightedMay, weighted++0.5.1.1 (21 May 2018)+---------------------++- Only depend on `fail` package when GHC < 8.0.++0.5.1 (9 February 2017)+-----------------------++Re-export `System.Random` from `Control.Monad.Random.{Lazy,Strict}`+and hence also from `Control.Monad.Random`.++- Hackage r1: allow `transformers-compat-0.6.x`.++0.5 (3 January 2017)+--------------------++ This release has quite a few small additions as well as a big module+ reorganization. However, thanks to module re-exports, most existing+ code using the library should continue to work with no changes; the+ major version bump reflects the large reorganization and my+ inability to 100% guarantee that existing user code will not break.++ The biggest changes that may be of interest to users of the library+ include new lazy vs strict variants of the `Rand` monad; a new+ `MonadInterleave` class which is a big improvement over+ `MonadSplit`; new `PrimMonad` instances; and new random selection+ functions like `weighted`, `weightedMay`, `uniformMay`, *etc.*. See+ the list below for full details.++ Although there was some discussion of generalizing `MonadRandom` to+ work for a wider range of underlying generators+ (see+ [#26](https://github.com/byorgey/MonadRandom/issues/26),+ [#31](https://github.com/byorgey/MonadRandom/issues/31), and+ [comments on this blog post](https://byorgey.wordpress.com/2016/11/16/monadrandom-0-5-and-mwc-random-feedback-wanted/)),+ I decided to punt on that for now. It seems rather complicated and+ there+ are+ [already good alternatives](http://hackage.haskell.org/package/random%2Dfu) so+ I decided to keep things simple for this release. I'm still open to+ proposals for generalizing future releases.++ Changes in 0.5 include:++ - Refactor to reflect structure of `mtl` and `transformers` libraries.+ - Add lazy and strict variants of `RandT`.+ - Add `MonadRandom` and `MonadSplit` instances for `ListT`.+ - Add (but do not export) `unRandT` field to `RandT`.+ - Add `MonadCont`, `MonadError`, `MonadRWS`, `PrimMonad`, and `MonadFail`+ instances for `RandT`.+ - Add `evalRandTIO` operation.+ - Move `fromList` and `uniform` operations to+ `Control.Monad.Random.Class`.+ - `fromList` now raises an error when the total weight of elements+ is zero.+ - Generalize the type of `uniform` to work over any `Foldable`.+ - Add new operations `weighted`, `weightedMay`, `fromListMay`, and+ `uniformMay`. `weighted` is like `fromList` but generalized to+ work over any `Foldable`. The `May` variants return a `Maybe`+ result instead of raising an error.+ - New `MonadInterleave` class for random monads which can interleave+ random generation using `split`. In some ways this is similar to+ `MonadSplit` but much more useful.+ - Improved documentation.++0.4.2.3 (21 April 2016)+-----------------------++ - Mark `Control.Monad.Random` as `Trustworthy`.++0.4.2.2 (18 January 2016)+-------------------------++ - Allow `transformers-0.5`.++0.4.2.1 (16 January 2016)+-------------------------++ - Allow `transformers-compat-0.5`.++0.4.2 (16 January 2016)+-----------------------++ - Add `MonadPlus` and `Alternative` instances for `RandT`.++0.4.1 (20 November 2015)+------------------------++ - Remove unnecessary `Monad m` constraint from `liftRandT` and+ `runRandT`.++ This should again technically require a major version bump, but I'm+ not doing it this time in the interest of not being super annoying.+ If this breaks something for you, just yell, and I will+ deprecate this version and do a proper 0.5 release.++0.4 (12 May 2015)+-----------------++ - Remove unnecessary `RandomGen g` constraints from `liftRandT`,+ `liftRand`, `evalRandT`, `evalRand`, `runRandT`, `runRand`.++ A major version bump is required by the PVP since the types of all+ the above methods have changed, but this release is again very+ unlikely to break any client code.+ 0.3.0.2 (30 March 2015) ----------------------- - - Add transformers-compat to allow building with newer mtl+ - Add `transformers-compat` to allow building with newer `mtl` 0.3.0.1 (24 November 2014) --------------------------@@ -22,7 +188,7 @@ 0.2.0.1 (24 August 2014) ------------------------ - - Allow building with both transformers-0.3 and 0.4.+ - Allow building with both `transformers-0.3` and `0.4`. 0.2 (20 August 2014) --------------------@@ -37,29 +203,29 @@ 0.1.13 (9 February 2014) ------------------------ - - add simple 'uniform' function for creating a uniform distribution+ - add simple `uniform` function for creating a uniform distribution over a list of values 0.1.12 (30 September 2013) -------------------------- - - add liftRandT and liftRand functions, for lifting explicit- generator-passing functions into RandT and Rand, respectively.+ - add `liftRandT` and `liftRand` functions, for lifting explicit+ generator-passing functions into `RandT` and `Rand`, respectively. 0.1.11 (1 August 2013) ---------------------- - - add MonadRandom and MonadSplit instances for IdentityT- - derive MonadReader and MonadWriter instances instead of declaring+ - add `MonadRandom` and `MonadSplit` instances for `IdentityT`+ - derive `MonadReader` and `MonadWriter` instances instead of declaring them explicitly (thanks again to James Koppel) 0.1.10 (16 July 2013) --------------------- - - add MonadRandom and MonadSplit instances for ContT+ - add `MonadRandom` and `MonadSplit` instances for `ContT` (thanks to James Koppel for the patch) 0.1.9 (26 April 2013) --------------------- - - add MonadRandom and MonadSplit instances for MaybeT+ - add `MonadRandom` and `MonadSplit` instances for `MaybeT`
Control/Monad/Random.hs view
@@ -1,291 +1,20 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE UndecidableInstances #-}-{-# OPTIONS -fno-warn-orphans #-}+{-# LANGUAGE Safe #-} {- |-Copyright : 2006-2007 Cale Gibbard, Russell O'Connor, Dan Doel, Remi Turk, Eric Kidd.-License : OtherLicense-Stability : experimental-Portability : non-portable (multi-parameter type classes, undecidable instances)--A random number generation monad. See-<http://www.haskell.org/haskellwiki/NewMonads/MonadRandom> for the original-version of this code.--The actual interface is defined by-'Control.Monad.Random.Class.MonadRandom'.--[Computation type:] Computations which consume random values.--[Binding strategy:] The computation proceeds in the same fashion as the-identity monad, but it carries a random number generator that may be-queried to generate random values.+Module : Control.Monad.Random+Copyright : (c) Brent Yorgey 2016+License : BSD3 (see LICENSE) -[Useful for:] Monte Carlo algorithms and simulating random processes.+Maintainer : byorgey@gmail.com+Stability : experimental+Portability : non-portable (multi-param classes, functional dependencies, undecidable instances) +This module is provided for backwards compatibility, and simply+re-exports "Control.Monad.Random.Lazy". -} -module Control.Monad.Random (- module System.Random,- module Control.Monad.Random.Class,- evalRandT,- runRandT,- evalRand,- runRand,- evalRandIO,- fromList,- uniform,- Rand, RandT, -- but not the data constructors- -- * Special lift functions- liftRand,- liftRandT- -- * Example- -- $RandExample+module Control.Monad.Random+ ( module Control.Monad.Random.Lazy, ) where -import Control.Applicative-import Control.Arrow-import Control.Monad ()-import Control.Monad.Cont-import Control.Monad.Error-import Control.Monad.Identity-import Control.Monad.Random.Class-import Control.Monad.Reader-import qualified Control.Monad.RWS.Lazy as RWSL-import qualified Control.Monad.RWS.Strict as RWSS-import Control.Monad.State-import qualified Control.Monad.State.Lazy as SL-import qualified Control.Monad.State.Strict as SS-import Control.Monad.Trans ()-import Control.Monad.Trans.Except-import Control.Monad.Trans.Identity-import Control.Monad.Trans.Maybe-import Control.Monad.Writer.Class-import qualified Control.Monad.Writer.Lazy as WL-import qualified Control.Monad.Writer.Strict as WS-import Data.Monoid (Monoid)-import System.Random---- | A monad transformer which adds a random number generator to an--- existing monad.-newtype RandT g m a = RandT (StateT g m a)- deriving (Functor, Monad, MonadTrans, MonadIO, MonadFix, MonadReader r, MonadWriter w)--instance (Functor m,Monad m) => Applicative (RandT g m) where- pure = return- (<*>) = ap---- | Lift arbitrary action to RandT-liftRandT :: (Monad m, RandomGen g) =>- (g -> m (a, g)) -- ^ action returning value and new generator state- -> RandT g m a-liftRandT = RandT . StateT---- | Lift arbitrary action to Rand-liftRand :: (RandomGen g) =>- (g -> (a, g)) -- ^ action returning value and new generator state- -> Rand g a-liftRand = RandT . state--instance (Monad m, RandomGen g) => MonadRandom (RandT g m) where- getRandom = RandT . state $ random- getRandoms = RandT . state $ first randoms . split- getRandomR (x,y) = RandT . state $ randomR (x,y)- getRandomRs (x,y) = RandT . state $- first (randomRs (x,y)) . split--instance (Monad m, RandomGen g) => MonadSplit g (RandT g m) where- getSplit = RandT . state $ split---- | Evaluate a RandT computation using the generator @g@. Note that the--- generator @g@ is not returned, so there's no way to recover the--- updated version of @g@.-evalRandT :: (Monad m, RandomGen g) => RandT g m a -> g -> m a-evalRandT (RandT x) g = evalStateT x g---- | Run a RandT computation using the generator @g@, returning the result and--- the updated generator.-runRandT :: (Monad m, RandomGen g) => RandT g m a -> g -> m (a, g)-runRandT (RandT x) g = runStateT x g---- | A basic random monad.-type Rand g = RandT g Identity---- | Evaluate a random computation using the generator @g@. Note that the--- generator @g@ is not returned, so there's no way to recover the--- updated version of @g@.-evalRand :: (RandomGen g) => Rand g a -> g -> a-evalRand x g = runIdentity (evalRandT x g)---- | Run a random computation using the generator @g@, returning the result--- and the updated generator.-runRand :: (RandomGen g) => Rand g a -> g -> (a, g)-runRand x g = runIdentity (runRandT x g)---- | Evaluate a random computation in the IO monad, splitting the global standard generator to get a new one for the computation.-evalRandIO :: Rand StdGen a -> IO a-evalRandIO x = fmap (evalRand x) newStdGen---- | Sample a random value from a weighted list. The total weight of all--- elements must not be 0.-fromList :: (MonadRandom m) => [(a,Rational)] -> m a-fromList [] = error "MonadRandom.fromList called with empty list"-fromList [(x,_)] = return x-fromList xs = do- -- TODO: Do we want to be able to use floats as weights?- -- TODO: Better error message if weights sum to 0.- let s = (fromRational (sum (map snd xs))) :: Double -- total weight- cs = scanl1 (\(_,q) (y,s') -> (y, s'+q)) xs -- cumulative weight- p <- liftM toRational $ getRandomR (0.0,s)- return . fst . head $ dropWhile (\(_,q) -> q < p) cs---- | Sample a value from a uniform distribution of a list of elements.-uniform :: (MonadRandom m) => [a] -> m a-uniform = fromList . fmap (flip (,) 1)--instance (MonadRandom m) => MonadRandom (IdentityT m) where- getRandom = lift getRandom- getRandomR = lift . getRandomR- getRandoms = lift getRandoms- getRandomRs = lift . getRandomRs--instance (MonadRandom m) => MonadRandom (SL.StateT s m) where- getRandom = lift getRandom- getRandomR = lift . getRandomR- getRandoms = lift getRandoms- getRandomRs = lift . getRandomRs--instance (MonadRandom m) => MonadRandom (SS.StateT s m) where- getRandom = lift getRandom- getRandomR = lift . getRandomR- getRandoms = lift getRandoms- getRandomRs = lift . getRandomRs--instance (MonadRandom m, Monoid w) => MonadRandom (WL.WriterT w m) where- getRandom = lift getRandom- getRandomR = lift . getRandomR- getRandoms = lift getRandoms- getRandomRs = lift . getRandomRs--instance (MonadRandom m, Monoid w) => MonadRandom (WS.WriterT w m) where- getRandom = lift getRandom- getRandomR = lift . getRandomR- getRandoms = lift getRandoms- getRandomRs = lift . getRandomRs--instance (MonadRandom m) => MonadRandom (ReaderT r m) where- getRandom = lift getRandom- getRandomR = lift . getRandomR- getRandoms = lift getRandoms- getRandomRs = lift . getRandomRs--instance (MonadRandom m, Monoid w) => MonadRandom (RWSL.RWST r w s m) where- getRandom = lift getRandom- getRandomR = lift . getRandomR- getRandoms = lift getRandoms- getRandomRs = lift . getRandomRs--instance (MonadRandom m, Monoid w) => MonadRandom (RWSS.RWST r w s m) where- getRandom = lift getRandom- getRandomR = lift . getRandomR- getRandoms = lift getRandoms- getRandomRs = lift . getRandomRs--instance (MonadRandom m) => MonadRandom (ExceptT e m) where- getRandom = lift getRandom- getRandomR = lift . getRandomR- getRandoms = lift getRandoms- getRandomRs = lift . getRandomRs--instance (Error e, MonadRandom m) => MonadRandom (ErrorT e m) where- getRandom = lift getRandom- getRandomR = lift . getRandomR- getRandoms = lift getRandoms- getRandomRs = lift . getRandomRs--instance (MonadRandom m) => MonadRandom (MaybeT m) where- getRandom = lift getRandom- getRandomR = lift . getRandomR- getRandoms = lift getRandoms- getRandomRs = lift . getRandomRs--instance MonadRandom m => MonadRandom (ContT r m) where- getRandom = lift getRandom- getRandomR = lift . getRandomR- getRandoms = lift getRandoms- getRandomRs = lift . getRandomRs--instance (MonadSplit g m) => MonadSplit g (IdentityT m) where- getSplit = lift getSplit--instance (MonadSplit g m) => MonadSplit g (SL.StateT s m) where- getSplit = lift getSplit--instance (MonadSplit g m) => MonadSplit g (SS.StateT s m) where- getSplit = lift getSplit--instance (MonadSplit g m, Monoid w) => MonadSplit g (WL.WriterT w m) where- getSplit = lift getSplit--instance (MonadSplit g m, Monoid w) => MonadSplit g (WS.WriterT w m) where- getSplit = lift getSplit--instance (MonadSplit g m) => MonadSplit g (ReaderT r m) where- getSplit = lift getSplit--instance (MonadSplit g m, Monoid w) => MonadSplit g (RWSL.RWST r w s m) where- getSplit = lift getSplit--instance (MonadSplit g m, Monoid w) => MonadSplit g (RWSS.RWST r w s m) where- getSplit = lift getSplit--instance (MonadSplit g m) => MonadSplit g (ExceptT e m) where- getSplit = lift getSplit--instance (Error e, MonadSplit g m) => MonadSplit g (ErrorT e m) where- getSplit = lift getSplit--instance (MonadSplit g m) => MonadSplit g (MaybeT m) where- getSplit = lift getSplit--instance (MonadSplit g m) => MonadSplit g (ContT r m) where- getSplit = lift getSplit--instance (MonadState s m) => MonadState s (RandT g m) where- get = lift get- put = lift . put--instance MonadRandom IO where- getRandom = randomIO- getRandomR = randomRIO- getRandoms = fmap randoms newStdGen- getRandomRs b = fmap (randomRs b) newStdGen--instance MonadSplit StdGen IO where- getSplit = newStdGen--{- $RandExample--The @die@ function simulates the roll of a die, picking a number between 1-and 6, inclusive, and returning it in the 'Rand' monad. Notice that this-code will work with any source of random numbers @g@.-->die :: (RandomGen g) => Rand g Int->die = getRandomR (1,6)--The @dice@ function uses @replicate@ and @sequence@ to simulate the roll of-@n@ dice.-->dice :: (RandomGen g) => Int -> Rand g [Int]->dice n = sequence (replicate n die)--To extract a value from the 'Rand' monad, we can can use 'evalRandIO'.-->main = do-> values <- evalRandIO (dice 2)-> putStrLn (show values)---}+import Control.Monad.Random.Lazy
Control/Monad/Random/Class.hs view
@@ -1,50 +1,397 @@-{-# LANGUAGE MultiParamTypeClasses, UndecidableInstances, FunctionalDependencies #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE Safe #-}+{-# LANGUAGE UndecidableInstances #-} -{- |-Copyright : 2006-2007 Cale Gibbard, Russell O'Connor, Dan Doel, Remi Turk, Eric Kidd.-License : OtherLicense-Stability : experimental-Portability : non-portable (multi-parameter type classes, undecidable instances)+-- |+-- Module : Control.Monad.Random.Class+-- Copyright : (c) Brent Yorgey 2016+-- License : BSD3 (see LICENSE)+-- Maintainer : byorgey@gmail.com+--+-- The 'MonadRandom', 'MonadSplit', and 'MonadInterleave' classes.+--+-- * 'MonadRandom' abstracts over monads with the capability of+-- generating random values.+--+-- * 'MonadSplit' abstracts over random monads with the ability to get a+-- split generator state. It is not very useful but kept here for+-- backwards compatibility.+--+-- * 'MonadInterleave' abstracts over random monads supporting an+-- 'interleave' operation, which allows sequencing computations which do+-- not depend on each other's random generator state, by splitting the+-- generator between them.+--+-- This module also defines convenience functions for sampling from a+-- given collection of values, either uniformly or according to given+-- weights.+module Control.Monad.Random.Class (+ -- * MonadRandom+ MonadRandom (..), -A type class for random number generation monads. See-<http://www.haskell.org/haskellwiki/NewMonads/MonadRandom> for the original-version of this code.+ -- * MonadSplit+ MonadSplit (..), -Instances of this type class include 'Control.Monad.Random.Rand' and-monads created using 'Control.Monad.Random.RandT'.+ -- * MonadInterleave+ MonadInterleave (..), --}+ -- * Sampling functions+ fromList,+ fromListMay,+ uniform,+ uniformMay,+ weighted,+ weightedMay,+) where -module Control.Monad.Random.Class (- MonadRandom,- getRandom,- getRandomR,- getRandoms,- getRandomRs,- MonadSplit,- getSplit- ) where+import Control.Monad+import Control.Monad.Trans.Class+import Control.Monad.Trans.Cont+import Control.Monad.Trans.Except+import Control.Monad.Trans.Identity+import Control.Monad.Trans.Maybe+import qualified Control.Monad.Trans.RWS.Lazy as LazyRWS+import qualified Control.Monad.Trans.RWS.Strict as StrictRWS+import Control.Monad.Trans.Reader+import qualified Control.Monad.Trans.State.Lazy as LazyState+import qualified Control.Monad.Trans.State.Strict as StrictState+import qualified Control.Monad.Trans.Writer.Lazy as LazyWriter+import qualified Control.Monad.Trans.Writer.Strict as StrictWriter+import qualified Data.Foldable as F+import Data.Word (Word64)+import qualified System.Random as Random -import System.Random+#if MIN_VERSION_base(4,8,0)+#else+import Data.Monoid (Monoid)+#endif --- | An interface to random number generation monads.-class (Monad m) => MonadRandom m where- -- | Return a randomly-selected value of type @a@. See- -- 'System.Random.random' for details.- getRandom :: (Random a) => m a- -- | Return an infinite stream of random values of type @a@. See- -- 'System.Random.randoms' for details.- getRandoms :: (Random a) => m [a]- -- | Return a randomly-selected value of type @a@ in the range- -- /[lo,hi]/. See 'System.Random.randomR' for details.- getRandomR :: (Random a) => (a,a) -> m a- -- | Return an infinite stream of randomly-selected value of type @a@- -- in the range /[lo,hi]/. See 'System.Random.randomRs' for details.- getRandomRs :: (Random a) => (a,a) -> m [a]+------------------------------------------------------------+-- MonadRandom+------------------------------------------------------------ --- | An interface to monads with splittable state (as most random number generation monads will have).--- The intention is that the 'getSplit' action splits the state, returning one half of the result, and--- setting the new state to the other.-class (Monad m) => MonadSplit s m | m -> s where- getSplit :: m s+-- | With a source of random number supply in hand, the 'MonadRandom' class+-- allows the programmer to extract random values of a variety of types.+class Monad m => MonadRandom m where+ -- | Takes a range /(lo,hi)/ and a random number generator+ -- /g/, and returns a computation that returns a random value uniformly+ -- distributed in the closed interval /[lo,hi]/, together with a new+ -- generator. It is unspecified what happens if /lo>hi/. For continuous+ -- types there is no requirement that the values /lo/ and /hi/ are ever+ -- produced, but they may be, depending on the implementation and the+ -- interval.+ --+ -- See 'System.Random.randomR' for details.+ getRandomR :: Random.Random a => (a, a) -> m a + -- | The same as 'getRandomR', but using a default range determined by the type:+ --+ -- * For bounded types (instances of 'Bounded', such as 'Char'),+ -- the range is normally the whole type.+ --+ -- * For fractional types, the range is normally the semi-closed interval+ -- @[0,1)@.+ --+ -- * For 'Integer', the range is (arbitrarily) the range of 'Int'.+ --+ -- See 'System.Random.random' for details.+ getRandom :: Random.Random a => m a++ -- | Plural variant of 'getRandomR', producing an infinite list of+ -- random values instead of returning a new generator.+ --+ -- See 'System.Random.randomRs' for details.+ getRandomRs :: Random.Random a => (a, a) -> m [a]++ -- | Plural variant of 'getRandom', producing an infinite list of+ -- random values instead of returning a new generator.+ --+ -- See 'System.Random.randoms' for details.+ getRandoms :: Random.Random a => m [a]++instance MonadRandom IO where+ getRandomR = Random.randomRIO+ getRandom = Random.randomIO+ getRandomRs lohi = liftM (Random.randomRs lohi) Random.newStdGen+ getRandoms = liftM Random.randoms Random.newStdGen++instance MonadRandom m => MonadRandom (ContT r m) where+ getRandomR = lift . getRandomR+ getRandom = lift getRandom+ getRandomRs = lift . getRandomRs+ getRandoms = lift getRandoms++instance MonadRandom m => MonadRandom (ExceptT e m) where+ getRandomR = lift . getRandomR+ getRandom = lift getRandom+ getRandomRs = lift . getRandomRs+ getRandoms = lift getRandoms++instance MonadRandom m => MonadRandom (IdentityT m) where+ getRandomR = lift . getRandomR+ getRandom = lift getRandom+ getRandomRs = lift . getRandomRs+ getRandoms = lift getRandoms++instance MonadRandom m => MonadRandom (MaybeT m) where+ getRandomR = lift . getRandomR+ getRandom = lift getRandom+ getRandomRs = lift . getRandomRs+ getRandoms = lift getRandoms++instance (Monoid w, MonadRandom m) => MonadRandom (LazyRWS.RWST r w s m) where+ getRandomR = lift . getRandomR+ getRandom = lift getRandom+ getRandomRs = lift . getRandomRs+ getRandoms = lift getRandoms++instance (Monoid w, MonadRandom m) => MonadRandom (StrictRWS.RWST r w s m) where+ getRandomR = lift . getRandomR+ getRandom = lift getRandom+ getRandomRs = lift . getRandomRs+ getRandoms = lift getRandoms++instance MonadRandom m => MonadRandom (ReaderT r m) where+ getRandomR = lift . getRandomR+ getRandom = lift getRandom+ getRandomRs = lift . getRandomRs+ getRandoms = lift getRandoms++instance MonadRandom m => MonadRandom (LazyState.StateT s m) where+ getRandomR = lift . getRandomR+ getRandom = lift getRandom+ getRandomRs = lift . getRandomRs+ getRandoms = lift getRandoms++instance MonadRandom m => MonadRandom (StrictState.StateT s m) where+ getRandomR = lift . getRandomR+ getRandom = lift getRandom+ getRandomRs = lift . getRandomRs+ getRandoms = lift getRandoms++instance (MonadRandom m, Monoid w) => MonadRandom (LazyWriter.WriterT w m) where+ getRandomR = lift . getRandomR+ getRandom = lift getRandom+ getRandomRs = lift . getRandomRs+ getRandoms = lift getRandoms++instance (MonadRandom m, Monoid w) => MonadRandom (StrictWriter.WriterT w m) where+ getRandomR = lift . getRandomR+ getRandom = lift getRandom+ getRandomRs = lift . getRandomRs+ getRandoms = lift getRandoms++------------------------------------------------------------+-- MonadSplit+------------------------------------------------------------++-- | The class 'MonadSplit' proivides a way to specify a random number+-- generator that can be split into two new generators.+--+-- This class is not very useful in practice: typically, one cannot+-- actually do anything with a generator. It remains here to avoid+-- breaking existing code unnecessarily. For a more practically+-- useful interface, see 'MonadInterleave'.+class Monad m => MonadSplit g m | m -> g where+ -- | The 'getSplit' operation allows one to obtain two distinct random number+ -- generators.+ --+ -- See 'System.Random.split' for details.+ getSplit :: m g++instance MonadSplit Random.StdGen IO where+ getSplit = Random.newStdGen++instance MonadSplit g m => MonadSplit g (ContT r m) where+ getSplit = lift getSplit++instance MonadSplit g m => MonadSplit g (ExceptT e m) where+ getSplit = lift getSplit++instance MonadSplit g m => MonadSplit g (IdentityT m) where+ getSplit = lift getSplit++instance MonadSplit g m => MonadSplit g (MaybeT m) where+ getSplit = lift getSplit++instance (Monoid w, MonadSplit g m) => MonadSplit g (LazyRWS.RWST r w s m) where+ getSplit = lift getSplit++instance (Monoid w, MonadSplit g m) => MonadSplit g (StrictRWS.RWST r w s m) where+ getSplit = lift getSplit++instance MonadSplit g m => MonadSplit g (ReaderT r m) where+ getSplit = lift getSplit++instance MonadSplit g m => MonadSplit g (LazyState.StateT s m) where+ getSplit = lift getSplit++instance MonadSplit g m => MonadSplit g (StrictState.StateT s m) where+ getSplit = lift getSplit++instance (Monoid w, MonadSplit g m) => MonadSplit g (LazyWriter.WriterT w m) where+ getSplit = lift getSplit++instance (Monoid w, MonadSplit g m) => MonadSplit g (StrictWriter.WriterT w m) where+ getSplit = lift getSplit++------------------------------------------------------------+-- MonadInterleave+------------------------------------------------------------++-- | The class 'MonadInterleave' proivides a convenient interface atop+-- a 'split' operation on a random generator.+class MonadRandom m => MonadInterleave m where+ -- | If @x :: m a@ is a computation in some random monad, then+ -- @interleave x@ works by splitting the generator, running @x@+ -- using one half, and using the other half as the final generator+ -- state of @interleave x@ (replacing whatever the final generator+ -- state otherwise would have been). This means that computation+ -- needing random values which comes after @interleave x@ does not+ -- necessarily depend on the computation of @x@. For example:+ --+ -- > >>> evalRandIO $ snd <$> ((,) <$> undefined <*> getRandom)+ -- > *** Exception: Prelude.undefined+ -- > >>> evalRandIO $ snd <$> ((,) <$> interleave undefined <*> getRandom)+ -- > 6192322188769041625+ --+ -- This can be used, for example, to allow random computations to+ -- run in parallel, or to create lazy infinite structures of+ -- random values. In the example below, the infinite tree+ -- @randTree@ cannot be evaluated lazily: even though it is cut+ -- off at two levels deep by @hew 2@, the random value in the+ -- right subtree still depends on generation of all the random+ -- values in the (infinite) left subtree, even though they are+ -- ultimately unneeded. Inserting a call to @interleave@, as in+ -- @randTreeI@, solves the problem: the generator splits at each+ -- @Node@, so random values in the left and right subtrees are+ -- generated independently.+ --+ -- > data Tree = Leaf | Node Int Tree Tree deriving Show+ -- >+ -- > hew :: Int -> Tree -> Tree+ -- > hew 0 _ = Leaf+ -- > hew _ Leaf = Leaf+ -- > hew n (Node x l r) = Node x (hew (n-1) l) (hew (n-1) r)+ -- >+ -- > randTree :: Rand StdGen Tree+ -- > randTree = Node <$> getRandom <*> randTree <*> randTree+ -- >+ -- > randTreeI :: Rand StdGen Tree+ -- > randTreeI = interleave $ Node <$> getRandom <*> randTreeI <*> randTreeI+ --+ -- > >>> hew 2 <$> evalRandIO randTree+ -- > Node 2168685089479838995 (Node (-1040559818952481847) Leaf Leaf) (Node ^CInterrupted.+ -- > >>> hew 2 <$> evalRandIO randTreeI+ -- > Node 8243316398511136358 (Node 4139784028141790719 Leaf Leaf) (Node 4473998613878251948 Leaf Leaf)+ interleave :: m a -> m a++instance MonadInterleave m => MonadInterleave (ContT r m) where+ interleave = mapContT interleave++instance MonadInterleave m => MonadInterleave (ExceptT e m) where+ interleave = mapExceptT interleave++instance MonadInterleave m => MonadInterleave (IdentityT m) where+ interleave = mapIdentityT interleave++instance MonadInterleave m => MonadInterleave (MaybeT m) where+ interleave = mapMaybeT interleave++instance (Monoid w, MonadInterleave m) => MonadInterleave (LazyRWS.RWST r w s m) where+ interleave = LazyRWS.mapRWST interleave++instance (Monoid w, MonadInterleave m) => MonadInterleave (StrictRWS.RWST r w s m) where+ interleave = StrictRWS.mapRWST interleave++instance MonadInterleave m => MonadInterleave (ReaderT r m) where+ interleave = mapReaderT interleave++instance MonadInterleave m => MonadInterleave (LazyState.StateT s m) where+ interleave = LazyState.mapStateT interleave++instance MonadInterleave m => MonadInterleave (StrictState.StateT s m) where+ interleave = StrictState.mapStateT interleave++instance (Monoid w, MonadInterleave m) => MonadInterleave (LazyWriter.WriterT w m) where+ interleave = LazyWriter.mapWriterT interleave++instance (Monoid w, MonadInterleave m) => MonadInterleave (StrictWriter.WriterT w m) where+ interleave = StrictWriter.mapWriterT interleave++------------------------------------------------------------+-- Convenience samplers+------------------------------------------------------------++-- | Sample a random value from a weighted nonempty collection of+-- elements. Crashes with a call to @error@ if the collection is+-- empty or the total weight is zero.+weighted :: (F.Foldable t, MonadRandom m) => t (a, Rational) -> m a+weighted t = do+ ma <- weightedMay t+ case ma of+ Nothing -> error "Control.Monad.Random.Class.weighted: empty collection, or total weight <= 0"+ Just a -> return a++-- | Sample a random value from a weighted collection of elements.+-- Returns @Nothing@ if the collection is empty or the total weight is+-- zero.+weightedMay :: (F.Foldable t, MonadRandom m) => t (a, Rational) -> m (Maybe a)+weightedMay = fromListMay . F.toList++-- | Sample a random value from a weighted list. The list must be+-- non-empty and the total weight must be non-zero.+fromList :: MonadRandom m => [(a, Rational)] -> m a+fromList ws = do+ ma <- fromListMay ws+ case ma of+ Nothing -> error "Control.Monad.Random.Class.fromList: empty list, or total weight = 0"+ Just a -> return a++-- | Sample a random value from a weighted list. Return @Nothing@ if+-- the list is empty or the total weight is nonpositive.+fromListMay :: MonadRandom m => [(a, Rational)] -> m (Maybe a)+fromListMay xs = do+ let s = sum (map snd xs)+ cums = scanl1 (\ ~(_, q) ~(y, s') -> (y, s' + q)) xs+ if s <= 0+ then return Nothing+ else do+ -- Pick a Word64 value uniformly+ w <- getRandom+ -- w / maxBound gives a uniform Rational in the range [0,1].+ -- Subtract from 1 to match the way uniform Double values are+ -- generated, and hence match the old behavior of this function.+ let p = s * (1 - toRational (w :: Word64) / toRational (maxBound :: Word64))+ return . fmap fst . F.find ((>= p) . snd) $ cums++-- | Sample a value uniformly from a nonempty collection of elements.+uniform :: (F.Foldable t, MonadRandom m) => t a -> m a+uniform t = do+ ma <- uniformMay t+ case ma of+ Nothing -> error "Control.Monad.Random.Class.uniform: empty collection"+ Just a -> return a++-- | Sample a value uniformly from a collection of elements. Return+-- @Nothing@ if the collection is empty.+uniformMay :: (F.Foldable t, MonadRandom m) => t a -> m (Maybe a)+uniformMay = fromListMay . map (flip (,) 1) . F.toList++------------------------------------------------------------++-- The old implementation of `fromListMay`, for comparison. See+-- https://github.com/byorgey/MonadRandom/issues/53 and+-- https://byorgey.github.io/blog/posts/2024/10/14/MonadRandom-version-bump.html+_fromListMayOld :: MonadRandom m => [(a, Rational)] -> m (Maybe a)+_fromListMayOld xs = do+ let s = fromRational (sum (map snd xs)) :: Double+ cums = scanl1 (\ ~(_, q) ~(y, s') -> (y, s' + q)) xs+ if s <= 0+ then return Nothing+ else do+ p <- liftM toRational $ getRandomR (0, s)+ return . fmap fst . F.find ((>= p) . snd) $ cums
+ Control/Monad/Random/Lazy.hs view
@@ -0,0 +1,61 @@+{-# LANGUAGE Safe #-}++{- |+Module : Control.Monad.Random.Lazy+Copyright : (c) Brent Yorgey 2016+License : BSD3 (see LICENSE)++Maintainer : byorgey@gmail.com+Stability : experimental+Portability : non-portable (multi-param classes, functional dependencies, undecidable instances)++Random monads that are lazy in the generator state. For a strict+version, see "Control.Monad.Random.Strict", which has the same+interface.+-}++module Control.Monad.Random.Lazy+ ( -- * The Rand monad+ Rand,+ liftRand,+ runRand,+ evalRand,+ execRand,+ mapRand,+ withRand,+ evalRandIO,++ -- * The RandT monad transformer+ RandT,+ liftRandT,+ runRandT,+ evalRandT,+ execRandT,+ mapRandT,+ withRandT,+ evalRandTIO,++ -- * Some convenience re-exports++ module System.Random,+ module Control.Monad.Random.Class,+ module Control.Monad,+ module Control.Monad.Fix,+ module Control.Monad.Trans,+ ) where++import System.Random hiding (uniform, uniformR)++import Control.Monad.Random.Class++import Control.Monad.Trans+import Control.Monad.Trans.Random.Lazy (Rand, RandT, evalRand,+ evalRandIO, evalRandT,+ evalRandTIO, execRand,+ execRandT, liftRand,+ liftRandT, mapRand, mapRandT,+ runRand, runRandT, withRand,+ withRandT)++import Control.Monad+import Control.Monad.Fix
+ Control/Monad/Random/Strict.hs view
@@ -0,0 +1,59 @@+{-# LANGUAGE Safe #-}++{- |+Module : Control.Monad.Random.Strict+Copyright : (c) Brent Yorgey 2016+License : BSD3 (see LICENSE)++Maintainer : byorgey@gmail.com+Stability : experimental+Portability : non-portable (multi-param classes, functional dependencies, undecidable instances)++Random monads that are strict in the generator state. For a lazy+version, see "Control.Monad.Random.Lazy", which has the same+interface.+-}++module Control.Monad.Random.Strict+ ( -- * The Rand monad transformer+ Rand,+ liftRand,+ runRand,+ evalRand,+ execRand,+ mapRand,+ withRand,+ evalRandIO,+ -- * The RandT monad transformer+ RandT,+ liftRandT,+ runRandT,+ evalRandT,+ execRandT,+ mapRandT,+ withRandT,+ evalRandTIO,+ -- * Some convenience re-exports++ module System.Random,+ module Control.Monad.Random.Class,+ module Control.Monad,+ module Control.Monad.Fix,+ module Control.Monad.Trans,+ ) where++import System.Random hiding (uniform, uniformR)++import Control.Monad.Random.Class++import Control.Monad.Trans+import Control.Monad.Trans.Random.Strict (Rand, RandT, evalRand,+ evalRandIO, evalRandT,+ evalRandTIO, execRand,+ execRandT, liftRand,+ liftRandT, mapRand,+ mapRandT, runRand, runRandT,+ withRand, withRandT)++import Control.Monad+import Control.Monad.Fix
+ Control/Monad/Trans/Random.hs view
@@ -0,0 +1,22 @@+{-# LANGUAGE Safe #-}++{- |+Module : Control.Monad.Trans.Random+Copyright : (c) Brent Yorgey 2016+License : BSD3 (see LICENSE)++Maintainer : byorgey@gmail.com+Stability : experimental+Portability : non-portable (multi-param classes, functional dependencies, undecidable instances)++Random monads, passing a random number generator through a computation.++This version is lazy; for a strict version, see+"Control.Monad.Trans.Random.Strict", which has the same interface.+-}++module Control.Monad.Trans.Random+ ( module Control.Monad.Trans.Random.Lazy,+ ) where++import Control.Monad.Trans.Random.Lazy
+ Control/Monad/Trans/Random/Lazy.hs view
@@ -0,0 +1,353 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE Trustworthy #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE UndecidableInstances #-}++{- |+Module : Control.Monad.Trans.Random.Lazy+Copyright : (c) Brent Yorgey 2016+License : BSD3 (see LICENSE)++Maintainer : byorgey@gmail.com+Stability : experimental+Portability : non-portable (multi-param classes, functional dependencies, undecidable instances)++Lazy random monads, passing a random number generator through a computation.+See below for examples.++For a strict version with the same interface, see+"Control.Monad.Trans.Random.Strict".+-}++module Control.Monad.Trans.Random.Lazy+ ( -- * The Rand monad transformer+ Rand,+ liftRand,+ runRand,+ evalRand,+ execRand,+ mapRand,+ withRand,+ evalRandIO,+ -- * The RandT monad transformer+ RandT,+ liftRandT,+ runRandT,+ evalRandT,+ execRandT,+ mapRandT,+ withRandT,+ -- * Lifting other operations+ liftCallCC,+ liftCallCC',+ liftCatch,+ liftListen,+ liftPass,+ evalRandTIO,+ -- * StatefulGen interface+ RandGen(..),+ withRandGen,+ withRandGen_,+ -- * Examples+ -- ** Random monads+ -- $examples+ ) where++import Control.Applicative ( Alternative )+import Control.Arrow (first)+import Control.Monad ( liftM, MonadPlus )+import Control.Monad.Cont.Class (MonadCont(..))+import Control.Monad.Error.Class ( MonadError(..) )+import qualified Control.Monad.Fail as Fail+import Control.Monad.Fix ( MonadFix )+import Control.Monad.IO.Class ( MonadIO(..) )+import Control.Monad.Primitive ( PrimMonad(..) )+import Control.Monad.Random.Class ( MonadInterleave(..), MonadSplit(..), MonadRandom(..) )+import Control.Monad.RWS.Class ( MonadState(..), MonadRWS, MonadReader, MonadWriter )+import Control.Monad.Signatures ( Listen, Pass, CallCC, Catch )+import Control.Monad.Trans.Class ( MonadTrans(..) )+import qualified Control.Monad.Trans.State.Lazy as LazyState+import Control.Monad.Trans.Random.Strict (RandGen(..))+import Data.Functor.Identity ( Identity(runIdentity) )+#if MIN_VERSION_random(1,2,0)+import System.Random.Stateful+#else+import System.Random+#endif++-- | A random monad parameterized by the type @g@ of the generator to carry.+--+-- The 'return' function leaves the generator unchanged, while '>>=' uses the+-- final generator of the first computation as the initial generator of the+-- second.+type Rand g = RandT g Identity++-- | Construct a random monad computation from a function.+-- (The inverse of 'runRand'.)+liftRand+ :: (g -> (a, g))+ -- ^ pure random transformer+ -> Rand g a+ -- ^ equivalent generator-passing computation+liftRand = RandT . state++-- | Unwrap a random monad computation as a function.+-- (The inverse of 'liftRand'.)+runRand+ :: Rand g a+ -- ^ generator-passing computation to execute+ -> g+ -- ^ initial generator+ -> (a, g)+ -- ^ return value and final generator+runRand t = runIdentity . runRandT t++-- | Evaluate a random computation with the given initial generator and return+-- the final value, discarding the final generator.+--+-- * @'evalRand' m s = fst ('runRand' m s)@+evalRand+ :: Rand g a+ -- ^ generator-passing computation to execute+ -> g+ -- ^ initial generator+ -> a+ -- ^ return value of the random computation+evalRand t = runIdentity . evalRandT t++-- | Evaluate a random computation with the given initial generator and return+-- the final generator, discarding the final value.+--+-- * @'execRand' m s = snd ('runRand' m s)@+execRand+ :: Rand g a+ -- ^ generator-passing computation to execute+ -> g+ -- ^ initial generator+ -> g+ -- ^ final generator+execRand t = runIdentity . execRandT t++-- | Map both the return value and final generator of a computation using the+-- given function.+--+-- * @'runRand' ('mapRand' f m) = f . 'runRand' m@+mapRand :: ((a, g) -> (b, g)) -> Rand g a -> Rand g b+mapRand f = mapRandT (liftM f)++-- | @'withRand' f m@ executes action @m@ on a generator modified by applying @f@.+--+-- * @'withRand' f m = 'modify' f >> m@+withRand :: (g -> g) -> Rand g a -> Rand g a+withRand = withRandT++-- | A random transformer monad parameterized by:+--+-- * @g@ - The generator.+--+-- * @m@ - The inner monad.+--+-- The 'return' function leaves the generator unchanged, while '>>=' uses the+-- final generator of the first computation as the initial generator of the+-- second.+newtype RandT g m a = RandT { unRandT :: LazyState.StateT g m a }+ deriving (Functor, Applicative, Alternative, Monad, MonadPlus, MonadTrans, MonadIO, MonadFix, MonadReader r, MonadWriter w)++-- | Construct a random monad computation from an impure function.+-- (The inverse of 'runRandT'.)+liftRandT+ :: (g -> m (a, g))+ -- ^ impure random transformer+ -> RandT g m a+ -- ^ equivalent generator-passing computation+liftRandT = RandT . LazyState.StateT++-- | Unwrap a random monad computation as an impure function.+-- (The inverse of 'liftRandT'.)+runRandT+ :: RandT g m a+ -- ^ generator-passing computation to execute+ -> g+ -- ^ initial generator+ -> m (a, g)+ -- ^ return value and final generator+runRandT = LazyState.runStateT . unRandT++-- | Evaluate a random computation with the given initial generator and return+-- the final value, discarding the final generator.+--+-- * @'evalRandT' m g = liftM fst ('runRandT' m g)@+evalRandT :: (Monad m) => RandT g m a -> g -> m a+evalRandT = LazyState.evalStateT . unRandT++-- | Evaluate a random computation with the given initial generator and return+-- the final generator, discarding the final value.+--+-- * @'execRandT' m g = liftM snd ('runRandT' m g)@+execRandT :: (Monad m) => RandT g m a -> g -> m g+execRandT = LazyState.execStateT . unRandT++-- | Map both the return value and final generator of a computation using the+-- given function.+--+-- * @'runRandT' ('mapRandT' f m) = f . 'runRandT' m@+mapRandT :: (m (a, g) -> n (b, g)) -> RandT g m a -> RandT g n b+mapRandT f = RandT . LazyState.mapStateT f . unRandT++-- | @'withRandT' f m@ executes action @m@ on a generator modified by applying @f@.+--+-- * @'withRandT' f m = 'modify' f >> m@+withRandT :: (g -> g) -> RandT g m a -> RandT g m a+withRandT f = RandT . LazyState.withStateT f . unRandT++instance (MonadCont m) => MonadCont (RandT g m) where+ callCC = liftCallCC' callCC++instance (MonadError e m) => MonadError e (RandT g m) where+ throwError = lift . throwError+ catchError = liftCatch catchError++instance (MonadReader r m, MonadWriter w m, MonadState s m) => MonadRWS r w s (RandT g m)++instance (RandomGen g, Monad m) => MonadRandom (RandT g m) where+ getRandomR lohi = RandT . state $ randomR lohi+ getRandom = RandT . state $ random+ getRandomRs lohi = RandT . state $ first (randomRs lohi) . split+ getRandoms = RandT . state $ first randoms . split++instance (RandomGen g, Monad m) => MonadSplit g (RandT g m) where+ getSplit = RandT . state $ split++instance (Monad m, RandomGen g) => MonadInterleave (RandT g m) where+ interleave (RandT m) = liftRandT $ \g -> case split g of+ (gl, gr) -> liftM (\p -> (fst p, gr)) $ LazyState.runStateT m gl++instance (MonadState s m) => MonadState s (RandT g m) where+ get = lift get+ put = lift . put++instance PrimMonad m => PrimMonad (RandT s m) where+ type PrimState (RandT s m) = PrimState m+ primitive = lift . primitive++instance Fail.MonadFail m => Fail.MonadFail (RandT g m) where+ fail = lift . Fail.fail++-- | Uniform lifting of a @callCC@ operation to the new monad.+-- This version rolls back to the original state on entering the+-- continuation.+liftCallCC :: CallCC m (a, g) (b, g) -> CallCC (RandT g m) a b+liftCallCC callCC_ f = RandT $ LazyState.liftCallCC callCC_ $ \c -> unRandT (f (RandT . c))++-- | In-situ lifting of a @callCC@ operation to the new monad.+-- This version uses the current state on entering the continuation.+-- It does not satisfy the uniformity property (see "Control.Monad.Signatures").+liftCallCC' :: CallCC m (a, g) (b, g) -> CallCC (RandT g m) a b+liftCallCC' callCC_ f = RandT $ LazyState.liftCallCC' callCC_ $ \c -> unRandT (f (RandT . c))++-- | Lift a @catchE@ operation to the new monad.+liftCatch :: Catch e m (a, g) -> Catch e (RandT g m) a+liftCatch catchE_ m f = RandT $ LazyState.liftCatch catchE_ (unRandT m) (unRandT . f)++-- | Lift a @listen@ operation to the new monad.+liftListen :: (Monad m) => Listen w m (a, g) -> Listen w (RandT g m) a+liftListen listen_ m = RandT $ LazyState.liftListen listen_ (unRandT m)++-- | Lift a @pass@ operation to the new monad.+liftPass :: (Monad m) => Pass w m (a, g) -> Pass w (RandT g m) a+liftPass pass_ m = RandT $ LazyState.liftPass pass_ (unRandT m)++-- | Evaluate a random computation in the `IO` monad, splitting the global+-- standard generator to get a new one for the computation.+evalRandIO :: Rand StdGen a -> IO a+evalRandIO t = liftM (evalRand t) newStdGen++-- | Evaluate a random computation that is embedded in the `IO` monad,+-- splitting the global standard generator to get a new one for the+-- computation.+evalRandTIO :: (MonadIO m) => RandT StdGen m a -> m a+evalRandTIO t = liftIO newStdGen >>= evalRandT t++#if MIN_VERSION_random(1,2,0)+-- |+--+-- @since 0.5.3+instance (Monad m, RandomGen g) => StatefulGen (RandGen g) (RandT g m) where+ uniformWord32R r = applyRandT (genWord32R r)+ uniformWord64R r = applyRandT (genWord64R r)+ uniformWord8 = applyRandT genWord8+ uniformWord16 = applyRandT genWord16+ uniformWord32 = applyRandT genWord32+ uniformWord64 = applyRandT genWord64+#if MIN_VERSION_random(1,3,0)+ uniformByteArrayM pinned sz = applyRandT $ uniformByteArray pinned sz+#else+ uniformShortByteString n = applyRandT (genShortByteString n)+#endif++-- |+--+-- @since 0.5.3+instance (Monad m, RandomGen g) => RandomGenM (RandGen g) g (RandT g m) where+ applyRandomGenM = applyRandT++applyRandT :: Applicative m => (g -> (a, g)) -> RandGen g -> RandT g m a+applyRandT f _ = liftRandT (pure . f)+#endif++-- | A `RandT` runner that allows using it with `StatefulGen` restricted actions. Returns+-- the outcome of random computation and the new pseudo-random-number generator+--+-- >>> withRandGen (mkStdGen 2021) uniformM :: IO (Int, StdGen)+-- (6070831465987696718,StdGen {unStdGen = SMGen 4687568268719557181 4805600293067301895})+--+-- @since 0.5.3+withRandGen ::+ g+ -- ^ initial generator+ -> (RandGen g -> RandT g m a)+ -> m (a, g)+ -- ^ return value and final generator+withRandGen g action = runRandT (action RandGen) g++-- | Same as `withRandGen`, but discards the resulting generator.+--+-- >>> withRandGen_ (mkStdGen 2021) uniformM :: IO Int+-- 6070831465987696718+--+-- @since 0.5.3+withRandGen_ ::+ Monad m+ => g+ -- ^ initial generator+ -> (RandGen g -> RandT g m a)+ -> m a+ -- ^ return value and final generator+withRandGen_ g action = evalRandT (action RandGen) g+++{- $examples++The @die@ function simulates the roll of a die, picking a number between 1+and 6, inclusive, and returning it in the 'Rand' monad transformer. Notice+that this code will work with any random number generator @g@.++> die :: (RandomGen g) => Rand g Int+> die = getRandomR (1, 6)++The @dice@ function uses @replicate@ and @sequence@ to simulate the roll of+@n@ dice.++> dice :: (RandomGen g) => Int -> Rand g [Int]+> dice n = sequence (replicate n die)++To extract a value from the 'Rand' monad transformer, we can use 'evalRandIO'.++> main = do+> values <- evalRandIO (dice 2)+> putStrLn (show values)++-}
+ Control/Monad/Trans/Random/Strict.hs view
@@ -0,0 +1,360 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE Trustworthy #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE UndecidableInstances #-}++{- |+Module : Control.Monad.Trans.Random.Strict+Copyright : (c) Brent Yorgey 2016+License : BSD3 (see LICENSE)++Maintainer : byorgey@gmail.com+Stability : experimental+Portability : non-portable (multi-param classes, functional dependencies, undecidable instances)++Strict random monads, passing a random number generator through a computation.+See below for examples.++In this version, sequencing of computations is strict (but computations are not+strict in the state unless you force it with seq or the like). For a lazy+version with the same interface, see "Control.Monad.Trans.Random.Lazy".+-}++module Control.Monad.Trans.Random.Strict+ ( -- * The Rand monad transformer+ Rand,+ liftRand,+ runRand,+ evalRand,+ execRand,+ mapRand,+ withRand,+ evalRandIO,+ -- * The RandT monad transformer+ RandT,+ liftRandT,+ runRandT,+ evalRandT,+ execRandT,+ mapRandT,+ withRandT,+ evalRandTIO,+ -- * Lifting other operations+ liftCallCC,+ liftCallCC',+ liftCatch,+ liftListen,+ liftPass,+ -- * StatefulGen interface+ RandGen(..),+ withRandGen,+ withRandGen_,+ -- * Examples+ -- ** Random monads+ -- $examples+ ) where++import Control.Applicative ( Alternative )+import Control.Arrow (first)+import Control.Monad ( liftM, MonadPlus )+import Control.Monad.Cont.Class (MonadCont(..))+import Control.Monad.Error.Class ( MonadError(..) )+import qualified Control.Monad.Fail as Fail+import Control.Monad.Fix ( MonadFix )+import Control.Monad.IO.Class ( MonadIO(..) )+import Control.Monad.Primitive ( PrimMonad(..) )+import Control.Monad.Random.Class ( MonadInterleave(..), MonadSplit(..), MonadRandom(..) )+import Control.Monad.RWS.Class ( MonadState(..), MonadRWS, MonadReader, MonadWriter )+import Control.Monad.Signatures ( Listen, Pass, CallCC, Catch )+import Control.Monad.Trans.Class ( MonadTrans(..) )+import qualified Control.Monad.Trans.State.Strict as StrictState+import Data.Functor.Identity ( Identity(runIdentity) )+#if MIN_VERSION_random(1,2,0)+import System.Random.Stateful+#else+import System.Random+#endif++-- | A random monad parameterized by the type @g@ of the generator to carry.+--+-- The 'return' function leaves the generator unchanged, while '>>=' uses the+-- final generator of the first computation as the initial generator of the+-- second.+type Rand g = RandT g Identity++-- | Construct a random monad computation from a function.+-- (The inverse of 'runRand'.)+liftRand+ :: (g -> (a, g))+ -- ^ pure random transformer+ -> Rand g a+ -- ^ equivalent generator-passing computation+liftRand = RandT . state++-- | Unwrap a random monad computation as a function.+-- (The inverse of 'liftRand'.)+runRand+ :: Rand g a+ -- ^ generator-passing computation to execute+ -> g+ -- ^ initial generator+ -> (a, g)+ -- ^ return value and final generator+runRand t = runIdentity . runRandT t++-- | Evaluate a random computation with the given initial generator and return+-- the final value, discarding the final generator.+--+-- * @'evalRand' m s = fst ('runRand' m s)@+evalRand+ :: Rand g a+ -- ^ generator-passing computation to execute+ -> g+ -- ^ initial generator+ -> a+ -- ^ return value of the random computation+evalRand t = runIdentity . evalRandT t++-- | Evaluate a random computation with the given initial generator and return+-- the final generator, discarding the final value.+--+-- * @'execRand' m s = snd ('runRand' m s)@+execRand+ :: Rand g a+ -- ^ generator-passing computation to execute+ -> g+ -- ^ initial generator+ -> g+ -- ^ final generator+execRand t = runIdentity . execRandT t++-- | Map both the return value and final generator of a computation using the+-- given function.+--+-- * @'runRand' ('mapRand' f m) = f . 'runRand' m@+mapRand :: ((a, g) -> (b, g)) -> Rand g a -> Rand g b+mapRand f = mapRandT (liftM f)++-- | @'withRand' f m@ executes action @m@ on a generator modified by applying @f@.+--+-- * @'withRand' f m = 'modify' f >> m@+withRand :: (g -> g) -> Rand g a -> Rand g a+withRand = withRandT++-- | A random transformer monad parameterized by:+--+-- * @g@ - The generator.+--+-- * @m@ - The inner monad.+--+-- The 'return' function leaves the generator unchanged, while '>>=' uses the+-- final generator of the first computation as the initial generator of the+-- second.+newtype RandT g m a = RandT { unRandT :: StrictState.StateT g m a }+ deriving (Functor, Applicative, Alternative, Monad, MonadPlus, MonadTrans, MonadIO, MonadFix, MonadReader r, MonadWriter w)++-- | Construct a random monad computation from an impure function.+-- (The inverse of 'runRandT'.)+liftRandT+ :: (g -> m (a, g))+ -- ^ impure random transformer+ -> RandT g m a+ -- ^ equivalent generator-passing computation+liftRandT = RandT . StrictState.StateT++-- | Unwrap a random monad computation as an impure function.+-- (The inverse of 'liftRandT'.)+runRandT+ :: RandT g m a+ -- ^ generator-passing computation to execute+ -> g+ -- ^ initial generator+ -> m (a, g)+ -- ^ return value and final generator+runRandT = StrictState.runStateT . unRandT++-- | Evaluate a random computation with the given initial generator and return+-- the final value, discarding the final generator.+--+-- * @'evalRandT' m g = liftM fst ('runRandT' m g)@+evalRandT :: (Monad m) => RandT g m a -> g -> m a+evalRandT = StrictState.evalStateT . unRandT++-- | Evaluate a random computation with the given initial generator and return+-- the final generator, discarding the final value.+--+-- * @'execRandT' m g = liftM snd ('runRandT' m g)@+execRandT :: (Monad m) => RandT g m a -> g -> m g+execRandT = StrictState.execStateT . unRandT++-- | Map both the return value and final generator of a computation using the+-- given function.+--+-- * @'runRandT' ('mapRandT' f m) = f . 'runRandT' m@+mapRandT :: (m (a, g) -> n (b, g)) -> RandT g m a -> RandT g n b+mapRandT f = RandT . StrictState.mapStateT f . unRandT++-- | @'withRandT' f m@ executes action @m@ on a generator modified by applying @f@.+--+-- * @'withRandT' f m = 'modify' f >> m@+withRandT :: (g -> g) -> RandT g m a -> RandT g m a+withRandT f = RandT . StrictState.withStateT f . unRandT++instance (MonadCont m) => MonadCont (RandT g m) where+ callCC = liftCallCC' callCC++instance (MonadError e m) => MonadError e (RandT g m) where+ throwError = lift . throwError+ catchError = liftCatch catchError++instance (MonadReader r m, MonadWriter w m, MonadState s m) => MonadRWS r w s (RandT g m)++instance (RandomGen g, Monad m) => MonadRandom (RandT g m) where+ getRandomR lohi = RandT . state $ randomR lohi+ getRandom = RandT . state $ random+ getRandomRs lohi = RandT . state $ first (randomRs lohi) . split+ getRandoms = RandT . state $ first randoms . split++instance (RandomGen g, Monad m) => MonadSplit g (RandT g m) where+ getSplit = RandT . state $ split++instance (Monad m, RandomGen g) => MonadInterleave (RandT g m) where+ interleave (RandT m) = liftRandT $ \g -> case split g of+ (gl, gr) -> liftM (\p -> (fst p, gr)) $ StrictState.runStateT m gl++instance (MonadState s m) => MonadState s (RandT g m) where+ get = lift get+ put = lift . put++instance PrimMonad m => PrimMonad (RandT s m) where+ type PrimState (RandT s m) = PrimState m+ primitive = lift . primitive++instance Fail.MonadFail m => Fail.MonadFail (RandT g m) where+ fail = lift . Fail.fail++-- | Uniform lifting of a @callCC@ operation to the new monad.+-- This version rolls back to the original state on entering the+-- continuation.+liftCallCC :: CallCC m (a, g) (b, g) -> CallCC (RandT g m) a b+liftCallCC callCC_ f = RandT $ StrictState.liftCallCC callCC_ $ \c -> unRandT (f (RandT . c))++-- | In-situ lifting of a @callCC@ operation to the new monad.+-- This version uses the current state on entering the continuation.+-- It does not satisfy the uniformity property (see "Control.Monad.Signatures").+liftCallCC' :: CallCC m (a, g) (b, g) -> CallCC (RandT g m) a b+liftCallCC' callCC_ f = RandT $ StrictState.liftCallCC' callCC_ $ \c -> unRandT (f (RandT . c))++-- | Lift a @catchE@ operation to the new monad.+liftCatch :: Catch e m (a, g) -> Catch e (RandT g m) a+liftCatch catchE_ m f = RandT $ StrictState.liftCatch catchE_ (unRandT m) (unRandT . f)++-- | Lift a @listen@ operation to the new monad.+liftListen :: (Monad m) => Listen w m (a, g) -> Listen w (RandT g m) a+liftListen listen_ m = RandT $ StrictState.liftListen listen_ (unRandT m)++-- | Lift a @pass@ operation to the new monad.+liftPass :: (Monad m) => Pass w m (a, g) -> Pass w (RandT g m) a+liftPass pass_ m = RandT $ StrictState.liftPass pass_ (unRandT m)++-- | Evaluate a random computation in the `IO` monad, splitting the global+-- standard generator to get a new one for the computation.+evalRandIO :: Rand StdGen a -> IO a+evalRandIO t = liftM (evalRand t) newStdGen++-- | Evaluate a random computation that is embedded in the `IO` monad,+-- splitting the global standard generator to get a new one for the+-- computation.+evalRandTIO :: (MonadIO m) => RandT StdGen m a -> m a+evalRandTIO t = liftIO newStdGen >>= evalRandT t+++-- | A proxy that carries information about the type of generator to use with @RandT@+-- monad and its `StatefulGen` instance.+--+-- @since 0.5.3+data RandGen g = RandGen++#if MIN_VERSION_random(1,2,0)+-- |+--+-- @since 0.5.3+instance (Monad m, RandomGen g) => StatefulGen (RandGen g) (RandT g m) where+ uniformWord32R r = applyRandT (genWord32R r)+ uniformWord64R r = applyRandT (genWord64R r)+ uniformWord8 = applyRandT genWord8+ uniformWord16 = applyRandT genWord16+ uniformWord32 = applyRandT genWord32+ uniformWord64 = applyRandT genWord64+#if MIN_VERSION_random(1,3,0)+ uniformByteArrayM pinned sz = applyRandT $ uniformByteArray pinned sz+#else+ uniformShortByteString n = applyRandT (genShortByteString n)+#endif++-- |+--+-- @since 0.5.3+instance (Monad m, RandomGen g) => RandomGenM (RandGen g) g (RandT g m) where+ applyRandomGenM = applyRandT++applyRandT :: Applicative m => (g -> (a, g)) -> RandGen g -> RandT g m a+applyRandT f _ = liftRandT (pure . f)+#endif++-- | A `RandT` runner that allows using it with `StatefulGen` restricted actions. Returns+-- the outcome of random computation and the new pseudo-random-number generator+--+-- >>> withRandGen (mkStdGen 2021) uniformM :: IO (Int, StdGen)+-- (6070831465987696718,StdGen {unStdGen = SMGen 4687568268719557181 4805600293067301895})+--+-- @since 0.5.3+withRandGen ::+ g+ -- ^ initial generator+ -> (RandGen g -> RandT g m a)+ -> m (a, g)+ -- ^ return value and final generator+withRandGen g action = runRandT (action RandGen) g++-- | Same as `withRandGen`, but discards the resulting generator.+--+-- >>> withRandGen_ (mkStdGen 2021) uniformM :: IO Int+-- 6070831465987696718+--+-- @since 0.5.3+withRandGen_ ::+ Monad m+ => g+ -- ^ initial generator+ -> (RandGen g -> RandT g m a)+ -> m a+ -- ^ return value and final generator+withRandGen_ g action = evalRandT (action RandGen) g+++{- $examples++The @die@ function simulates the roll of a die, picking a number between 1+and 6, inclusive, and returning it in the 'Rand' monad transformer. Notice+that this code will work with any random number generator @g@.++> die :: (RandomGen g) => Rand g Int+> die = getRandomR (1, 6)++The @dice@ function uses @replicate@ and @sequence@ to simulate the roll of+@n@ dice.++> dice :: (RandomGen g) => Int -> Rand g [Int]+> dice n = sequence (replicate n die)++To extract a value from the 'Rand' monad transformer, we can use 'evalRandIO'.++> main = do+> values <- evalRandIO (dice 2)+> putStrLn (show values)++-}
LICENSE view
@@ -1,38 +1,34 @@-MonadRandom library.-Copyright 2006-2007 Cale Gibbard. All rights reserved.-Copyright 2006 Russell O'Connor, Dan Doel, Remi Turk. All rights reserved.-Copyright 2007 Eric Kidd. All rights reserved.--[This code was originally posted by Cale Gibbard to the Haskell Wiki,-remnants of which can be seen at:--http://web.archive.org/web/20070615071737/http://haskell.org/hawiki/MonadRandom--This code was relicensed from a 2-clause BSD license to the license below-by Cale Gibbard's statement at:--http://www.haskell.org/haskellwiki/User:CaleGibbard+Copyright (c) 2016, Brent Yorgey -The code was then moved to:+All rights reserved. -http://www.haskell.org/haskellwiki/NewMonads/MonadRandom+Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met: -New additions by several people were provided under the implicit license at:+ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer. -http://www.haskell.org/haskellwiki/HaskellWiki:Copyrights+ * Redistributions in binary form must reproduce the above+ copyright notice, this list of conditions and the following+ disclaimer in the documentation and/or other materials provided+ with the distribution. -So, in theory, this license should apply to everything in this library.]+ * Neither the name of Brent Yorgey nor the names of other+ contributors may be used to endorse or promote products derived+ from this software without specific prior written permission. -Permission is hereby granted, free of charge, to any person obtaining this-work (the "Work"), to deal in the Work without restriction, including-without limitation the rights to use, copy, modify, merge, publish,-distribute, sublicense, and/or sell copies of the Work, and to permit-persons to whom the Work is furnished to do so.+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -THE WORK IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING-FROM, OUT OF OR IN CONNECTION WITH THE WORK OR THE USE OR OTHER DEALINGS IN-THE WORK.+Previous versions of this package were distributed under the simple+permissive license used on the Haskell Wiki; see OLD-LICENSE for+details.
MonadRandom.cabal view
@@ -1,8 +1,8 @@ name: MonadRandom-version: 0.3.0.2+version: 0.6.2.1 synopsis: Random-number generation monad. description: Support for computations which consume random values.-license: OtherLicense+license: BSD3 license-file: LICENSE author: Cale Gibbard and others maintainer: Brent Yorgey <byorgey@gmail.com>@@ -11,12 +11,27 @@ build-type: Simple cabal-version: >=1.10 extra-source-files: CHANGES.markdown+tested-with: GHC ==8.0.2 || ==8.2.2 || ==8.4.4 || ==8.6.5 || ==8.8.4 || ==8.10.7 || ==9.0.2 || ==9.2.8 || ==9.4.8 || ==9.6.6 || ==9.8.2 || ==9.10.1 || ==9.12.1 || ==9.14.1+ source-repository head type: git- location: git://github.com/byorgey/MonadRandom.git+ location: https://github.com/byorgey/MonadRandom library- exposed-modules: Control.Monad.Random, Control.Monad.Random.Class- build-depends: base >= 2 && < 5, transformers >= 0.3 && < 0.5, transformers-compat == 0.4.*, mtl, random+ exposed-modules:+ Control.Monad.Random,+ Control.Monad.Random.Class,+ Control.Monad.Random.Lazy,+ Control.Monad.Random.Strict,+ Control.Monad.Trans.Random,+ Control.Monad.Trans.Random.Lazy,+ Control.Monad.Trans.Random.Strict+ build-depends:+ base >=4.8 && <5,+ transformers >=0.5 && <0.7,+ mtl >=2.2.1 && <2.3 || >= 2.3.1 && < 2.4,+ primitive >=0.6 && <0.10,+ random >=1.0.1 && <1.4 ghc-options: -Wall default-language: Haskell2010+ other-extensions: Safe