I have recursive types like this:
type QueryInfo =
   { Title     : string
     Check     : Client -> bool
     Positive  : Decision
     Negative  : Decision }
 and Decision = 
    | Result of string
    | Query  of QueryInfo
I  am going to make a generator for FsCheck. I have seen this and I am not interested in approaches like  static member. My main problem is that every field has a different type.
FsCheck can already generate values of QueryInfo, but since the type is recursive, the generated values can become so deeply nested that generation of values effectively never stops (or, at least) is very slow.
Something like this ought to work:
let qi =
    let createQi t c p n = {
        Title = t
        Check = c
        Positive = p
        Negative = n }
    let makeQiGen =
        Gen.map4 createQi Arb.generate<string> Arb.generate<Client -> bool>
    let resultGen = Arb.generate<string> |> Gen.map Result
    let rec qi' size =
        if size <= 0
        then makeQiGen resultGen resultGen
        else
            let subQi = qi' (size - 1) |> Gen.map Query
            Gen.oneof [
                makeQiGen resultGen resultGen
                makeQiGen subQi subQi
                makeQiGen resultGen subQi
                makeQiGen subQi resultGen ]
    Gen.sized qi'
The essence is to prevent infinite (or very deep) recursions, which is done by Gen.sized.
When size reaches 0, the generator always returns a leaf node - that is: both Positive and Negative are Result values.
When size is greater than 0, the generators picks from one of four generators:
Positive and Negative are new Query values.Positive is a Result, but Negative is a Query.Positive is a Query, but Negative is a Result.In each recursion, though, size is decremented, so eventually, a leaf node is returned.
This answer is based on the Generating recursive data types section of the FsCheck documentation.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With