Open
Description
Replaces #129. TaskEx top level issue: #139
The default implementation of the Async.AwaitTask
methods in FSharp.Core
have some key shortcomings:
- when the
Task
faults, yielding an exception, that exception is typically (always?) wrapped in an egregiousAggregateException
- the default implementation does not abort/cancel when the ambient
CancellationToken
of theasync
expr within whichAsync.AwaitTask
is triggered - cancelling an Async computation should not just abort the processing, it should also propagate a
TaskCancelledException
to align with the behavior ofTask
While it can be argued that the current behavior is 'wrong', it's also obvious that breaking it would be untenable, and the semantic differences are beyond what one might cover with subtle overloads and/or adding optional arguments etc.
Current proposed APIs (will be updated inline based on any discussion below):
module Async =
let inline ofTask (t : Task<'t>) : Async<'t> = AwaitTaskCorrect t
let inline ofUnitTask (t : Task) : Async<'t> = AwaitTaskCorrect t
module Task =
let inline toAsync (t : Task<'t>) : Async<'t> = Async.ofTask t
NOTES:
- the above naming is taken from the helpers within
TaskSeq
, which are exposed in theFSharp.Control
namespace. NOTE the current implementations useAsync.AwaitTask
, but that was not as a conscious choice, and there is a desire` to fix at least some of the shortcomings noted - given the fact that
task
andasync
are now first class citizens ofFSharp.Core
, having anof/to
pairing would seem to make sense. This is open to debate; not sure the degree to which the prior art is consistent wrt this beyond collection types/modules - There is an
fslang-suggestion
regarding this. The purpose of this issue is as a placeholder for potentially filling the gap until this issue can be more thoroughly resolved inFSharp.Core
proper. - Equinox, Propulsion and FSharp.AWS.DynamoDB all have copies of the canonical impl in Eirik's fssnip. Not ruling out tweaks to the semantics, but ideally those libraries, and others, would all share the same semantics
- if this is handled as a
module
, one also frequently needs anofUnitTask
alongside as above (Async.AwaitTask is a pair of overloaded methods, which often brings its own issues with intellisense and error messages etc)