I'm using FsCheck to make a generator to create fake database records. Depending upon the field in question it uses chooseFromList, or generators for ints, floats, etc.
However, one troublesome field is a primary key field that must be sequential. I.e. put 1 in the first record generated, 2 in the second record generated, etc. After I generate a passel of records, I'll use them to impersonate a database table in my software under test.
Every solution my FORTRAN-brain dreams up seems evil, impure, and procedural. But I believe this is a problem that smarter FsCheck users than I have engaged. Could this be a use-case for functors or monads...
We can generate sequential numbers with a function ident
that increments an internal counter:
type Shirt = { Collar : float; Sleeve : float; id: int }
let collars = [ 14.0; 14.5; 15.0; 15.5; 16.0; 16.5; 17.0; 17.5; 18.0; ]
let sleeves = [ 30.5; 31.5; 32.5; 33.5; 34.5; 35.5; 36.5; 37.5; 38.5]
let ident =
let n = ref 0
fun () -> n := !n + 1; !n
let shirtGen = gen {
let! collar = Gen.elements collars
let! sleeve = Gen.elements sleeves
return { Collar = collar ; Sleeve = sleeve; id = ident() }
}
shirtGen
|> Gen.sample 0 10
|> List.rev
|> Seq.iter (printfn "%A")
I am reversing the list produced by Gen.sample
because it produces an inverted list. This are the samples generated:
{Collar = 14.5; Sleeve = 35.5; id = 1;}
{Collar = 15.0; Sleeve = 31.5; id = 2;}
{Collar = 16.5; Sleeve = 32.5; id = 3;}
{Collar = 17.5; Sleeve = 35.5; id = 4;}
{Collar = 16.5; Sleeve = 37.5; id = 5;}
{Collar = 14.0; Sleeve = 35.5; id = 6;}
{Collar = 18.0; Sleeve = 30.5; id = 7;}
{Collar = 16.0; Sleeve = 38.5; id = 8;}
{Collar = 17.0; Sleeve = 34.5; id = 9;}
{Collar = 14.5; Sleeve = 36.5; id = 10;}