How to do the equivalent of Gen.filter in a gen CE

52 Views Asked by At

FsCheck offers the Gen.filter / Gen.where functions. Example:

type Status = Pending | Activated | Deactivated
type Item = { Id: int; Status: Status }

let nonPendingItemGen =
    Arb.generate
    |> Gen.filter (fun item -> item.Id > 0 && item.Status <> Pending }

Is it possible to refactor this code to use gen computation expression with the "filter" inside?

  • A quick attempt using an if statement failed because of the type discrepancies between the if and the else branches.
  • I found a solution by generating a sequence and then filter this sequence but I find it less elegant.
1

There are 1 best solutions below

0
Brian Berns On

You can do this by generating a valid ID and status within the computation expression and then constructing the item from those values. Instead of thinking about what values you don't want, think about the values you do want and generate them:

let nonPendingItemGen =
    gen {
        let! id = Arb.generate<PositiveInt>
        let! status = Gen.elements [ Activated; Deactivated ]
        return { Id = id.Get; Status = status }
    }

This approach is more efficient in general, because you don't waste time generating (and then rejecting) invalid items.

Note that I've used FsCheck's built-in PositiveInt type. Alternatively, you could generate a positive integer directly via:

Gen.choose (1, Int32.MaxValue)

Related Questions in F#