essenceDocumentation
essenceDocs
on this pageAt a glance
Coming from JavaScript?

Randomness

type

A source of random values: seed one, draw Booleans, Integers, Rationals and Strings, and pick or shuffle.

A source of random values, and the one value in the language that changes.

Every draw answers a value and advances the source, so two draws answer two values. That is what a Generatable body needs: asking for a name and then for a score must not answer the same draw twice.

A source is carried by hand. A Method that draws takes one as a Parameter, and its caller passes the same source on. Mapping over a List with one source draws once for each item, in the order the items stand in.

Taught on: Tests.

At a glance

Declared
namespace Randomness for Randomness

Creating a source

entropy

on Randomness

static entropy() -> Randomness

Answers the host's own source of randomness.

Every call answers the one source the whole Program draws from. It reads the host at each draw rather than carrying words of its own. So a Constant holding it answers a new value at every draw, and a run that draws from it can not be replayed. Use seeded(_) for a run that has to repeat.

the host's source of randomness.

seeded

on Randomness

static seeded(_ seed: String) -> Randomness

Answers a source that draws one sequence for one seed.

The same seed, drawn from by the same calls in the same order, answers the same values. The source carries words that every draw advances, so a caller that wants the sequence again builds a second source from the seed. Any text is a seed. A seed is hashed to 32 bits, so two different seeds almost always answer two different sequences.

NameTypeDescription
_Stringthe text the sequence is built from

a source that replays.

Example
constant source = Randomness.seeded("league")
constant replay = Randomness.seeded("league")

constant drawn = source::drawInteger(between 1, and 100)

expect replay::drawInteger(between 1, and 100)::is(drawn)

Drawing values

drawBoolean

on Randomness

Answers true or false.

The entry taking no Argument answers each of the two half the time. A chance of 1/4 answers true once in four draws. A chance below zero answers false at every draw, and a chance above one answers true at every draw.

drawBoolean() -> Boolean

true or false, with each equally likely.

Example
constant coin = Randomness.seeded("coin")
constant heads = List.of(integersFrom 1, through 100)
	::count(where (_) { <- coin::drawBoolean() })

expect heads::isBetween(30, and 70)
drawBoolean(withProbability chance: Rational) -> Boolean
NameTypeDescription
withProbabilityRationalhow often the answer is true, as a part of one

true that often, and false the rest of the time.

Example
constant coin = Randomness.seeded("coin")
constant rare = List.of(integersFrom 1, through 100)
	::count(where (_) { <- coin::drawBoolean(withProbability 1/4) })

expect rare::isBetween(10, and 40)
expect coin::drawBoolean(withProbability 3/2)

drawInteger

on Randomness

Answers a whole number the source draws.

The two bounds name the same range in either order, and both of them are inside it. The below: entry counts from zero, and the bound it is given is outside the range.

drawInteger(between low: Integer, and high: Integer) -> Integer
NameTypeDescription
betweenIntegerone bound of the range.
andIntegerthe other bound of the range.

a number from the range, with each one equally likely.

Example
constant die = Randomness.seeded("dice")

expect die::drawInteger(between 1, and 6)::isBetween(1, and 6)
expect die::drawInteger(between 6, and 1)::isBetween(1, and 6)
expect die::drawInteger(between 4, and 4)::is(4)
drawInteger(below bound: PositiveInteger) -> NonNegativeInteger
NameTypeDescription
belowPositiveIntegerthe number the range stops below, proven to be above zero

a number from zero up to the bound, with each one equally likely.

Example
constant source = Randomness.seeded("index")

expect source::drawInteger(below 10)::isBetween(0, and 9)
expect source::drawInteger(below 1)::is(0)

drawRational

on Randomness

Answers an exact fraction between two bounds, with both bounds included.

The two bounds name the same range in either order. The entry taking no denominator draws over one of twelve small denominators. Those are 1, 2, 3, 4, 5, 6, 8, 10, 12, 16, 100 and 1000. A caller that needs another names it with over:. Either way the answer is a Rational in lowest terms. Its denominator is the one drawn over or a divisor of it, so a draw of 105/100 answers 21/20. A range too narrow to hold a multiple of the denominator answers the lower bound.

drawRational(between low: Rational, and high: Rational) -> Rational
NameTypeDescription
betweenRationalone bound of the range.
andRationalthe other bound of the range.

