Skip to content

Comments

Fsharp.Core :: {Array;List;Set;Array.Parallel} partitionWith (taking Choice<T,U> partitioner)#19335

Open
T-Gro wants to merge 16 commits intomainfrom
feature-fsharpcore-partitionMap
Open

Fsharp.Core :: {Array;List;Set;Array.Parallel} partitionWith (taking Choice<T,U> partitioner)#19335
T-Gro wants to merge 16 commits intomainfrom
feature-fsharpcore-partitionMap

Conversation

@T-Gro
Copy link
Member

@T-Gro T-Gro commented Feb 20, 2026

Implements fsharp/fslang-suggestions#1119

Signatures

module Array =
    val inline partitionWith: partitioner: ('T -> Choice<'T1, 'T2>) -> array: 'T array -> 'T1 array * 'T2 array

module Array.Parallel =
    val inline partitionWith: partitioner: ('T -> Choice<'T1, 'T2>) -> array: 'T array -> 'T1 array * 'T2 array

module List =
    val inline partitionWith: partitioner: ('T -> Choice<'T1, 'T2>) -> list: 'T list -> 'T1 list * 'T2 list

module Set =
    val partitionWith: partitioner: ('T -> Choice<'T1, 'T2>) -> set: Set<'T> -> Set<'T1> * Set<'T2>

Example — active patterns

A total active pattern (|A|B|) is already 'T -> Choice<'T1, 'T2> — pass it directly.

let (|Valid|Invalid|) (s: string) =
    match System.Int32.TryParse s with
    | true, n -> Valid n
    | _ -> Invalid s

let parsed, errors =
    ["42"; "hello"; "7"; "oops"; "99"]
    |> List.partitionWith (|Valid|Invalid|)

// parsed = [42; 7; 99]
// errors = ["hello"; "oops"]

T-Gro and others added 16 commits February 13, 2026 22:54
Adds List.partitionWith that splits a list into two lists of potentially
different types using a Choice-returning partitioner function.

- Implementation in list.fs using ListCollector and recursive loop
- Signature with XML docs and example in list.fsi
- Unit tests covering basic, empty, all-Choice1, all-Choice2, single
  element, and order preservation cases
Adds Array.partitionWith function that splits an array into two arrays
by applying a partitioner function returning Choice<'T1,'T2>.
Uses ArrayCollector<'T> for efficient array building.

Includes XML documentation with example and unit tests covering:
- Basic type-changing partition
- Empty array input
- All Choice1Of2 / all Choice2Of2
- Single element
- Order preservation
- Null input throws ArgumentNullException
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- List: stack safety test with 100k elements verifying tail recursion
- List/Array/Set: exception propagation tests with InvalidOperationException

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…tests

Replace Assert.Throws<InvalidOperationException> with the existing
CheckThrowsInvalidOperationExn helper from LibraryTestFx.fs for
consistency with the rest of the test codebase.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds FsCheck property-based consistency tests to
CollectionModulesConsistency.fs:
- List vs Array: verifies identical ordered results across int,
  string, NormalFloat
- Set vs Array: verifies set-equal results (order-independent)
  across int, string, NormalFloat

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace iter-based ascending traversal with right→key→left recursive
traversal, matching partitionAux's proven strategy. Elements are inserted
largest-first into output trees, causing fewer rebalancing rotations.
Internal accumulator uses struct tuples to avoid per-step allocations.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace ChunkedArrayCollector with a two-pass strategy:
- Pass 1: call partitioner, store results, count Choice1Of2
- Pass 2: allocate exact-sized arrays, scatter results

Mark function inline with [<InlineIfLambda>] on partitioner parameter.
Remove ChunkedArrayCollector type (only used by partitionWith).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Rewrite body to use ListCollector instead of freshConsNoTail/setFreshConsTail
to avoid FieldAccessException when inlined into external assemblies.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Implement the parallel version of Array.partitionWith using the same
Parallel.For + thread-local accumulator + Interlocked.Add pattern as
Array.Parallel.choose.

- Implementation in src/FSharp.Core/array.fs
- Signature with XML docs in src/FSharp.Core/array.fsi
- Unit tests and consistency tests
- Surface area baselines updated (4 .bsl files)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ase notes

- Replace intermediate Choice<'T1,'T2>[] in Array.partitionWith with the
  3-array pattern (bool[] + T1[] + T2[]) matching the parallel version,
  eliminating N per-element heap allocations of Choice DU objects
- Add Array.Parallel.partitionWith to release notes

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions
Copy link
Contributor

❗ Release notes required


✅ Found changes and release notes in following paths:

Warning

No PR link found in some release notes, please consider adding it.

Change path Release notes path Description
src/FSharp.Core docs/release-notes/.FSharp.Core/10.0.300.md No current pull request URL (#19335) found, please consider adding it

@Happypig375
Copy link
Member

No Choice<'a, 'b, 'c> or Choice<'a, 'b, 'c, 'd> versions?

@T-Gro
Copy link
Member Author

T-Gro commented Feb 20, 2026

No Choice<'a, 'b, 'c> or Choice<'a, 'b, 'c, 'd> versions?

fsharp/fslang-suggestions#1119 (comment)

But your question made me realize that if we wanted to support all various sorts (all the 7 choices, and result,..), it could be solved by a language-level facility that inverts collection of N-case DU items, and puts them into N different collections of respective fields of those cases.

Maybe more on the "type provider generate types+members from existing types" side ... ?

@T-Gro T-Gro marked this pull request as ready for review February 20, 2026 10:31
@T-Gro T-Gro requested a review from a team as a code owner February 20, 2026 10:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: New

Development

Successfully merging this pull request may close these issues.

2 participants