Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/release-notes/.FSharp.Core/11.0.100.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,7 @@

* Fix `Array.exists2` documentation examples to use equal-length arrays; the previous examples would throw `ArgumentException` at runtime instead of returning the documented `false`/`true` values. ([PR #19672](https://github.com/dotnet/fsharp/pull/19672))
* Move `Async.StartChild` to the "Starting Async Computations" docs category alongside `Async.StartChildAsTask`. ([Issue #19667](https://github.com/dotnet/fsharp/issues/19667))

### Added

* Added camelCase module-level functions `result`, `map`, `bind`, `ignore`, `catchWith`, `catch`, and `empty` in `module`s `Async`,`Task`, `ValueTask`, plus `Task.ofValueTask` and `ValueTask.ofTask`. ([LanguageSuggestion #1466](https://github.com/fsharp/fslang-suggestions/issues/1466), [PR #19844](https://github.com/dotnet/fsharp/pull/19844))
42 changes: 42 additions & 0 deletions src/FSharp.Core/async.fs
Original file line number Diff line number Diff line change
Expand Up @@ -2355,3 +2355,45 @@ module WebExtensions =
start = (fun userToken -> this.DownloadFileAsync(address, fileName, userToken)),
result = (fun _ -> ())
)

[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this be FSharpAsyncModule, pin AsyncModule or something else?
more important for Task and ValueTask of course

module Async =

[<CompiledName("Result")>]
let inline result (value: 'T) : Async<'T> =
async.Return value

[<CompiledName("Map")>]
let inline map ([<InlineIfLambda>] mapping: 'T -> 'U) (computation: Async<'T>) : Async<'U> =
async.Bind(computation, mapping >> async.Return)

[<CompiledName("Bind")>]
let inline bind ([<InlineIfLambda>] binder: 'T -> Async<'U>) (computation: Async<'T>) : Async<'U> =
async.Bind(computation, binder)

[<CompiledName("Ignore")>]
[<RequiresExplicitTypeArguments>]
let inline ignore<'T> (computation: Async<'T>) : Async<unit> =
Async.Ignore computation

[<CompiledName("CatchWith")>]
let catchWith (handler: exn -> 'T) (computation: Async<'T>) : Async<'T> =
async {
try
return! computation
with e ->
return handler e
}

[<CompiledName("Catch")>]
let catch (computation: Async<'T>) : Async<Result<'T, exn>> =
async {
try
let! v = computation
return Result.Ok v
with e ->
return Result.Error e
}

[<CompiledName("Empty")>]
let empty: Async<unit> = async.Zero()
119 changes: 119 additions & 0 deletions src/FSharp.Core/async.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -1547,3 +1547,122 @@ namespace Microsoft.FSharp.Control
module internal AsyncBuilderImpl =
val async : AsyncBuilder

/// <summary>Contains camelCase module-level functions for <see cref="T:Microsoft.FSharp.Control.FSharpAsync`1"/> computations.</summary>
///
/// <category index="1">Async Programming</category>
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module Async =

/// <summary>Creates an asynchronous computation that returns the given value.</summary>
///
/// <param name="value">The value to return.</param>
///
/// <returns>An asynchronous computation that returns <c>value</c> when executed.</returns>
///
/// <example id="async-result-1">
/// <code lang="fsharp">
/// let computation = Async.result 42
/// computation |> Async.RunSynchronously // evaluates to 42
/// </code>
/// </example>
[<CompiledName("Result")>]
val inline result: value: 'T -> Async<'T>

/// <summary>Creates an asynchronous computation that applies the mapping function to the result of the given computation.</summary>
///
/// <param name="mapping">The function to apply to the result.</param>
/// <param name="computation">The input computation.</param>
///
/// <returns>An asynchronous computation that applies <c>mapping</c> to the result of <c>computation</c>.</returns>
///
/// <example id="async-map-1">
/// <code lang="fsharp">
/// let computation = Async.result 21 |> Async.map (fun x -> x * 2)
/// computation |> Async.RunSynchronously // evaluates to 42
/// </code>
/// </example>
[<CompiledName("Map")>]
val inline map: mapping: ('T -> 'U) -> computation: Async<'T> -> Async<'U>

/// <summary>Creates an asynchronous computation that passes the result of the given computation to the binder function.</summary>
///
/// <param name="binder">A function that takes the result of the computation and returns a new asynchronous computation.</param>
/// <param name="computation">The input computation.</param>
///
/// <returns>An asynchronous computation that performs a monadic bind on the result of <c>computation</c>.</returns>
///
/// <example id="async-bind-1">
/// <code lang="fsharp">
/// let computation = Async.result 21 |> Async.bind (fun x -> Async.result (x * 2))
/// computation |> Async.RunSynchronously // evaluates to 42
/// </code>
/// </example>
[<CompiledName("Bind")>]
val inline bind: binder: ('T -> Async<'U>) -> computation: Async<'T> -> Async<'U>

/// <summary>Creates an asynchronous computation that runs the given computation and ignores its result.</summary>
///
/// <param name="computation">The input computation.</param>
///
/// <returns>A computation that is equivalent to the input computation, but disregards the result.</returns>
///
/// <example id="async-ignore-1">
/// <code lang="fsharp">
/// let readFile filename numBytes =
/// async {
/// use file = System.IO.File.OpenRead(filename)
/// do! file.AsyncRead(numBytes) |> Async.ignore&lt;int&gt;
/// }
/// </code>
/// </example>
[<CompiledName("Ignore")>]
[<RequiresExplicitTypeArguments>]
val inline ignore<'T> : computation: Async<'T> -> Async<unit>

/// <summary>Creates an asynchronous computation that runs the given computation.
/// If it raises an exception, the handler function is called with the exception and its result is returned.</summary>
///
/// <param name="handler">A function to handle exceptions, returning a recovery value.</param>
/// <param name="computation">The input computation.</param>
///
/// <returns>An asynchronous computation that returns the result of <c>computation</c>, or the result of <c>handler</c> if an exception is raised.</returns>
///
/// <example id="async-catchwith-1">
/// <code lang="fsharp">
/// let safeDiv x y =
/// async { return x / y }
/// |> Async.catchWith (fun _ -> 0)
/// safeDiv 10 0 |> Async.RunSynchronously // evaluates to 0
/// </code>
/// </example>
[<CompiledName("CatchWith")>]
val catchWith: handler: (exn -> 'T) -> computation: Async<'T> -> Async<'T>

/// <summary>Creates an asynchronous computation that runs the given computation and returns its result as <c>Ok</c>,
/// or returns <c>Error</c> with the exception if one is raised.</summary>
///
/// <param name="computation">The input computation.</param>
///
/// <returns>An asynchronous computation that returns <c>Ok</c> of the result or <c>Error</c> of the exception.</returns>
///
/// <example id="async-catch-1">
/// <code lang="fsharp">
/// let safeDiv x y =
/// async { return x / y } |> Async.catch
/// safeDiv 10 2 |> Async.RunSynchronously // evaluates to Ok 5
/// safeDiv 10 0 |> Async.RunSynchronously // evaluates to Error (DivideByZeroException ...)
/// </code>
/// </example>
[<CompiledName("Catch")>]
val catch: computation: Async<'T> -> Async<Result<'T, exn>>

/// <summary>An asynchronous computation that returns <c>unit</c>. This is equivalent to <c>async.Zero()</c>.</summary>
///
/// <example id="async-empty-1">
/// <code lang="fsharp">
/// Async.empty |> Async.RunSynchronously // evaluates to ()
/// </code>
/// </example>
[<CompiledName("Empty")>]
val empty: Async<unit>

156 changes: 156 additions & 0 deletions src/FSharp.Core/tasks.fs
Original file line number Diff line number Diff line change
Expand Up @@ -716,3 +716,159 @@ module LowPlusPriority =
this.Bind(computation, fun (result2: ^TResult2) -> this.Return struct (result1, result2))
)
)

namespace Microsoft.FSharp.Control

open System.Threading.Tasks
open Microsoft.FSharp.Core
open TaskBuilder
open Microsoft.FSharp.Control.TaskBuilderExtensions
open Microsoft.FSharp.Control.TaskBuilderExtensions.LowPriority
open Microsoft.FSharp.Control.TaskBuilderExtensions.HighPriority

[<RequireQualifiedAccess>]
module Task =

[<CompiledName("Result")>]
let inline result (value: 'T) : Task<'T> =
Task.FromResult value

[<CompiledName("Empty")>]
let empty: Task<unit> = result ()

[<CompiledName("Map")>]
let inline map ([<InlineIfLambda>] mapping: 'T -> 'U) (task: Task<'T>) : Task<'U> =
if task.Status = TaskStatus.RanToCompletion then
result (mapping task.Result)
else
TaskBuilder.task {
let! v = task
return mapping v
}

[<CompiledName("Bind")>]
let inline bind ([<InlineIfLambda>] binder: 'T -> Task<'U>) (task: Task<'T>) : Task<'U> =
if task.Status = TaskStatus.RanToCompletion then
binder task.Result
else
TaskBuilder.task {
let! v = task
return! binder v
}

[<CompiledName("Ignore")>]
[<RequiresExplicitTypeArguments>]
let inline ignore<'T> (task: Task<'T>) : Task<unit> =
if task.Status = TaskStatus.RanToCompletion then
empty
else
map ignore task

[<CompiledName("CatchWith")>]
let inline catchWith ([<InlineIfLambda>] handler: exn -> 'T) (task: Task<'T>) : Task<'T> =
if task.Status = TaskStatus.RanToCompletion then
task
else
TaskBuilder.task {
try
return! task
with e ->
return handler e
}

[<CompiledName("Catch")>]
let catch (task: Task<'T>) : Task<Result<'T, exn>> =
if task.Status = TaskStatus.RanToCompletion then
result (Ok task.Result)
else
TaskBuilder.task {
try
let! v = task
return Ok v
with e ->
return Error e
}
Comment thread
bartelink marked this conversation as resolved.

#if NETSTANDARD2_1
[<CompiledName("OfValueTask")>]
let inline ofValueTask (valueTask: ValueTask<'T>) : Task<'T> =
valueTask.AsTask()
#endif

#if NETSTANDARD2_1
[<RequireQualifiedAccess>]
module ValueTask =

[<CompiledName("Result")>]
let inline result (value: 'T) : ValueTask<'T> =
ValueTask<'T>(value)

[<CompiledName("Map")>]
let inline map ([<InlineIfLambda>] mapping: 'T -> 'U) (task: ValueTask<'T>) : ValueTask<'U> =
if task.IsCompletedSuccessfully then
ValueTask<'U>(mapping task.Result)
else
let t: Task<'U> =
TaskBuilder.task {
let! v = task
return mapping v
}

ValueTask<'U>(t)

[<CompiledName("Bind")>]
let inline bind ([<InlineIfLambda>] binder: 'T -> ValueTask<'U>) (task: ValueTask<'T>) : ValueTask<'U> =
if task.IsCompletedSuccessfully then
binder task.Result
else
let t: Task<'U> =
TaskBuilder.task {
let! v = task
return! binder v
}

ValueTask<'U>(t)

[<CompiledName("Ignore")>]
[<RequiresExplicitTypeArguments>]
let inline ignore<'T> (task: ValueTask<'T>) : ValueTask<unit> =
map ignore task

[<CompiledName("CatchWith")>]
let inline catchWith ([<InlineIfLambda>] handler: exn -> 'T) (task: ValueTask<'T>) : ValueTask<'T> =
if task.IsCompletedSuccessfully then
task
else
let t: Task<'T> =
TaskBuilder.task {
try
return! task
with e ->
return handler e
}

ValueTask<'T>(t)

[<CompiledName("Catch")>]
let catch (task: ValueTask<'T>) : ValueTask<Result<'T, exn>> =
if task.IsCompletedSuccessfully then
ValueTask<Result<'T, exn>>(Ok task.Result)
else
let t: Task<Result<'T, exn>> =
TaskBuilder.task {
try
let! v = task
return Ok v
with e ->
return Error e
}

ValueTask<Result<'T, exn>>(t)

[<CompiledName("Empty")>]
let empty: ValueTask<unit> = result ()

[<CompiledName("OfTask")>]
let inline ofTask (task: Task<'T>) : ValueTask<'T> =
ValueTask<'T>(task)
#endif
Loading
Loading