Skip to content

feat(TaskResult+AsyncResult+JobResult): bindResult, req, reqFilter, ok, either, eitherMap, ignore etc - #376

Open
bartelink wants to merge 26 commits into
demystifyfp:masterfrom
bartelink:bindresult
Open

bartelink wants to merge 26 commits into
demystifyfp:masterfrom
bartelink:bindresult

Conversation

@bartelink

@bartelink bartelink commented Sep 15, 2026 •

Copy link
Copy Markdown
Contributor

Cleanups to harmonize AsyncResult, Result, TaskResult, CancellableTaskResult and JobResult:

  • add ok (missing from JobResult)
  • add ignore (missing from Async)
  • add bindResult generic helper for *Result
  • add either (erroneously named foldResult) on TaskResult, AsycResult, JobResult
  • add eitherMap to *Result
  • change ignore, adding RequireExplicitTypeArguments (more error-safe; aligns with FSharp.Core Task, Async, ValueTask) for Task, Async, Option
  • correct *Option: make either follow equivalents in taking synchronous functions only
  • obsolete valueOr (was only present on Result; duplicates built-in defaultWith)
  • generalize Result.requireX and the asociated per-Computation requireX and bindRequireX to a single req function
    • any Req function you write plugs into any container without any specific work
    • all Req functions have reliable and correct With variants as a one-liner
    • a req function binding to any Req function is present on all compurations and computation-results, so CancellableTask, CancellableTaskResult, CancellableValueTask and many more gain the same support that only the baseline Async/Task/Job computations had to date
    • reduces maintenance/review burden

Fixes:

  • remove propagation of xmldoc from pinned FSharp.Core v 6.0.4 (for pre-net9.0 builds)

See changelog for authoritative list of changes

resolves #375

@bartelink
bartelink force-pushed the bindresult branch 7 times, most recently from 0e67281 to 6d72e02 Compare September 15, 2026 20:08
@bartelink bartelink changed the title feat(TaskResult+AsyncResult+JobResult): bindResult, ok feat(TaskResult+AsyncResult+JobResult): bindResult, ok, some, either, eitherMap, ignore Sep 15, 2026
@bartelink
bartelink force-pushed the bindresult branch 11 times, most recently from 7c46139 to 748630c Compare September 15, 2026 21:37
@bartelink
bartelink marked this pull request as ready for review September 15, 2026 21:40
Copilot AI lite review requested due to automatic review settings September 15, 2026 21:40
@bartelink

Copy link
Copy Markdown
Contributor Author

@TheAngryByrd The opening objective was to address #375, but I think it makes sense to include parts of #373 in order to make that a more digestible review, but also including some preparatory work for same.

There's lots of xmldoc corrections, fixing orphans and typos etc.

Let me know if you need anything to change (I assume you squash merge - my history is imperfect) but for now I'll assume that this will merge relatively soon. I'll start rebasing #373 on this next.

@bartelink
bartelink force-pushed the bindresult branch 4 times, most recently from deb32c6 to f5db8b2 Compare September 15, 2026 21:57

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning

Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.

Pull request overview

This PR harmonizes the Result-family APIs across Result, AsyncResult, TaskResult, CancellableTaskResult, and JobResult by introducing consistent helpers (ok, either, eitherMap, bindResult, ignore) and updating tests/docs accordingly, plus a packaging workaround for FSharp.Core xml docs.

Changes:

  • Added/standardized helpers (ok, error, either, eitherMap, bindResult) across async/task/job result modules; marked legacy names (e.g., foldResult, valueOr) as obsolete.
  • Made Task.ignore / Option.ignore / Async.ignore require explicit type arguments and updated tests/docs to match.
  • Updated GitBook docs, release notes, and build props (incl. FSharp.Core ExcludeAssets="contentfiles" workaround).

Reviewed changes