a fraction from the range, drawn over one of the twelve denominators and reduced.

drawRational(
	between low: Rational,
	and high: Rational,
	over denominator: PositiveInteger,
) -> Rational
NameTypeDescription
betweenRationalone bound of the range.
andRationalthe other bound of the range.
overPositiveIntegerthe denominator every answer is drawn over, proven to be above zero

a fraction from the range, drawn over the given denominator and reduced.

Example
constant source = Randomness.seeded("prices")
constant quarter = source::drawRational(between 0/1, and 1/1, over 4)

expect [0/1, 1/4, 1/2, 3/4, 1/1]::contains(quarter)
expect source::drawRational(between 1/10, and 1/5, over 2)::is(1/10)

drawString

on Randomness

drawString(upTo length: Integer) -> String

Answers a String of the given number of characters or fewer.

The characters are letters, digits and a space, with four characters that are outside ASCII among them. A bound of zero or less answers the empty String.

NameTypeDescription
upToIntegerthe greatest number of characters the answer can have.

a String of that length or shorter.

Example
constant source = Randomness.seeded("names")

expect source::drawString(upTo 8)::length()::isLessThanOrEqualTo(8)
expect source::drawString(upTo 0)::isEmpty()

Choosing from a List

pick

on Randomness

Answers items of the given List, chosen by the source.

The counted entry draws without replacement, so it never answers one item twice, and a count above the length of the List answers every item. The drawn items stand in the order they were drawn. A weight of zero or less is drawn as zero, and weights that are all zero answer as the unweighted entry does.

pick<infer ItemType>(from items: NonEmptyList<ItemType>) -> ItemType
NameTypeDescription
fromNonEmptyList<ItemType>the items to choose from, which certainly has one.

an item of the List, with each item equally likely.

Example
constant colours: NonEmptyList<String> = ["red", "green", "blue"]
constant source = Randomness.seeded("colours")

expect colours::contains(source::pick(from colours))
pick<infer ItemType>(
	_ count: PositiveInteger,
	from items: NonEmptyList<ItemType>,
) -> NonEmptyList<ItemType>
NameTypeDescription
_PositiveIntegerhow many items to answer, proven to be above zero
fromNonEmptyList<ItemType>the items to choose from, which certainly has one.

the drawn items, which certainly holds one.

Example
constant team: NonEmptyList<String> = ["Ada", "Grace", "Alan"]
constant source = Randomness.seeded("team")
constant pair = source::pick(2, from team)

expect pair::length()::is(2)
expect pair::hasDuplicates()::negate()
expect source::pick(5, from team)::sort()::is(["Ada", "Alan", "Grace"])
pick<infer ItemType>(
	from items: NonEmptyList<ItemType>,
	weightedBy weight: (_: ItemType) -> Rational,
) -> ItemType
NameTypeDescription
fromNonEmptyList<ItemType>the items to choose from, which certainly has one.
weightedBy(_: ItemType) -> Rationalhow likely each item is, against the weights of the others

an item of the List, as likely as its own weight.

Example
constant source = Randomness.seeded("prizes")
constant prize = source::pick(
	from [
		{ name = "car", weight = 0/1 },
		{ name = "pen", weight = 3/4 },
	],
	weightedBy .weight,
)

expect prize.name::is("pen")

shuffle

on Randomness

Answers a new List holding the same items in an order the source draws.

Every order of the items is equally likely. The List it is given is left as it was.

shuffle<infer ItemType>(_ items: List<ItemType>) -> List<ItemType>
NameTypeDescription
_List<ItemType>the items to reorder

the reordered List.

Example
constant deck: List<Integer> = [1, 2, 3, 4]
constant source = Randomness.seeded("deck")
constant shuffled = source::shuffle(deck)

expect shuffled::sort()::is([1, 2, 3, 4])
shuffle<infer ItemType>(
	_ items: NonEmptyList<ItemType>,
) -> NonEmptyList<ItemType>
NameTypeDescription
_NonEmptyList<ItemType>the items to reorder, which certainly has one

the reordered List, which certainly holds one.

Example
constant names: NonEmptyList<String> = ["Ada", "Grace", "Alan"]
constant source = Randomness.seeded("names")
constant shuffled = source::shuffle(names)

expect names::contains(shuffled::firstItem())
expect shuffled::length()::is(3)

See also

esc