Cannot use asyncResult computation expressions in resolvers
See original GitHub issueDescription
I am trying to use asyncResult
computation expressions (from the excellent FsToolkit.ErrorHandling) in an AsyncField
. This results in a run-time error when building the schema.
Repro steps
Here is a simplified script that shows the problem. You can run it with dotnet fsi ./Repro.fsx
#r "nuget: FSharp.Data.GraphQL.Server, 1.0.7"
#r "nuget: FsToolkit.ErrorHandling, 2.2.0"
open System
open FSharp.Data.GraphQL
type Product =
{
ID : Guid
Name : string
Weight : int
}
module Schema =
open FsToolkit.ErrorHandling
open FSharp.Data.GraphQL.Types
let private tryParseGuid (x : string) =
match System.Guid.TryParse x with
| (true, g) -> Ok g
| _ -> Error (sprintf "Invalid GUID: %s" x)
let productType =
Define.Object
(
"Product",
[
Define.Field ("id", ID, fun ctx x -> x.ID)
Define.Field ("name", String, fun ctx x -> x.Name)
Define.Field ("weight", Int, fun ctx x -> x.Weight)
]
)
let queryType =
Define.Object
(
name = "Query",
fields =
[
Define.AsyncField
(
"product",
Nullable productType,
"Fetch a product by ID",
[
Define.Input ("id", ID)
],
(fun ctx (x : Map<Guid, Product>) ->
async {
let! result =
asyncResult {
let! id =
ctx.Args
|> Map.tryFind "id"
|> Result.requireSome "Missing id argument"
|> Result.bind (fun x ->
match x with
| :? System.Guid as g -> Ok g
| :? System.String as s -> tryParseGuid s
| x -> Error (sprintf "Invalid id: %A" x)
)
let product = Map.tryFind id x
return product
}
match result with
| Ok x ->
return x
| Error message ->
return failwith message
})
)
]
)
let schema = Schema (Schema.queryType)
let executor = Executor (schema)
let crateOfOranges =
{
ID = Guid.NewGuid ()
Name = "Crate of Oranges"
Weight = 123
}
let data =
Map.empty
|> Map.add crateOfOranges.ID crateOfOranges
async {
let plan =
Parser.parse "query product($id : ID!) { product(id: $id) { id, name, weight } }"
let variables =
Map.empty
|> Map.add "id" (crateOfOranges.ID |> string :> obj)
let! result =
executor.AsyncExecute (plan, data, variables)
printfn "%A" result
}
|> Async.RunSynchronously
Expected behavior
Output like:
{ Content =
Direct
(seq
[[documentId, 303956704];
[data, { product: { id: d63d5b1a-31ee-4c61-a68c-06c7b576afcc,
name: "Crate of Oranges",
weight: 123 } }]],
[])
Metadata = map [] }
Actual behavior
Run-time error related to quotations. I’m not sure what’s going on since my code doesn’t appear to use quotations!
System.InvalidOperationException: Could not bind function AsyncResultBuilder.Source in type FsToolkit.ErrorHandling.AsyncResultCEExtensions
at Microsoft.FSharp.Quotations.PatternsModule.fail@1118[a](Type ty, String nm, Unit unitVar0) in F:\workspace\_work\1\s\src\fsharp\FSharp.Core\quotations.fs:line 1118
at Microsoft.FSharp.Quotations.PatternsModule.bindModuleFunctionWithCallSiteArgs$cont@1110(Type ty, String nm, Type[] argTypes, Type[] tyArgs, Unit unitVar) in F:\workspace\_work\1\s\src\fsharp\FSharp.Core\quotations.fs:line 1169
at Microsoft.FSharp.Quotations.PatternsModule.bindModuleFunctionWithCallSiteArgs(Type ty, String nm, FSharpList`1 argTypes, FSharpList`1 tyArgs) in F:\workspace\_work\1\s\src\fsharp\FSharp.Core\quotations.fs:line 1105
at Microsoft.FSharp.Quotations.PatternsModule.u_ModuleDefn@1572-1.Invoke(FSharpList`1 argTypes, FSharpList`1 tyargs) in F:\workspace\_work\1\s\src\fsharp\FSharp.Core\quotations.fs:line 1576
at Microsoft.FSharp.Quotations.PatternsModule.u_constSpec@1636.Invoke(FSharpList`1 argTypes, FSharpList`1 tyargs) in F:\workspace\_work\1\s\src\fsharp\FSharp.Core\quotations.fs:line 1636
at Microsoft.FSharp.Quotations.PatternsModule.u_Expr@1492.Invoke(BindingEnv env) in F:\workspace\_work\1\s\src\fsharp\FSharp.Core\quotations.fs:line 1501
...
Known workarounds
Write in async
computation expressions only. This can lead to less readable code in some situations.
async {
let id =
ctx.Args
|> Map.find "id"
|> (
function
| :? System.Guid as g -> g
| :? System.String as s -> System.Guid.Parse s
| x -> failwithf "Invalid id: %A" x
)
let product = Map.tryFind id x
return product
})
I would appreciate any work-arounds that allow the use of asyncResult
.
Related information
- Operating system:
Ubuntu 20.04
- Nuget packages:
FSharp.Data.GraphQL.Server (1.0.7)
,FsToolkit.ErrorHandling (2.2)
- .NET Runtime, CoreCLR or Mono Version
dotnet --version
5.0.103
Issue Analytics
- State:
- Created 2 years ago
- Comments:7 (4 by maintainers)
Top Results From Across the Web
Using let! inside match statements causes compilation error
As already explained, the problem is that asynchronous workflows only allow certain kinds of nestings - you cannot use let! inside an ...
Read more >Use binding in an asyncResult computation expression #2
I think I'm running into a bug where use-bound values in asyncResult computation expressions are disposed prematurely.
Read more >Computation Expression - FsToolkit.ErrorHandling - GitBook
A login flow can be implemented as below using the asyncResult CE and a few helpers: type LoginError = | InvalidUser. | InvalidPwd....
Read more >When should computational expressions be used? : r/fsharp
The pure logic is expressed in result{} CEs, the I/O has a lot of taskResult{}/asyncResult{} code. It's a killer feature.
Read more >Computation expressions: Introduction
Computation expressions seem to have a reputation for being abstruse and difficult to understand. On one hand, they're easy enough to use.
Read more >Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start FreeTop Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Top GitHub Comments
This will work:
Yes, they are. Allow to use directly