Description
Task Monad is not abide associativity low when one of the results is error
Repro steps
open FSharpx.Task
[<Test>]
let ``For Task monad associativity low must work even if some result contains error`` () =
let am x = returnM x
let bm x = returnM x
let cm _ = Task.FromException<int> (exn "error")
let l = am >=> bm >=> cm
let r = bm >=> cm >=> am
let rl = run (fun () -> l 1)
let rr = run (fun () -> r 1)
//Assert.Equal(rl, rr)
// Dunno how to compare object instances in F# correctly
match (rl, rr) with
| Error e1, Error e2 ->
// Will fail
Assert.Equal(e1.InnerException.Message, e2.InnerException.Message)
| _ -> Assert.True(false)```
Expected behavior
let l = am >=> bm >=> cm
let r = bm >=> cm >=> am
Left and right results must be the same.
Actual behavior
They are not, since
let inline bind (f: 'T -> Task<'U>) (m: Task<'T>) =
m.ContinueWith(fun (x: Task<_>) -> f x.Result).Unwrap()
implemented incorrectly, when x.Result will be invoked for task with error, it will throw new exception since x.Result doesn't have value. From example above r value indeed will have following exception
Exception inside AggregatedException inside AggregatedException. Every bind down the flow will stack up another aggregated exception to the error.
Known workarounds
let bind (f: _ -> Task<_>) (x: Task<_>) =
x.ContinueWith(fun (t: Task<_>) ->
if t.IsFaulted then Task.FromException<_>(t.Exception.InnerException) else f t.Result
) .Unwrap()
Related information
Probably this behavior is incorrect also for map and similar operators. Also possible the same situation when Task has canceled result
Description
Task Monad is not abide associativity low when one of the results is error
Repro steps
Expected behavior
Left and right results must be the same.
Actual behavior
They are not, since
implemented incorrectly, when x.Result will be invoked for task with error, it will throw new exception since
x.Resultdoesn't have value. From example abovervalue indeed will have following exceptionExceptioninsideAggregatedExceptioninsideAggregatedException. Every bind down the flow will stack up another aggregated exception to the error.Known workarounds
Related information
Probably this behavior is incorrect also for
mapand similar operators. Also possible the same situation when Task hascanceledresult