<html><head><meta http-equiv="Content-Type" content="text/html; charset=us-ascii"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;" class=""><p class="">Hello Libraries,</p><p class="">You may recall that following the blog <a href="https://alexey.kuleshevi.ch/blog/2019/12/21/random-benchmarks/" class="">post</a> by @lehins, a group of us
(@curiousleo, @lehins and me) invited participation in <a href="https://mail.haskell.org/pipermail/libraries/2020-February/030261.html" class="">February</a> to
take this work and apply it to improving the current <code class="">random</code> library.</p><p class="">Our proximate goals were to fix <a href="https://github.com/haskell/random/issues/25" class="">#25</a> (filed in 2015) and <a href="https://github.com/haskell/random/issues/51" class="">#51</a> (filed in
2018). After a lot of discussion and experimentation, we have a
proposal that addresses both these issues and also: <a href="https://github.com/haskell/random/issues/26" class="">#26</a>, <a href="https://github.com/haskell/random/issues/44" class="">#44</a>, <a href="https://github.com/haskell/random/issues/53" class="">#53</a>,
<a href="https://github.com/haskell/random/issues/55" class="">#55</a>, <a href="https://github.com/haskell/random/issues/58" class="">#58</a> and <a href="https://github.com/haskell/random/issues/59" class="">#59</a>.</p><p class="">For backwards compatibility, the proposal retains the old style
classes and enhances them. Thus in 1.1 we have</p>
<pre class=""><code class="">class RandomGen g where
next :: g -> (Int, g)
genRange :: g -> (Int, Int)
split :: g -> (g, g)
{-# MINIMAL next, split #-}
</code></pre><p class="">and in 1.2 we have</p>
<pre class=""><code class="">class RandomGen g where
next :: g -> (Int, g)
genWord8 :: g -> (Word8, g)
genWord16 :: g -> (Word16, g)
genWord32 :: g -> (Word32, g)
genWord64 :: g -> (Word64, g)
genWord32R :: Word32 -> g -> (Word32, g)
genWord64R :: Word64 -> g -> (Word64, g)
genShortByteString :: Int
-> g -> (Data.ByteString.Short.Internal.ShortByteString, g)
genRange :: g -> (Int, Int)
split :: g -> (g, g)
{-# MINIMAL split, (genWord32 | genWord64 | next, genRange) #-}
</code></pre><p class="">and <code class="">next</code> and <code class="">genRange</code> are deprecated. This interface is what
allows the significantly faster performance as no longer is everything
forced to go via <code class="">Integer</code>.</p><p class="">Several new interfaces are introduced and it is recommended that new
applications use these and, where feasible, existing applications
migrate to using them.</p><p class="">The major API addition in this PR is the definition of a new class <code class="">MonadRandom</code>:</p>
<pre class=""><code class="">-- | 'MonadRandom' is an interface to monadic pseudo-random number generators.
class Monad m => MonadRandom g s m | g m -> s where
{-# MINIMAL freezeGen,thawGen,(uniformWord32|uniformWord64) #-}
type Frozen g = (f :: Type) | f -> g
freezeGen :: g s -> m (Frozen g)
thawGen :: Frozen g -> m (g s)
uniformWord32 :: g s -> m Word32 -- default implementation in terms of uniformWord64
uniformWord64 :: g s -> m Word64 -- default implementation in terms of uniformWord32
-- plus methods for other word sizes and for byte strings
-- all have default implementations so the MINIMAL pragma holds
</code></pre><p class="">Conceptually, in <code class="">MonadRandom g s m</code>, <code class="">g s</code> is the type of the
generator, <code class="">s</code> is the state type, and <code class="">m</code> the underlying monad. Via
the functional dependency <code class="">g m -> s</code>, the state type is determined by
the generator and monad.</p><p class=""><code class="">Frozen</code> is the type of the generator's state "at rest". It is defined
as an injective type family via <code class="">f -> g</code>, so there is no ambiguity as
to which <code class="">g</code> any <code class="">Frozen g</code> belongs to.</p><p class="">This definition is generic enough to accommodate, for example, the
<code class="">Gen</code> type from <code class="">mwc-random</code>, which itself abstracts over the
underlying primitive monad and state token. This is the full instance
declaration (provided here as an example - this instance is not part
of <code class="">random</code> as <code class="">random</code> does not depend on <code class="">mwc-random</code>):</p>
<pre class=""><code class="">instance (s ~ PrimState m, PrimMonad m) => MonadRandom MWC.Gen s m where
type Frozen MWC.Gen = MWC.Seed
freezeGen = MWC.save
thawGen = MWC.restore
uniformWord8 = MWC.uniform
uniformWord16 = MWC.uniform
uniformWord32 = MWC.uniform
uniformWord64 = MWC.uniform
uniformShortByteString n g = unsafeSTToPrim (genShortByteStringST n (MWC.uniform g))
</code></pre><p class="">Pure random number generators can also be made instances of this class
providing a uniform interface to both pure and stateful random number
generators. An instance for the standard number generator <code class="">StdGen</code> is
provided.</p><p class="">The <code class="">Random</code> typeclass has conceptually been split into <code class="">Uniform</code> and
<code class="">UniformRange</code>. The <code class="">Random</code> typeclass is still included for backwards
compatibility. <code class="">Uniform</code> is for types where it is possible to sample
from the type's entire domain; <code class="">UniformRange</code> is for types where one
can sample from a specified range:</p>
<pre class=""><code class="">class Uniform a where
uniformM :: MonadRandom g s m => g s -> m a
class UniformRange a where
uniformRM :: MonadRandom g s m => (a, a) -> g s -> m a
</code></pre><p class="">The proposal is a breaking change but the changes are not very
intrusive and we have PRs ready for the affected downstream libraries:</p>
<ul class="">
<li class="">requires <code class="">base</code> >= 4.10 (GHC-8.2)</li>
<li class=""><code class="">StdGen</code> is no longer an instance of <code class="">Read</code></li>
<li class=""><code class="">randomIO</code> and <code class="">randomRIO</code> were extracted from the <code class="">Random</code> class
into separate functions</li>
</ul><p class="">In addition, there may be import clashes with new functions,
e.g. <code class="">uniform</code> and <code class="">uniformR</code>.</p><p class="">Further explanatory details may be found
<a href="https://github.com/idontgetoutmuch/random/blob/v1.2-release-notes/RELEASE-NOTES-v1.2.md#api-changes" class="">here</a>
and the PR for the proposed new version is
<a href="https://github.com/haskell/random/pull/61" class="">here</a>.</p><p class="">Here are some benchmarks run on a 3.1 GHz Intel Core i7. The full
benchmarks can be run using e.g. <code class="">stack bench</code>. The benchmarks are
measured in milliseconds per 100,000 generations. In some cases, the
performance is over x1000(!) times better; the minimum performance
increase for the types listed below is more than x35.</p>
<pre class=""><code class="">| Name | Mean (1.1) | Mean (1.2) | Improvement|
| ----------------------- | ---------- | ---------- | ---------- |
| pure/random/Float | 30 | 0.03 | 1038|
| pure/random/Double | 52 | 0.03 | 1672|
| pure/random/Integer | 43 | 0.33 | 131|
| pure/uniform/Word8 | 14 | 0.03 | 422|
| pure/uniform/Word16 | 13 | 0.03 | 375|
| pure/uniform/Word32 | 21 | 0.03 | 594|
| pure/uniform/Word64 | 42 | 0.03 | 1283|
| pure/uniform/Word | 44 | 0.03 | 1491|
| pure/uniform/Int8 | 15 | 0.03 | 511|
| pure/uniform/Int16 | 15 | 0.03 | 507|
| pure/uniform/Int32 | 22 | 0.03 | 749|
| pure/uniform/Int64 | 44 | 0.03 | 1405|
| pure/uniform/Int | 43 | 0.03 | 1512|
| pure/uniform/Char | 17 | 0.49 | 35|
| pure/uniform/Bool | 18 | 0.03 | 618|
| pure/uniform/CChar | 14 | 0.03 | 485|
| pure/uniform/CSChar | 14 | 0.03 | 455|
| pure/uniform/CUChar | 13 | 0.03 | 448|
| pure/uniform/CShort | 14 | 0.03 | 473|
| pure/uniform/CUShort | 13 | 0.03 | 457|
| pure/uniform/CInt | 21 | 0.03 | 737|
| pure/uniform/CUInt | 21 | 0.03 | 742|
| pure/uniform/CLong | 43 | 0.03 | 1544|
| pure/uniform/CULong | 42 | 0.03 | 1460|
| pure/uniform/CPtrdiff | 43 | 0.03 | 1494|
| pure/uniform/CSize | 43 | 0.03 | 1475|
| pure/uniform/CWchar | 22 | 0.03 | 785|
| pure/uniform/CSigAtomic | 21 | 0.03 | 749|
| pure/uniform/CLLong | 43 | 0.03 | 1554|
| pure/uniform/CULLong | 42 | 0.03 | 1505|
| pure/uniform/CIntPtr | 43 | 0.03 | 1476|
| pure/uniform/CUIntPtr | 42 | 0.03 | 1463|
| pure/uniform/CIntMax | 43 | 0.03 | 1535|
| pure/uniform/CUIntMax | 42 | 0.03 | 1493|
</code></pre><br class=""><br class=""><div class="">
<div dir="auto" style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;" class=""><div style="color: rgb(0, 0, 0); font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;">Dominic Steinitz</div><div style="color: rgb(0, 0, 0); font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;"><a href="mailto:dominic@steinitz.org" class="">dominic@steinitz.org</a></div><div style="color: rgb(0, 0, 0); font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;"><a href="http://idontgetoutmuch.org" class="">http://idontgetoutmuch.org</a></div><div style="color: rgb(0, 0, 0); font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;">Twitter: @idontgetoutmuch</div><div style="color: rgb(0, 0, 0); font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><br class=""></div><br class="Apple-interchange-newline"></div><br class="Apple-interchange-newline">
</div>
<br class=""></body></html>