Search code examples
functional-programmingf#fscheck

Can an FsCheck generator create an ever-increasing sequence


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...


Solution

  • 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;}