Copilot reviewed 52 out of 52 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
tests/FsToolkit.ErrorHandling.Tests/TaskResult.fs Renames foldResult tests to either to match new API naming.
tests/FsToolkit.ErrorHandling.Tests/Result.fs Test cleanup (tuple type syntax, defaultWith test update, minor typos).
tests/FsToolkit.ErrorHandling.Tests/Option.fs Updates Option.ignore tests to include explicit type arguments; fixes typos.
tests/FsToolkit.ErrorHandling.Tests/AsyncResult.fs Renames foldResult tests to either for API alignment.
src/FsToolkit.ErrorHandling/TaskResult.fs Adds ok/error, either, eitherMap, bindResult; refactors bind-require helpers.
src/FsToolkit.ErrorHandling/Task.fs Adds RequiresExplicitTypeArguments to Task.ignore.
src/FsToolkit.ErrorHandling/Result.fs Marks valueOr obsolete and forwards to defaultWith.
src/FsToolkit.ErrorHandling/Option.fs Adds RequiresExplicitTypeArguments to Option.ignore.
src/FsToolkit.ErrorHandling/AsyncResultOptionCE.fs Uses AsyncResult.ok for Zero; minor builder construction cleanup.
src/FsToolkit.ErrorHandling/AsyncResult.fs Introduces either, obsoletes foldResult, adds bindResult, updates require-bind helpers.
src/FsToolkit.ErrorHandling/Async.fs Refactors map3 implementation and adds Async.ignore with explicit type args requirement.
src/FsToolkit.ErrorHandling.JobResult/JobResult.fs Adds ok/error, either, bindResult, expands map helpers, updates examples.
src/FsToolkit.ErrorHandling.IcedTasks/CancellableTaskResultCE.fs Cleans up CE internals and adds either/eitherMap + obsolete alias.
src/Directory.Build.props Updates copyright years; adds ExcludeAssets="contentfiles" to pinned FSharp.Core.
gitbook/validation/ce.md Updates parsing example to use Option.tryParse.
gitbook/taskResultOption/ignore.md Documents explicit type arguments for TaskResultOption.ignore.
gitbook/taskResult/ignore.md Documents explicit type arguments for TaskResult.ignore.
gitbook/taskResult/foldResult.md Updates documentation to TaskResult.either naming.
gitbook/task/ignore.md Documents explicit type arguments for Task.ignore.
gitbook/seq/traverseResultM.md Updates examples to use Option.tryParse/Option.defaultWith.
gitbook/seq/traverseResultA.md Updates examples to use Option.tryParse.
gitbook/seq/sequenceResultM.md Updates examples to use Option.tryParse.
gitbook/seq/sequenceResultA.md Updates examples to use Option.tryParse.
gitbook/seq/partitionResults.md Updates examples to use Option.tryParse.
gitbook/script.fsx Updates parsing example to use Option.tryParse.
gitbook/resultOption/ignore.md Documents explicit type arguments for ResultOption.ignore.
gitbook/result/map3.md Updates parsing example to use Option.tryParse.
gitbook/result/map2.md Updates parsing example to use Option.tryParse.
gitbook/result/ignore.md Documents explicit type arguments for Result.ignore.
gitbook/result/fold.md Renames content to Result.either semantics (doc-only rename).
gitbook/result/eitherFunctions.md Fixes expected output comment for eitherMap example.
gitbook/option/ignore.md Updates docs to require explicit type args; adds CE example.
gitbook/list/traverseResultM.md Updates parsing example to use Option.tryParse.
gitbook/list/traverseResultA.md Updates parsing example to use Option.tryParse.
gitbook/list/traverseJobResultM.md Updates job parsing example to use Option.tryParse.
gitbook/list/traverseJobResultA.md Updates job parsing example to use JobResult.ok/error + Option.tryParse.
gitbook/list/sequenceResultM.md Updates parsing example to use Option.tryParse.
gitbook/list/sequenceResultA.md Updates parsing example to use Option.tryParse.
gitbook/list/partitionResults.md Updates parsing example to use Option.tryParse.
gitbook/jobResultOption/ignore.md Documents explicit type arguments for JobResultOption.ignore.
gitbook/jobResult/ignore.md Documents explicit type arguments for JobResult.ignore.
gitbook/jobResult/foldResult.md Updates documentation to JobResult.either naming.
gitbook/cancellableTaskResult/ignore.md Documents explicit type arguments for CancellableTaskResult.ignore.
gitbook/cancellableTaskResult/foldResult.md Updates documentation to CancellableTaskResult.either naming.
gitbook/asyncResultOption/ignore.md Documents explicit type arguments for AsyncResultOption.ignore.
gitbook/asyncResult/ignore.md Documents explicit type arguments for AsyncResult.ignore.
gitbook/asyncResult/foldResult.md Updates documentation to AsyncResult.either naming.
gitbook/asyncResult/eitherMap.md Updates examples; adds Async.RunSynchronously; rewrites combined usage guidance.
gitbook/array/partitionResults.md Updates parsing example to use Option.tryParse.
gitbook/SUMMARY.md Moves navigation entries from fold/foldResult to either.
build/build.fs Updates NuGet package summary wording.
RELEASE_NOTES.md Adds 6.0.0-beta002 notes describing API harmonization + packaging fix.
Suppressed comments (8)

