RandomMonad provides a number of composable primitives for constructing
"distributions". A distribution is understood in a broad sense: it is anything
on which rand
can be called. A distribution is like a recipe describing how
to construct an object of a certain type. For example, 1:3
is an implicit
distribution describing how to pick randomly an Int
among 1
, 2
, 3
.
Unlike Distributions.jl
which seriously addresses mathematical needs, the RandomMonad
package is
less specific and is intended to be generally useful for implementing
randomness. This is reflected in the core type, a simple Distribution{T}
,
where T
can be anything and is just the type of generated values.
Currently, RandomMonad
also implements few classical mathematical
distributions, like Bernoulli
or Poisson
, but these might eventually be
split off in another dedicated package.
A basic distribution is Fill(d, n)
, defining the generation of arrays of
length n
of elements drawn from distribution d
:
julia> f = Fill(1:9, 4)
Fill(1:9, 4)
julia> eltype(f)
Array{Int64,1}
julia> rand(f)
4-element Array{Int64,1}:
8
3
8
4
Many algorithms which use randomness can be encapsulated as a distribution.
For example, Shuffle
defines an alternate API to the Random.shuffle
function,
but is more general. The following example creates an array of two vectors of
length 4
of distinct elements from 1:5
:
julia> rand(Fill(Shuffle(1:5), 4), 2)
2-element Array{Array{Int64,1},1}:
[3, 4, 5, 1]
[4, 1, 3, 5]
This is sampling from a collection "without replacement", and is
equivalent to [StatsBase.sample(1:5, 4, replace=false) for _=1:2]
.
I won't add yet another tutorial on monads, but the good news is that knowing
the theory of monads is not at all necessary for using this package. It just
so happens that Distributions{T}
has a monadic structure, and the package
provides some related "combinators" (basic blocs to create more elaborate
constructions).