Published 13th of February, 2016, by Nikos Baxevanis. Tagged as f#, quickcheck.

Write you some QuickCheck - Generating random integers

This post is part of a series of posts on implementing a minimal version of QuickCheck from scratch. The source code is available on GitHub.

In the previous post I’ve generated random booleans. In this post, I’ll be generating random 32-bit signed integers.

Recall that the Gen<'a> type is ported already in this previous post as:

/// <summary>
/// A generator for values of type 'a.
/// </summary>
type Gen<'a> =
    private
    | Gen of (int -> StdGen -> 'a)

This means we can write functions that handle state (—monadster!) combining:

So, to generate numbers, we need:

Porting Gen’s sized

/// <summary>
/// Used to construct generators that depend on the size parameter.
/// </summary>
/// <param name="g">A generator for values of type 'a.</param>
let sized g =
    Gen(fun n r ->
        let (Gen m) = g n
        m n r)

Given the above, a 32-bit signed integer generator can be written as:

let int = Gen.sized (fun n -> Gen.choose (-n, n))

val int : Gen<int>

Here are some sample integers:

> Gen.int |> Gen.generate;;
val it : int = 0

> Gen.int |> Gen.generate;;
val it : int = -18

> Gen.int |> Gen.generate;;
val it : int = 0

> Gen.int |> Gen.generate;;
val it : int = 6

> Gen.int |> Gen.generate;;
val it : int = -6

> Gen.int |> Gen.generate;;
val it : int = 1

> Gen.int |> Gen.generate;;
val it : int = -2

> Gen.int |> Gen.generate;;
val it : int = 5

> Gen.int |> Gen.generate;;
val it : int = -4

> Gen.int |> Gen.generate;;
val it : int = -18

> Gen.int |> Gen.generate;;
val it : int = 5

> Gen.int |> Gen.generate;;
val it : int = -17

> Gen.int |> Gen.generate;;
val it : int = -7

> Gen.int |> Gen.generate;;
val it : int = -3

> Gen.int |> Gen.generate;;
val it : int = 1

> Gen.int |> Gen.generate;;
val it : int = 12

> Gen.int |> Gen.generate;;
val it : int = 5

> Gen.int |> Gen.generate;;
val it : int = 6

> Gen.int |> Gen.generate;;
val it : int = -8

> Gen.int |> Gen.generate;;
val it : int = -10

But there’s a pattern here (can you tell?); all the generated integers are in the range [-30, 30] — that’s because generate sets the size for the generators to be up to 301.

Generating numbers in range [-999, 999]

I can run the integer generator again, but this time I’ll override the runtime size.

This prompts me to port Gen’s resize

/// <summary>
/// Overrides the size parameter. Returns a generator which uses the given size
/// instead of the runtime-size parameter.
/// </summary>
/// <param name="n">The size that's going to override the runtime-size.</param>
let resize n (Gen m) = Gen(fun _ r -> m n r)

Finally, here are some sample integers in the range [-999, 999]:

> Gen.int |> Gen.resize 999 |> Gen.generate;;
val it : int = -808

> Gen.int |> Gen.resize 999 |> Gen.generate;;
val it : int = 797

> Gen.int |> Gen.resize 999 |> Gen.generate;;
val it : int = -676

> Gen.int |> Gen.resize 999 |> Gen.generate;;
val it : int = 3

> Gen.int |> Gen.resize 999 |> Gen.generate;;
val it : int = 304

> Gen.int |> Gen.resize 999 |> Gen.generate;;
val it : int = -476

> Gen.int |> Gen.resize 999 |> Gen.generate;;
val it : int = 276


  1. That’s how the generate function works in QuickCheck 2; It’s slightly different (and easier to use) compared to QuickCheck 1. 


Wish to comment?

You can comment on this post on GitHub. Alternatively, you can discuss this post on Twitter or elsewhere with a permalink. Ping me with the link, and I may respond.


Copyright © 2011 - 2020 Nikos Baxevanis · Source code available