src/FsToolkit.ErrorHandling/TaskResult.fs:1

  • The generic shape of TaskResult.eitherMap is unexpectedly restrictive: it forces the mapped Ok type ('b) to be the same as the input error type (also 'b). eitherMap typically allows mapping Ok and Error independently: Task<Result<'ok,'err>> -> Task<Result<'ok2,'err2>>. Consider changing the signature to accept Task<Result<'a,'c>> and return Task<Result<'b,'d>> with onSuccess: 'a -> 'b and onError: 'c -> 'd (matching Result.eitherMap and your JobResult.eitherMap implementation).
    src/FsToolkit.ErrorHandling.IcedTasks/CancellableTaskResultCE.fs:1
  • Same issue as TaskResult.eitherMap: this signature forces the mapped Ok type to equal the input error type. To preserve the standard eitherMap shape, consider CancellableTask<Result<'a,'c>> -> CancellableTask<Result<'b,'d>> with onSuccess: 'a -> 'b and onError: 'c -> 'd.
    src/FsToolkit.ErrorHandling.IcedTasks/CancellableTaskResultCE.fs:1
  • The obsolete message points to TaskResult.either, but this module defines CancellableTaskResult.either. This is likely confusing for users and should reference CancellableTaskResult.either instead.
    src/FsToolkit.ErrorHandling/AsyncResult.fs:1
  • AsyncResult.eitherMap now has an explicit return type but leaves onSuccess, onError, and input untyped. This can make the public API signature harder to read in IntelliSense and can produce less helpful type errors. Consider fully annotating it (like JobResult.eitherMap) to clearly communicate the intended Async<Result<'ok,'err>> -> Async<Result<'ok2,'err2>> shape.
    gitbook/task/ignore.md:1
  • The explicit type argument passed to Task.ignore appears inconsistent with the surrounding example (which indicates savePost returns a PostId). The ignored type argument should match the task's result type (e.g., Task.ignore<PostId> in this example), otherwise the snippet won’t compile and may mislead users.
    tests/FsToolkit.ErrorHandling.Tests/Result.fs:1
  • This test is under defaultWith Tests and calls Result.defaultWith, but the test name says defaultValue. Renaming the test case to defaultWith ... would better reflect what’s being tested and reduce confusion when failures occur.
    src/FsToolkit.ErrorHandling/Result.fs:1
  • The PR description says valueOr is removed, but the code keeps it as an obsolete alias. Either update the PR description/release notes to reflect the deprecation approach, or actually remove valueOr if the intent is a hard removal in this version.
    src/FsToolkit.ErrorHandling/TaskResult.fs:1
  • New public surface area (TaskResult.bindResult, plus the new either/eitherMap helpers in this module) doesn’t appear to have corresponding tests in the diffs. Since this repo has a dedicated test project for these modules, adding direct unit tests for bindResult and eitherMap (both Ok and Error paths) would help prevent regressions and validate the intended type behavior.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread gitbook/SUMMARY.md
Comment thread gitbook/SUMMARY.md
Comment thread gitbook/SUMMARY.md Outdated
Comment thread gitbook/SUMMARY.md
Comment thread gitbook/asyncResult/eitherMap.md Outdated
Comment thread RELEASE_NOTES.md Outdated
@bartelink
bartelink force-pushed the bindresult branch 3 times, most recently from a82aa59 to ff43e76 Compare September 15, 2026 22:11
@bartelink

Copy link
Copy Markdown
Contributor Author

OK, test coverage is complete per clanky and manual review
Missing TaskResult.bindRequireNone added
That claimed missing coverage for *Option.either was a false positive
I think that's everything @TheAngryByrd

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread tests/FsToolkit.ErrorHandling.JobResult.Tests/Main.fs Outdated
Comment thread gitbook/cancellableTaskOption/either.md Outdated
Comment thread gitbook/cancellableValueTaskOption/either.md Outdated
Comment thread gitbook/taskOption/either.md Outdated
Comment thread gitbook/taskOption/either.md Outdated
Comment thread gitbook/valueTaskValueOption/either.md Outdated
Comment thread src/FsToolkit.ErrorHandling.JobResult/JobResult.fs Outdated
Comment thread src/FsToolkit.ErrorHandling/AsyncOption.fs Outdated
Comment thread src/FsToolkit.ErrorHandling/Result.fs Outdated
Comment thread src/FsToolkit.ErrorHandling/TaskResult.fs Outdated
@bartelink

Copy link
Copy Markdown
Contributor Author

@TheAngryByrd FYI I have a solution for the Bind X With X Task/Async/Job explosion; will likely have something to push later...

@bartelink

Copy link
Copy Markdown
Contributor Author

@TheAngryByrd Took some time, and it's a monster, but despite the churn it'll cause I believe it's for the best...

I've tried to explain it relatively simply in the PR overview (and/or the effects on the docs and README make the pattern clear)

It may make sense to offer a way to make the Obsolete on bindRequire and require conditional, so a large codebase can hold off on porting.

@bartelink
bartelink force-pushed the bindresult branch 17 times, most recently from bca88cf to de9bcb9 Compare September 21, 2026 16:49

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Compile-breaking module shadowing and critical requirement-binding and lazy-error regressions remain unresolved.

Get a fresh assessment by requesting another Copilot review.

Review effort: Balanced
Findings: 5 High severity · 7 Medium severity · 22 Low severity

Open (34)

And 14 more that still need to be addressed.

Resolved since last review (10)

/// </summary>
let backgroundCancellableTaskResult = BackgroundCancellableTaskResultBuilder()

module CancellableTask =
let backgroundCancellableValueTaskResult =
BackgroundCancellableValueTaskResultBuilder()

module CancellableValueTask =
Comment thread src/FsToolkit.ErrorHandling.IcedTasks/CancellableValueTaskResultCE.fs Outdated
open IcedTasks

[<RequireQualifiedAccess>]
module ValueTask =
Comment thread src/FsToolkit.ErrorHandling/TaskResult.fs Outdated
Comment thread src/FsToolkit.ErrorHandling.IcedTasks/ValueTaskValueOption.fs Outdated
Comment thread src/FsToolkit.ErrorHandling.JobResult/JobOption.fs Outdated
Comment thread src/FsToolkit.ErrorHandling/Req.fs Outdated
Comment thread src/FsToolkit.ErrorHandling/TaskOption.fs Outdated
Comment thread src/FsToolkit.ErrorHandling/TaskValueOption.fs Outdated
@bartelink

bartelink commented Sep 22, 2026 •

Copy link
Copy Markdown
Contributor Author

@TheAngryByrd remaining work / considerations:

  • 3x shadowning issues left for you to consider
  • 3x DisposableNull things for you
  • I used the bindRequire and requireTests to validate the Obsoletion migration messages are correct
  • I reviewed everything humanly by hand just now (after having addressed balanced review comments)
  • if you have tokens to burn I'd followup on another balanced review!
  • I think docs are reasonably complete but there are so many things that have the bindResult/req/reqFilter triple that maybe they need a better overview
  • I toyed with that/exists as the name for require
    • I find check, verify, require meaningless; they don't help me pick what to do - kinda like the words process or manage being useless in a name?
    • for me a predicate that processes stuff but is filter, but it's kinda like you're checking a la Option.exists/Result.exists
    • that reads well but is it also a bit meaningless?
  • not sure if having a reqFilter everywhere is enough of a win - basically it's to stop having to type/scan parens when Thing.req (Req.filter predicate) gets messy (though it is the deprecation path for require, but we could easily refer them to Please use Thing.req (Req.filter predicate) instead of Thing.require predicate)
  • would adding a Req.pick make sense - i.e. a predicate that can reject or transform? Would seem more useful than check (I have never used check and don't know how common these operations are when you've had them in your toolbox for ages)

I'm definitely ready to get my life back from this rabbit hole though; I can imagine the review burden/desire to tell me where to go must be strong? If you can give me a steer on whether you think this can fly and/or the degree of to/fro you feel might be needed, I'll do some dogfooding internally to validate?

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

One or more issues must be addressed before approval.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 4 High severity · 6 Low severity

Open (10)
Resolved since last review (31)

And 11 more resolved.

Comment thread src/FsToolkit.ErrorHandling.JobResult/JobResultOption.fs Outdated
Comment thread RELEASE_NOTES.md Outdated
Comment thread gitbook/cancellableTaskResult/others.md Outdated
Comment thread gitbook/result/reqFunctions.md Outdated
Comment thread gitbook/taskValueOption/either.md Outdated
Comment thread src/FsToolkit.ErrorHandling/AsyncOption.fs Outdated
Comment thread src/FsToolkit.ErrorHandling/ResultOption.fs Outdated

This branch has not been deployed

No deployments
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.

Async/TaskResult.bindResult ?

3 participants