packages feed

MonadRandom 0.6 → 0.6.1

raw patch · 3 files changed

+174/−143 lines, 3 filesPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

API changes (from Hackage documentation)

- Control.Monad.Random.Class: class (Monad m) => MonadRandom m
+ Control.Monad.Random.Class: class Monad m => MonadRandom m
- Control.Monad.Random.Class: class (Monad m) => MonadSplit g m | m -> g
+ Control.Monad.Random.Class: class Monad m => MonadSplit g m | m -> g
- Control.Monad.Random.Lazy: class Finite a
+ Control.Monad.Random.Lazy: class () => Finite a
- Control.Monad.Random.Lazy: class Random a
+ Control.Monad.Random.Lazy: class () => Random a
- Control.Monad.Random.Lazy: class RandomGen g
+ Control.Monad.Random.Lazy: class () => RandomGen g
- Control.Monad.Random.Lazy: class Uniform a
+ Control.Monad.Random.Lazy: class () => Uniform a
- Control.Monad.Random.Lazy: class UniformRange a
+ Control.Monad.Random.Lazy: class () => UniformRange a
- Control.Monad.Random.Lazy: data StdGen
+ Control.Monad.Random.Lazy: data () => StdGen
- Control.Monad.Random.Strict: class Finite a
+ Control.Monad.Random.Strict: class () => Finite a
- Control.Monad.Random.Strict: class Random a
+ Control.Monad.Random.Strict: class () => Random a
- Control.Monad.Random.Strict: class RandomGen g
+ Control.Monad.Random.Strict: class () => RandomGen g
- Control.Monad.Random.Strict: class Uniform a
+ Control.Monad.Random.Strict: class () => Uniform a
- Control.Monad.Random.Strict: class UniformRange a
+ Control.Monad.Random.Strict: class () => UniformRange a
- Control.Monad.Random.Strict: data StdGen
+ Control.Monad.Random.Strict: data () => StdGen

Files

CHANGES.markdown view
@@ -1,3 +1,15 @@+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) ---------------- @@ -5,12 +17,17 @@ - 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`-- Additioon of `withRandGen` and `withRandGen_`+- Addition of `withRandGen` and `withRandGen_`  - r1 (28 April 2021): require `base >= 4.6` - r2 (9 Aug 2021): allow `transformers-compat-0.7`.
Control/Monad/Random/Class.hs view
@@ -1,72 +1,68 @@-{-# LANGUAGE CPP                    #-}-{-# LANGUAGE FlexibleInstances      #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FunctionalDependencies #-}-{-# LANGUAGE MultiParamTypeClasses  #-}-{-# LANGUAGE Safe                   #-}-{-# LANGUAGE UndecidableInstances   #-}--{- |-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.---}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE Safe #-}+{-# LANGUAGE UndecidableInstances #-} +-- |+-- 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(..),+  -- * MonadRandom+  MonadRandom (..), -    -- * MonadSplit-    MonadSplit(..),+  -- * MonadSplit+  MonadSplit (..), -    -- * MonadInterleave-    MonadInterleave(..),+  -- * MonadInterleave+  MonadInterleave (..), -    -- * Sampling functions-    fromList,-    fromListMay,-    uniform,-    uniformMay,-    weighted,-    weightedMay-    ) where+  -- * Sampling functions+  fromList,+  fromListMay,+  uniform,+  uniformMay,+  weighted,+  weightedMay,+) 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           Control.Monad.Trans.Reader-import qualified Control.Monad.Trans.RWS.Lazy      as LazyRWS-import qualified Control.Monad.Trans.RWS.Strict    as StrictRWS-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 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 System.Random                     as Random--import qualified Data.Foldable                     as F+import qualified Data.Foldable as F+import Data.Word (Word64)+import qualified System.Random as Random  #if MIN_VERSION_base(4,8,0) #else@@ -79,7 +75,7 @@  -- | 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+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@@ -89,7 +85,7 @@   -- interval.   --   -- See 'System.Random.randomR' for details.-  getRandomR :: (Random.Random a) => (a, a) -> m a+  getRandomR :: Random.Random a => (a, a) -> m a    -- | The same as 'getRandomR', but using a default range determined by the type:   --@@ -102,91 +98,91 @@   -- * For 'Integer', the range is (arbitrarily) the range of 'Int'.   --   -- See 'System.Random.random' for details.-  getRandom :: (Random.Random a) => m a+  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]+  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]+  getRandoms :: Random.Random a => m [a]  instance MonadRandom IO where-  getRandomR       = Random.randomRIO-  getRandom        = Random.randomIO+  getRandomR = Random.randomRIO+  getRandom = Random.randomIO   getRandomRs lohi = liftM (Random.randomRs lohi) Random.newStdGen-  getRandoms       = liftM Random.randoms Random.newStdGen+  getRandoms = liftM Random.randoms Random.newStdGen -instance (MonadRandom m) => MonadRandom (ContT r m) where-  getRandomR  = lift . getRandomR-  getRandom   = lift getRandom+instance MonadRandom m => MonadRandom (ContT r m) where+  getRandomR = lift . getRandomR+  getRandom = lift getRandom   getRandomRs = lift . getRandomRs-  getRandoms  = lift getRandoms+  getRandoms = lift getRandoms -instance (MonadRandom m) => MonadRandom (ExceptT e m) where-  getRandomR  = lift . getRandomR-  getRandom   = lift getRandom+instance MonadRandom m => MonadRandom (ExceptT e m) where+  getRandomR = lift . getRandomR+  getRandom = lift getRandom   getRandomRs = lift . getRandomRs-  getRandoms  = lift getRandoms+  getRandoms = lift getRandoms -instance (MonadRandom m) => MonadRandom (IdentityT m) where-  getRandomR  = lift . getRandomR-  getRandom   = lift getRandom+instance MonadRandom m => MonadRandom (IdentityT m) where+  getRandomR = lift . getRandomR+  getRandom = lift getRandom   getRandomRs = lift . getRandomRs-  getRandoms  = lift getRandoms+  getRandoms = lift getRandoms -instance (MonadRandom m) => MonadRandom (MaybeT m) where-  getRandomR  = lift . getRandomR-  getRandom   = lift getRandom+instance MonadRandom m => MonadRandom (MaybeT m) where+  getRandomR = lift . getRandomR+  getRandom = lift getRandom   getRandomRs = lift . getRandomRs-  getRandoms  = lift getRandoms+  getRandoms = lift getRandoms  instance (Monoid w, MonadRandom m) => MonadRandom (LazyRWS.RWST r w s m) where-  getRandomR  = lift . getRandomR-  getRandom   = lift getRandom+  getRandomR = lift . getRandomR+  getRandom = lift getRandom   getRandomRs = lift . getRandomRs-  getRandoms  = lift getRandoms+  getRandoms = lift getRandoms  instance (Monoid w, MonadRandom m) => MonadRandom (StrictRWS.RWST r w s m) where-  getRandomR  = lift . getRandomR-  getRandom   = lift getRandom+  getRandomR = lift . getRandomR+  getRandom = lift getRandom   getRandomRs = lift . getRandomRs-  getRandoms  = lift getRandoms+  getRandoms = lift getRandoms -instance (MonadRandom m) => MonadRandom (ReaderT r m) where-  getRandomR  = lift . getRandomR-  getRandom   = lift getRandom+instance MonadRandom m => MonadRandom (ReaderT r m) where+  getRandomR = lift . getRandomR+  getRandom = lift getRandom   getRandomRs = lift . getRandomRs-  getRandoms  = lift getRandoms+  getRandoms = lift getRandoms -instance (MonadRandom m) => MonadRandom (LazyState.StateT s m) where-  getRandomR  = lift . getRandomR-  getRandom   = lift getRandom+instance MonadRandom m => MonadRandom (LazyState.StateT s m) where+  getRandomR = lift . getRandomR+  getRandom = lift getRandom   getRandomRs = lift . getRandomRs-  getRandoms  = lift getRandoms+  getRandoms = lift getRandoms -instance (MonadRandom m) => MonadRandom (StrictState.StateT s m) where-  getRandomR  = lift . getRandomR-  getRandom   = lift getRandom+instance MonadRandom m => MonadRandom (StrictState.StateT s m) where+  getRandomR = lift . getRandomR+  getRandom = lift getRandom   getRandomRs = lift . getRandomRs-  getRandoms  = lift getRandoms+  getRandoms = lift getRandoms  instance (MonadRandom m, Monoid w) => MonadRandom (LazyWriter.WriterT w m) where-  getRandomR  = lift . getRandomR-  getRandom   = lift getRandom+  getRandomR = lift . getRandomR+  getRandom = lift getRandom   getRandomRs = lift . getRandomRs-  getRandoms  = lift getRandoms+  getRandoms = lift getRandoms  instance (MonadRandom m, Monoid w) => MonadRandom (StrictWriter.WriterT w m) where-  getRandomR  = lift . getRandomR-  getRandom   = lift getRandom+  getRandomR = lift . getRandomR+  getRandom = lift getRandom   getRandomRs = lift . getRandomRs-  getRandoms  = lift getRandoms+  getRandoms = lift getRandoms  ------------------------------------------------------------ -- MonadSplit@@ -199,8 +195,7 @@ --   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-+class Monad m => MonadSplit g m | m -> g where   -- | The 'getSplit' operation allows one to obtain two distinct random number   -- generators.   --@@ -210,16 +205,16 @@ instance MonadSplit Random.StdGen IO where   getSplit = Random.newStdGen -instance (MonadSplit g m) => MonadSplit g (ContT r m) where+instance MonadSplit g m => MonadSplit g (ContT r m) where   getSplit = lift getSplit -instance (MonadSplit g m) => MonadSplit g (ExceptT e m) where+instance MonadSplit g m => MonadSplit g (ExceptT e m) where   getSplit = lift getSplit -instance (MonadSplit g m) => MonadSplit g (IdentityT m) where+instance MonadSplit g m => MonadSplit g (IdentityT m) where   getSplit = lift getSplit -instance (MonadSplit g m) => MonadSplit g (MaybeT m) where+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@@ -228,13 +223,13 @@ 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+instance MonadSplit g m => MonadSplit g (ReaderT r m) where   getSplit = lift getSplit -instance (MonadSplit g m) => MonadSplit g (LazyState.StateT s m) where+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+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@@ -250,7 +245,6 @@ -- | 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@@ -295,16 +289,16 @@   --   > Node 8243316398511136358 (Node 4139784028141790719 Leaf Leaf) (Node 4473998613878251948 Leaf Leaf)   interleave :: m a -> m a -instance (MonadInterleave m) => MonadInterleave (ContT r m) where+instance MonadInterleave m => MonadInterleave (ContT r m) where   interleave = mapContT interleave -instance (MonadInterleave m) => MonadInterleave (ExceptT e m) where+instance MonadInterleave m => MonadInterleave (ExceptT e m) where   interleave = mapExceptT interleave -instance (MonadInterleave m) => MonadInterleave (IdentityT m) where+instance MonadInterleave m => MonadInterleave (IdentityT m) where   interleave = mapIdentityT interleave -instance (MonadInterleave m) => MonadInterleave (MaybeT m) where+instance MonadInterleave m => MonadInterleave (MaybeT m) where   interleave = mapMaybeT interleave  instance (Monoid w, MonadInterleave m) => MonadInterleave (LazyRWS.RWST r w s m) where@@ -313,13 +307,13 @@ 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+instance MonadInterleave m => MonadInterleave (ReaderT r m) where   interleave = mapReaderT interleave -instance (MonadInterleave m) => MonadInterleave (LazyState.StateT s m) where+instance MonadInterleave m => MonadInterleave (LazyState.StateT s m) where   interleave = LazyState.mapStateT interleave -instance (MonadInterleave m) => MonadInterleave (StrictState.StateT s m) where+instance MonadInterleave m => MonadInterleave (StrictState.StateT s m) where   interleave = StrictState.mapStateT interleave  instance (Monoid w, MonadInterleave m) => MonadInterleave (LazyWriter.WriterT w m) where@@ -340,7 +334,7 @@   ma <- weightedMay t   case ma of     Nothing -> error "Control.Monad.Random.Class.weighted: empty collection, or total weight <= 0"-    Just a  -> return a+    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@@ -350,23 +344,28 @@  -- | 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 :: 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+    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 :: MonadRandom m => [(a, Rational)] -> m (Maybe a) fromListMay xs = do-  let s    = fromRational (sum (map snd xs)) :: Double-      cums = scanl1 (\ ~(_,q) ~(y,s') -> (y, s'+q)) xs-  case s <= 0 of-    True -> return Nothing-    _    -> do-      p <- liftM toRational $ getRandomR (0, s)+  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 . Just . fst . head . dropWhile ((< p) . snd) $ cums  -- | Sample a value uniformly from a nonempty collection of elements.@@ -375,9 +374,24 @@   ma <- uniformMay t   case ma of     Nothing -> error "Control.Monad.Random.Class.uniform: empty collection"-    Just a  -> return a+    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 . Just . fst . head . dropWhile ((< p) . snd) $ cums
MonadRandom.cabal view
@@ -1,5 +1,5 @@ name:                MonadRandom-version:             0.6+version:             0.6.1 synopsis:            Random-number generation monad. description:         Support for computations which consume random values. license:             BSD3@@ -11,7 +11,7 @@ build-type:          Simple cabal-version:       >=1.10 extra-source-files:  CHANGES.markdown-tested-with:         GHC ==7.10.3 || ==8.0.2 || ==8.2.2 || ==8.4.4 || ==8.6.5 || ==8.8.4 || ==8.10.7 || ==9.0.2 || ==9.2.4 || ==9.4.2+tested-with:         GHC ==7.10.3 || ==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.5 || ==9.8.2 || ==9.10.1  source-repository head   type:     git@@ -27,12 +27,12 @@     Control.Monad.Trans.Random.Lazy,     Control.Monad.Trans.Random.Strict   build-depends:-    base                >=4.6 && <5,+    base                >=4.8 && <5,     transformers        >=0.4 && <0.7,     transformers-compat >=0.4 && <0.8,     mtl                 >=2.2.1 && <2.3 || >= 2.3.1 && < 2.4,-    primitive           >=0.6 && <0.8,-    random              >=1.0 && <1.3+    primitive           >=0.6 && <0.10,+    random              >=1.0.1 && <1.3   ghc-options:         -Wall   default-language:    Haskell2010   other-extensions:    Safe