Skip to content

Conversation

Copy link

Copilot AI commented Jan 26, 2026

Adds Gen.pick and Gen.tryPick by analogy to Seq.pick - applies a chooser function to successive generated values, returning the first Some x.

Changes

F# API (FSharp.Gen.fs)

  • Gen.tryPick: Bounded retry with increasing size, returns None if no match found
  • Gen.pick: Infinite retry until match found
  • Implementation mirrors existing tryWhere/where pattern

C# API (Fluent.Gen.fs)

  • TryPick: Returns value or null/default
  • Pick: Infinite retry for non-null values
  • Uses boxing for null checking across reference and value types

Tests (Gen.fs)

  • Property-based tests covering both functions

Usage

// F# - pick first even number from generator
let chooser x = if x % 2 = 0 then Some x else None
Gen.pick chooser (Gen.choose(0, 100))

// C# - pick first positive value
var chooser = new Func<int, int?>(x => x > 0 ? x : null);
Gen.Choose(0, 100).Pick(chooser)
Original prompt

This section details on the original issue you should resolve

<issue_title>Support Gen.pick by analogy to Seq.pick</issue_title>
<issue_description>Seq.pick is a very useful function that applies a given "chooser" function to successive elements, returning the first x where the function returns Some x. By analogy, I think FsCheck should provide a similar function. Here's a simple implementation that demonstrates the idea:

let rec pick chooser gn =
    gen {
        let! value = gn
        match chooser value with
            | Some v -> return v
            | None -> return! pick chooser gn
    }
```</issue_description>

<agent_instructions>Add Gen.pick and Gen.tryPick functions as described. Also exposre them in the C# API as per the standard pattern.</agent_instructions>

## Comments on the Issue (you are @copilot in this section)

<comments>
<comment_new><author>@kurtschelfthout</author><body>
Appreciate the idea @brianberns. I understand the utility of `pick` (combining transformation with filtering), but a few thoughts on the "cost" side of the equation.

Implementation wise, would  something like `Gen.map chooser >> Gen.filter Option.isSome >> Gen.map Option.get` work? That seems relatively straightforward to implement.

The situation in FsCheck is a bit more complicated than for `Seq` - there are safeguards when doing any kind of filtering to make sure you don't lose time generating elements that don't pass the filter. I've been tripped by this plenty of times, so filtering in general is not encouraged in FsCheck. It's much better to create "good" elements by construction or transformation. Filtering of course has some utility, e.g. to filter out zeroes or single elements from a wide range, but certainly less utility than `Seq.filter/pick`.

Lastly, for `Gen.filter` there is the alternative `Gen.tryFilter` https://fscheck.github.io/FsCheck/reference/fscheck-gen.html#tryFilter to increase the size if a number of elements don't pass the filter. Do we need `tryPick`?

With all that (ease of implementation + likely problematic in common use cases + non-trivial API impact), do you still feel the added utility outweighs the cost?
 
</body></comment_new>
<comment_new><author>@ploeh</author><body>
I'm fairly agnostic about this suggestion, but tend to agree with @kurtschelfthout that filtering should be avoided in most cases. (And then there are [some rare cases where it makes sense](https://blog.ploeh.dk/2016/01/18/make-pre-conditions-explicit-in-property-based-tests).)

> > filtering in general is not encouraged in FsCheck
> 
> I definitely agree with this. However, there are cases where it can't reasonably be avoided.

I tend to be curious when encountering claims like that. This may risk derailing the discussion, but would you mind sharing an example?</body></comment_new>
<comment_new><author>@ploeh</author><body>
That's what I get for asking 😅 That's not really in my wheelhouse, but in any case it's hard to suggest alternatives without code.</body></comment_new>
</comments>

✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Copilot AI and others added 2 commits January 26, 2026 22:13
Co-authored-by: kurtschelfthout <164917+kurtschelfthout@users.noreply.github.com>
Co-authored-by: kurtschelfthout <164917+kurtschelfthout@users.noreply.github.com>
Copilot AI changed the title [WIP] Add Gen.pick and Gen.tryPick functions Add Gen.pick and Gen.tryPick functions Jan 26, 2026
Copilot AI requested a review from kurtschelfthout January 26, 2026 22:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support Gen.pick by analogy to Seq.pick

2 participants