Closed jozefRudy closed 6 months ago
This works for me:
#r "nuget: FsToolkit.ErrorHandling, 4.15.1"
#r "nuget: FsToolkit.ErrorHandling.TaskResult, 4.15.1"
#r "nuget: Npgsql, 8.0.2"
open System.Threading
open System.Threading.Tasks
open FsToolkit.ErrorHandling
open Npgsql
type IDataSource =
abstract member OpenConnectionAsync : ?cancellationToken : CancellationToken -> ValueTask<NpgsqlConnection>
let foo (_dataSource : IDataSource) =
task {
use! conn = _dataSource.OpenConnectionAsync()
()
}
let bar (_dataSource : IDataSource) =
taskResult {
use! conn = _dataSource.OpenConnectionAsync()
()
}
Perhaps you have an old version of a package?
i think not. it's some intricate type problem beyond my current understanding.
What you wrote is right, hence if i simply write a function, it correctly behaves in both cases. Problem is implementing an interface.
while task version works, taskResult not ->
interface IStrategyRepository with
member this.Save(req: BacktestRequest, saved: DateTime) : Task<Result<int, exn>> =
taskResult {
try
use! conn = _dataSource.OpenConnectionAsync()
says
Type constraint mismatch. The type 'TaskResultCode<Result<int,'a>,exn,Result<int,'a>>' is not compatible with type 'TaskResultCode<int,exn,int>'
version placed simply in a class has exact same signature as implementation of interface
member StrategyRepository.Save2: req: BacktestRequest * saved: DateTime -> Task<Result<int,exn>>
while task works. simply placed inside class, they both work (only implementation of interface fails). that is puzzling for me.
Classes also work for me:
#r "nuget: FsToolkit.ErrorHandling, 4.15.1"
#r "nuget: FsToolkit.ErrorHandling.TaskResult, 4.15.1"
#r "nuget: Npgsql, 8.0.2"
open System
open System.Threading
open System.Threading.Tasks
open FsToolkit.ErrorHandling
open Npgsql
type IDataSource =
abstract member OpenConnectionAsync : ?cancellationToken : CancellationToken -> ValueTask<NpgsqlConnection>
type BacktestRequest = class end
type IStrategyRepository =
abstract member Save : req : BacktestRequest * saved : DateTime -> Task<Result<int, exn>>
type StrategyRepository1(_dataSource : IDataSource) =
interface IStrategyRepository with
member this.Save(req : BacktestRequest, saved : DateTime) =
task {
use! conn = _dataSource.OpenConnectionAsync()
return Ok 1
}
type StrategyRepository2(_dataSource : IDataSource) =
interface IStrategyRepository with
member this.Save(req : BacktestRequest, saved : DateTime) =
taskResult {
use! conn = _dataSource.OpenConnectionAsync()
return 1
}
Maybe I'm missing something?
i think i understand it more, maybe educational. I think try complicates it. Notice how with taskResult i need to use return!, and in task normal return is enough.
type StrategyRepository(dataSource: NpgsqlDataSource) =
let _dataSource: NpgsqlDataSource = dataSource
member this.Save2(req: BacktestRequest, saved: DateTime) : Task<Result<int, exn>> =
task {
try
use! conn = _dataSource.OpenConnectionAsync()
return Ok 1
with e ->
return Error e
}
interface IStrategyRepository with
member this.Save(req: BacktestRequest, saved: DateTime) : Task<Result<int, exn>> =
taskResult {
try
use! conn = _dataSource.OpenConnectionAsync()
return! Ok 1
with e ->
return! Error e
}
alternatively, i could just return 1
from taskResult version, but then condtion in return! Error e
is not satisfied (and return Error e
does not work either, nor return e
. so whole time this was a problem, while it showed that first statement inside taskResult was problematic, which it was not). So that was what was a problem, so it's not really 1:1 replacement in some cases.
Inside of a taskResult
, return! Ok 1
is equivalent to return 1
.
These examples might help with understanding:
let _ : Task<Result<int, string>> =
taskResult {
return 1
}
let _ : Task<Result<Result<int, string>, string>> =
taskResult {
return Ok 1
}
let _ : Task<Result<int, string>> =
taskResult {
return! Ok 1
}
yes but for error, there is no escaping using return! Error exn
i think?
let _: Task<Result<int, exn>> =
taskResult {
try
return 1
//or return! Ok 1
with e ->
return! Error e
// return e will not work
}
Thank you for this explanation, was assigning the error to a different cause.
yes but for error, there is no escaping using
return! Error exn
i think?let _: Task<Result<int, exn>> = taskResult { try return 1 //or return! Ok 1 with e -> return! Error e // return e will not work }
Thank you for this explanation, was assigning the error to a different cause.
Yes, this is by design.
return
in a taskResult
is only for the happy path. In your case, this is of type int
, so an Error exn
is a type mismatch.
Describe the bug
works just fine but
not so much. _dataSource.OpenConnectionAsync() has a signature
NpgsqlDataSource.OpenConnectionAsync(?cancellationToken: Threading.CancellationToken) : ValueTask<NpgsqlConnection>
Maybe i am overseeing something?