This library provides the ability for a user to generate random values of arbitrary type. It provides a pure-functional interface to preserve equational reasoning. Existing combinator libraries are provided for built-in data types. For example:
- Generating random product types (tuples) given generators for its components.
- Generating random sum types given generators for its constructors
scala.Either
scalaz.\/
scalaz.Validation
Example usages are provided in the /examples
source directory.
To use the library, add the following to your SBT configuration:
libraryDependencies += "com.nicta" %% "rng" % "1.3.0"
(changing the version number if necessary).
A generator is represented by the Rng
data type. A value of the type Rng[T]
will produce random values of the type
T
. For example, to generate random pairs (X, Y)
and given a Rng[X]
(call it randomX
) and a Rng[Y]
(call it randomY
), use the zip
function:
val randomPair: Rng[(X, Y)] =
randomX zip randomY
To generate random sum type (X \/ Y)
and given a Rng[X]
(call it randomX
) and a Rng[Y]
(call it randomY
), use
the either
function:
val randomEither: Rng[(X \/ Y)] =
randomX either randomY
The Rng
data type uses a specific technique for ensuring a pure-functional programming interface. It utilises
the free monad by defining a grammar for manipulation by the free monad.
The grammar is defined by the RngOp
data type, which defines two primitive operations:
nextbits
which is expected to produce a random integersetseed
which manipulates the seed for random generation
All other random generation operations are defined in terms of these two primitive operations.
The library user manipulates random generation by essentially building up a program that is composed of combinations of these operations. Although it is important to understand this programming model, the library user is effectively insulated by higher-level libraries. Users can expect the types provided by the library to dictate purpose. This robustness is a consequence of the pure-functional programming interface. Side-effects are guaranteed to never occur.
The run
method on Rng
executes the grammar and returns an IO
action to manipulate the value arbitrary. The
provided examples demonstrate how to achieve this.
Th RngOp
data type underlies the random generator by providing a grammar with two instructions (nextbits
and
setseed
). Programming the RngOp
data type directly is atypical and users might consider the higher-level library
provided on the Rng
data type instead. The RngOp
type constructor forms a comonad
and so has operations for
manipulating the operation:
- The
map
method onRngOp[A]
accepts a function (A => B
) and returns aRngOp[B]
- The
coflatMap
method onRngOp[A]
accepts a function (RngOp[A] => B
) and returns aRngOp[B]
RngOp
values can then be lifted to a generator (Rng
) using the lift
method.
Many random generators are provided, such as:
- The
option
method produces a generator forRng[Option[T]]
when the method is called on a value of the typeRng[T]
. - The
list
method produces a generator forRng[List[T]]
when the method is called on a value of the typeRng[T]
. - The
list1
method produces a generator forRng[NonEmptyList[T]]
when the method is called on a value of the typeRng[T]
. - Generators for primitive types provided as functions on the
Rng
object:int
to generate random integer valuesbyte
to generate random byte valuesshort
to generate random short valueslong
to generate random long valuesdouble
to generate random double valuesfloat
to generate random float valuesboolean
to generate random boolean valuesshort
to generate random short valueschar
to generate random character valueschars
to generate random lists of character valueschars1
to generate random non-empty lists of character values- Upper-case characters
upper
to generate random upper-case (A-Z) character valuesuppers
to generate random lists of upper-case (A-Z) character valuesuppers1
to generate random non-empty lists of upper-case (A-Z) character valuesupperstring
to generate random strings of upper-case (A-Z) character valuesupperstring1
to generate random non-empty strings of upper-case (A-Z) character values
- Lower-case characters
lower
to generate random lower-case (a-z) character valueslowers
to generate random lists of lower-case (a-z) character valueslowers1
to generate random non-empty lists of lower-case (a-z) character valueslowerstring
to generate random strings of lower-case (a-z) character valueslowerstring1
to generate random non-empty strings of lower-case (a-z) character values
- Alpha characters
alpha
to generate random alpha (a-z and A-Z) character valuesalphas
to generate random lists of alpha (a-z and A-Z)character valuesalphas1
to generate random non-empty lists of (a-z and A-Z) alpha character valuesalphastring
to generate random strings of alpha (a-z and A-Z) character valuesalphastring1
to generate random non-empty strings of alpha (a-z and A-Z) character values
- Numeric characters
numeric
to generate random numeric (0-9) character valuesnumerics
to generate random lists of numeric (0-9) character valuesnumerics1
to generate random non-empty lists of numeric (0-9) character valuesnumericstring
to generate random strings of numeric (0-9) character valuesnumericstring1
to generate random non-empty strings of numeric (0-9) character values
- Alpha-numeric characters
alphanumeric
to generate random alpha-numeric (a-z, A-Z and 0-9) character valuesalphanumerics
to generate random lists of alpha-numeric (a-z, A-Z and 0-9) character valuesalphanumerics1
to generate random non-empty lists of alpha-numeric (a-z, A-Z and 0-9) character valuesalphanumericstring
to generate random strings of alpha-numeric (a-z, A-Z and 0-9) character valuesalphanumericstring1
to generate random non-empty strings of alpha-numeric (a-z, A-Z and 0-9) character values
- Identifiers (an identifier is defined by a string of characters starting with an alpha character, followed by zero
or more alpha-numeric characters)
identifier
for generating random non-empty lists of characters representing an identifieridentifierstring
for generating random non-empty strings representing an identifier
- Proper noun (a proper noun is defined by a string of characters starting with an upper-case character, followed by
zero or more lower-case characters)
propernoun
for generating random non-empty lists of characters representing a proper nounpropernounstring
for generating random non-empty strings representing a proper noun
- Integer ranges
negative(double/float/long/int)
to generate random negative doubles/floats/longs/integerspositive(double/float/long/int)
to generate random positive doubles/floats/longs/integerschooseint
to generate random integers in a given rangechooselong
to generate random longs in a given rangechoosefloat
to generate random floats in a given rangechoosedouble
to generate random doubles in a given range
- Generators for values of the
scalaz.Digit
data type provided as functions on theRng
object:digit
to generate random digit valuesdigits
to generate random lists of digit valuesdigits1
to generate random non-empty lists of digit values
- Generators for manipulating lists of values
oneof
accepts a non-empty (variable argument) list of values and returns a generator that produces one of those valuesoneofL
does the same asoneof
, however, it is accepts an argument ofscalaz.NonEmptyList
instead of a non-empty argument list.frequency
accepts a non-empty (variable argument) list of pairs of values. The pair is an integer and a random generator where the integer represents the skewed frequency of the associated random generator. ThefrequencyL
function returns a generator that will select from the given list of generators with a skewed distribution.frequencyL
does the same asfrequency
, however, it is accepts an argument ofscalaz.NonEmptyList
instead of a non-empty argument list.
- Distributing and traversing generators
sequence
for taking a traversable of generators to a generator of traversables. A Traversable value is represented as a generalised interface (scalaz.Traverse
).distribute
for taking a generator of distributive values to a distribution of generators. A distributive value is represented as a generalised interface (scalaz.Distributive
).
The Rng
type constructor forms a monad making it trivial to combine existing random generators for user-defined data
types. For example, consider a data type combined of products and sums:
case class Person(name: String, age: Option[Int])
A random generator can be constructed by combining random generators for String
, Int
and Option
using a
for-comprehension:
val randomPerson: Rng[Person] =
for {
n <- Rng.string
a <- Rng.int.option
} yield Person(n, a)
Documentation for this library is provided by this document, example usages and static-type verification. The statically verified constraints provided by this library are a consequence of the pure-functional programming interface.