We already have a function that runs a generator Gen<'a> and returns random test data of type 'a.

valgenerate:Gen<'a>->'a

Now, in order to be able to generate random bytes, we need a couple of generators:

a generator that picks numbers from a given range (in this case [0..255])

a generator that applies a function (in this case Operators.byte<^T>) to an existing generator

Porting Gen’s choose

/// <summary>/// Generates a random element in the given inclusive range, uniformly/// distributed in the closed interval [lo,hi]./// </summary>/// <param name="lo">The lower bound.</param>/// <param name="hi">The upper bound.</param>letchoose(lo,hi)=Gen(funnr->r)|>Gen.map(Random.range(lo,hi)>>fst)

Porting Gen’s map, bind, and return^{1} since choose depends on them

/// <summary>/// Sequentially compose two actions, passing any value produced by the first/// as an argument to the second./// </summary>/// <param name="f">/// The action that produces a value to be passed as argument to the generator./// </param>letbind(Genm)f=Gen(funnr->let(r1,r2)=r|>Random.splitlet(Genm')=f(mnr1)m'nr2)/// <summary>/// Injects a value into a generator./// </summary>/// <param name="a">The value to inject into a generator.</param>letinita=Gen(funnr->a)/// <summary>/// Returns a new generator obtained by applying a function to an existing/// generator./// </summary>/// <param name="f">The function to apply to an existing generator.</param>/// <param name="m">The existing generator.</param>letmapfm=bindm(funm'->init(fm'))

All the pieces are now in place, and so a byte generator can be written as: