You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Continues the ongoing struct-optionals effort by moving the resolver and execution pipeline from reference option to struct voption, removing allocations on the hottest path of query execution.
This is a focused follow-up to the already-merged active-pattern conversion (#610) and is orthogonal to the optional-parameter work in #604 — reviewers of those PRs do not need to re-review that ground here.
ResolverResult<'T> now carries IObservable<GQLDeferredResponseContent> voption instead of option.
executeResolvers accepts AsyncVal<obj voption> instead of AsyncVal<obj option>.
Pattern matches and call sites updated to ValueSome / ValueNone.
Deferred/streamed resolver plumbing threads voption end-to-end, so no boxing round-trip remains between the resolver and the response writer.
Shared helpers
Extensions.fs: new ValueOption.mergeWith and ValueOption.unwrap, mirroring the existing Option counterparts.
ObjAndStructConversions.fs: added Array.vtryPick.
Reflection.fs: objectOptionCast is now accessible and documented; new toValueOption lifts an obj into obj voption unless it is already an option; the now-unused optionCast wrapper was dropped.
Client
BaseTypes.fs, GraphQLClient.fs, ReflectionPatterns.fs, Serialization.fs updated to the voption-based helpers, plus assorted string-formatting and XML-doc cleanups.
Compatibility notes
ResolverResult<'T> is public and its shape changed (option -> voption). Code that destructures it directly needs a mechanical Some/None -> ValueSome/ValueNone update.
Reflection.optionCast was removed; objectOptionCast (returning voption) is the replacement, and ValueOption.toOption recovers the old shape if needed.
* Refactored codebase to use F# `voption` (value option) instead of option for improved performance and clarity, especially in resolver and execution logic.
* Replaced `Option` functions with `ValueOption` equivalents, added `vtryPick` for arrays and extended `vtryFind` for lists.
* Updated pattern matching and function signatures to use `ValueSome`/`ValueNone`.
* Changed resolver result types to use `IObservable<_> voption`.
* Improved error handling, string formatting, and added `objectOptionCast`/`toValueOption` helpers in `Reflection.fs`.
* Updated code comments and XML docs.
* Ensured backward compatibility and better nullability handling.
* Refactored and reorganized collection and option helpers from `Extensions.fs` and `ObjAndStructConversions.fs` into `Helpers/CollectionExtensions.fs`.
* Moved `kvp`, `kvpObj`, and `IDictionary` extension methods to the new file.
* Removed redundant implementations from the original files.
* Updated usages to reference the new module and clarified documentation comments.
* Ensured helpers are internal or auto-opened for seamless usage.
xperiandri
changed the title
Switch resolver and execution logic to voption
Switched resolver and execution logic to voptionSep 19, 2026
This helper is now used by the execution path (Execution.fs:44), but implementing it through Seq.where and Seq.vtryHead creates a lazy sequence/enumerator for every list lookup. That can allocate more than the previous List.tryFind and undermines the PR's allocation-reduction goal; implement the voption lookup with a direct tail-recursive loop over the list instead.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Continues the ongoing struct-optionals effort by moving the resolver and execution pipeline from reference
optionto structvoption, removing allocations on the hottest path of query execution.This is a focused follow-up to the already-merged active-pattern conversion (#610) and is orthogonal to the optional-parameter work in #604 — reviewers of those PRs do not need to re-review that ground here.
What changed
Execution pipeline (
src/FSharp.Data.GraphQL.Server/Execution.fs)ResolverResult<'T>now carriesIObservable<GQLDeferredResponseContent> voptioninstead ofoption.executeResolversacceptsAsyncVal<obj voption>instead ofAsyncVal<obj option>.ValueSome/ValueNone.voptionend-to-end, so no boxing round-trip remains between the resolver and the response writer.Shared helpers
Extensions.fs: newValueOption.mergeWithandValueOption.unwrap, mirroring the existingOptioncounterparts.ObjAndStructConversions.fs: addedArray.vtryPick.Reflection.fs:objectOptionCastis now accessible and documented; newtoValueOptionlifts anobjintoobj voptionunless it is already an option; the now-unusedoptionCastwrapper was dropped.Client
BaseTypes.fs,GraphQLClient.fs,ReflectionPatterns.fs,Serialization.fsupdated to thevoption-based helpers, plus assorted string-formatting and XML-doc cleanups.Compatibility notes
ResolverResult<'T>is public and its shape changed (option->voption). Code that destructures it directly needs a mechanicalSome/None->ValueSome/ValueNoneupdate.Reflection.optionCastwas removed;objectOptionCast(returningvoption) is the replacement, andValueOption.toOptionrecovers the old shape if needed.Validation
dotnet build FSharp.Data.GraphQL.slnx— 0 errors, 0 warnings.dotnet test tests/FSharp.Data.GraphQL.Tests— 667 passed, 0 failed, 5 skipped.Related
[<Struct>] ?optional parameters andvoptioninDefine/DefineRecand the introspection chain #604 — struct optional parameters (open, independent)