Consider the Discrimination Union:
type DU = | Foo of string | Bar of int | Baz of decimal * float | Qux of bool
I would like to create a list of DU values ββusing FsCheck, but I want none of the values ββto be in the case of Qux .
This predicate already exists:
let isQux = function Qux _ -> true | _ -> false
First try
My first attempt to create a list of DU values ββwithout a Qux case was something like this:
type DoesNotWork = static member DU () = Arb.from<DU> |> Arb.filter (not << isQux) [<Property(MaxTest = 10 , Arbitrary = [| typeof<DoesNotWork> |])>] let repro (dus : DU list) = printfn "%-5b : %O" (dus |> List.exists isQux |> not) dus
Doing this seems to lead to a stack overflow, so I assume that what happens behind the scenes is that Arb.from<DU> calls DoesNotWork.DU .
Second attempt
Then I tried this:
type DoesNotWorkEither = static member DU () = Arb.generate<DU> |> Gen.suchThat (not << isQux) |> Arb.fromGen [<Property(MaxTest = 10 , Arbitrary = [| typeof<DoesNotWorkEither> |])>] let repro (dus : DU list) = printfn "%-5b : %O" (dus |> List.exists isQux |> not) dus
The same problems as above.
Detailed solution
This is the best solution I've been able to come up with:
type WithoutQux = static member DU () = [ Arb.generate<string> |> Gen.map Foo Arb.generate<int> |> Gen.map Bar Arb.generate<decimal * float> |> Gen.map Baz ] |> Gen.oneof |> Arb.fromGen [<Property(MaxTest = 10 , Arbitrary = [| typeof<WithoutQux> |])>] let repro (dus : DU list) = printfn "%-5b : %O" (dus |> List.exists isQux |> not) dus
This works, but has the following disadvantages:
- It seems like a lot of work.
- It does not use the already available
isQux function, so it seems to subtly violate DRY - It really does not filter, but rather creates only the necessary cases (therefore, it filters only inaction).
- This is not particularly convenient because if I ever add a fifth case to
DU , I would also have to add Gen for this case.
Is there a more elegant way to tell FsCheck to filter Qux values?
f # fscheck
Mark seemann
source share