From eae1a8f1ad27adb08766058be1947f02a15c9615 Mon Sep 17 00:00:00 2001 From: Matthew Watt Date: Tue, 4 Feb 2025 10:18:47 -0600 Subject: [PATCH 1/2] Add AsyncOption.orElse and orElseWith; add entries for Option.traverseAsync and sequenceAsync to SUMMARY.md; add documentation for the rest of the functions in module --- gitbook/SUMMARY.md | 11 ++ gitbook/asyncOption/apply.md | 49 ++++++++ gitbook/asyncOption/bind.md | 65 ++++++++++ gitbook/asyncOption/either.md | 30 +++++ gitbook/asyncOption/map.md | 30 +++++ gitbook/asyncOption/orElseFunctions.md | 111 ++++++++++++++++++ gitbook/asyncOption/others.md | 33 ++++++ src/FsToolkit.ErrorHandling/AsyncOption.fs | 49 ++++++++ .../AsyncOption.fs | 66 +++++++++++ 9 files changed, 444 insertions(+) create mode 100644 gitbook/asyncOption/apply.md create mode 100644 gitbook/asyncOption/bind.md create mode 100644 gitbook/asyncOption/either.md create mode 100644 gitbook/asyncOption/map.md create mode 100644 gitbook/asyncOption/orElseFunctions.md create mode 100644 gitbook/asyncOption/others.md diff --git a/gitbook/SUMMARY.md b/gitbook/SUMMARY.md index 9c149fc5..e8f4084d 100644 --- a/gitbook/SUMMARY.md +++ b/gitbook/SUMMARY.md @@ -42,8 +42,10 @@ * [map](option/map.md) * [map2](option/map2.md) * [map3](option/map3.md) + * [sequenceAsync](option/sequenceAsync.md) * [sequenceResult](option/sequenceResult.md) * [tee Functions](option/teeFunctions.md) + * [traverseAsync](option/traverseAsync.md) * [traverseResult](option/traverseResult.md) * [zip](option/zip.md) * Lists @@ -112,6 +114,15 @@ * [ofTask](asyncResult/ofTask.md) * [ofTaskAction](asyncResult/ofTaskAction.md) + * AsyncOption + * [apply](asyncOption/apply.md) + * [bind](asyncOption/bind.md) + * [Computation Expression](asyncOption/ce.md) + * [either](asyncOption/either.md) + * [map](asyncOption/map.md) + * [orElse Functions](asyncOption/orElseFunctions.md) + * [Other Functions](asyncOption/others.md) + * AsyncResultOption * [apply](asyncResultOption/apply.md) * [bind](asyncResultOption/bind.md) diff --git a/gitbook/asyncOption/apply.md b/gitbook/asyncOption/apply.md new file mode 100644 index 00000000..f2c8fd93 --- /dev/null +++ b/gitbook/asyncOption/apply.md @@ -0,0 +1,49 @@ +# AsyncOption.apply + +Namespace: `FsToolkit.ErrorHandling` + +Function Signature: + +```fsharp +Async<('a -> 'b) option> -> Async<'a option> + -> Async<'b option> +``` + +## Examples + +Take the following function for example + +```fsharp +// string -> int +let characterCount (s: string) = s.Length +``` + +### Example 1 + +```fsharp +let result = + AsyncOption.some "foo" // Async + |> AsyncOption.apply (AsyncOption.some characterCount) // Async + +// async { Some 3 } +``` + +### Example 2 + +```fsharp +let result = + Async.singleton None // Async + |> AsyncOption.apply (AsyncOption.some characterCount) // Async + +// async { None } +``` + +### Example 3 + +```fsharp +let result : Async = + AsyncOption.some "foo" // Async + |> AsyncOption.apply (Async.singleton None) // Async + +// async { None } +``` diff --git a/gitbook/asyncOption/bind.md b/gitbook/asyncOption/bind.md new file mode 100644 index 00000000..b7beccd6 --- /dev/null +++ b/gitbook/asyncOption/bind.md @@ -0,0 +1,65 @@ +# AsyncOption.bind + +Namespace: `FsToolkit.ErrorHandling` + +## Function Signature + +```fsharp +('TInput -> Async<'TOutput option>) -> Async<'TInput option> -> Async<'TOutput option> +``` + +## Examples + +Take the following function for example + +```fsharp +type Account = + { EmailAddress : string + Name : string } + +// string -> Async +let lookupAccountByEmail email = async { + let john = { EmailAddress = "john@test.com"; Name = "John Johnson" } + let jeff = { EmailAddress = "jeff@test.com"; Name = "Jeff Jefferson" } + let jack = { EmailAddress = "jack@test.com"; Name = "Jack Jackson" } + + // Just a map lookup, but imagine we look up an account in our database + let accounts = Map.ofList [ + ("john@test.com", john) + ("jeff@test.com", jeff) + ("jack@test.com", jack) + ] + + return Map.tryFind email accounts +} +``` + +### Example 1 + +```fsharp +let asyncOpt : Async = + AsyncOption.some "john@test.com" // Async + |> AsyncOption.bind lookupAccountByEmail // Async + +// async { Some { EmailAddress = "john@test.com"; Name = "John Johnson" } } +``` + +### Example 2 + +```fsharp +let asyncOpt : Async = + AsyncOption.some "jerry@test.com" // Async + |> AsyncOption.bind lookupAccountByEmail // Async + +// async { None } +``` + +### Example 3 + +```fsharp +let asyncOpt : Async = + Async.singleton None // Async + |> AsyncOption.bind lookupAccountByEmail // Async + +// async { None } +``` diff --git a/gitbook/asyncOption/either.md b/gitbook/asyncOption/either.md new file mode 100644 index 00000000..59d07c89 --- /dev/null +++ b/gitbook/asyncOption/either.md @@ -0,0 +1,30 @@ +# AsyncOption.either + +Namespace: `FsToolkit.ErrorHandling` + +## Function Signature + +Provide two functions to execute depending on the value of the option. If the option is `Some`, the first function will be executed. If the option is `None`, the second function will be executed. + +```fsharp +(onSome : 'T -> Async<'output>) -> (onNone : Async<'output>) -> (input : Async<'T option>) -> Async<'output> +``` + +## Examples + +### Example 1 + +```fsharp +AsyncOption.either (fun x -> async { x * 2 }) (async { 0 }) (AsyncOption.some 5) + +// async { 10 } +``` + +### Example 2 + +```fsharp +AsyncOption.either (fun x -> x * 2) (async { 0 }) None + +// async { 0 } +``` + diff --git a/gitbook/asyncOption/map.md b/gitbook/asyncOption/map.md new file mode 100644 index 00000000..3ca65c0e --- /dev/null +++ b/gitbook/asyncOption/map.md @@ -0,0 +1,30 @@ +# AsyncOption.map + +Namespace: `FsToolkit.ErrorHandling` + +Apply a function to the value of an async option if it is `Some`. If the option is `None`, return `None`. + +## Function Signature + +```fsharp +('TInput -> 'TOutput) -> AsyncOption<'TInput> -> AsyncOption<'TOutput> +``` + +## Examples + +### Example 1 + +```fsharp +AsyncOption.map (fun x -> x + 1) (AsyncOption.some 1) + +// async { Some 2 } +``` + +### Example 2 + +```fsharp +AsyncOption.map (fun x -> x + 1) (Async.singleton None) + +// async { None } +``` + diff --git a/gitbook/asyncOption/orElseFunctions.md b/gitbook/asyncOption/orElseFunctions.md new file mode 100644 index 00000000..723ac19a --- /dev/null +++ b/gitbook/asyncOption/orElseFunctions.md @@ -0,0 +1,111 @@ +# OrElse Functions + +## AsyncOption.orElse + +Namespace: `FsToolkit.ErrorHandling` + +Returns the option if the option is Some, otherwise returns the given option + +### Function Signature + +```fsharp +(ifNone : Option<'value>) -> (input : Option<'value>) + -> Option<'value> +``` + +### Examples + +#### Example 1 + +```fsharp +let asyncOption : AsyncOption = + AsyncOption.some 1 + |> AsyncOption.orElse (AsyncOption.some 2) + +// async { Some 1 } +``` + +#### Example 2 + +```fsharp +let asyncOption : AsyncOption = + AsyncOption.some 1 + |> AsyncOption.orElse (Async.singleton None) + +// async { Some 1 } +``` + +#### Example 3 + +```fsharp +let asyncOption : AsyncOption = + Async.singleton None + |> AsyncOption.orElse (Some 2) + +// async { Some 2 } +``` + +#### Example 4 + +```fsharp +let asyncOption : AsyncOption = + Async.singleton None + |> AsyncOption.orElse (Async.singleton None) + +// async { None } +``` + +## AsyncOption.orElseWith + +Namespace: `FsToolkit.ErrorHandling` + +Returns the option if the option is Some, otherwise evaluates the given function and returns the result. + +### Function Signature + +```fsharp +(ifNoneFunc : unit -> AsyncOption<'value>) -> (input : AsyncOption<'value>) + -> AsyncOption<'value> +``` + +### Examples + +#### Example 1 + +```fsharp +let asyncOption : AsyncOption = + AsyncOption.some 1 + |> AsyncOption.orElseWith (fun () -> AsyncOption.some 2) + +// async { Some 1 } +``` + +#### Example 2 + +```fsharp +let asyncOption : AsyncOption = + AsyncOption.some 1 + |> AsyncOption.orElseWith (fun () -> None) + +// async { Some 1 } +``` + +#### Example 3 + +```fsharp +let asyncOption : AsyncOption = + Async.singleton None + |> AsyncOption.orElseWith (fun () -> AsyncOption.some 2) + +// async { Some 2 } +``` + +#### Example 4 + +```fsharp +let asyncOption : AsyncOption = + Async.singleton None + |> AsyncOption.orElseWith (fun () -> Async.singleton None) + +// async { None } +``` diff --git a/gitbook/asyncOption/others.md b/gitbook/asyncOption/others.md new file mode 100644 index 00000000..a6265c0e --- /dev/null +++ b/gitbook/asyncOption/others.md @@ -0,0 +1,33 @@ +# Other AsyncOption Functions + +## defaultValue + +Returns the contained value if Some, otherwise returns the provided value + +### Function Signature + +```fsharp +'a -> Async<'a option> -> Async<'a> +``` + +## defaultWith + +Returns the contained value if Some, otherwise evaluates the given function and returns the result. + +### Function Signature + +```fsharp +(unit -> 'a) -> Async<'a option> -> Async<'a> +``` + +## some + +Wraps the provided value in an Async + +### Function Signature + +```fsharp +'a -> Async<'a option> +``` + + diff --git a/src/FsToolkit.ErrorHandling/AsyncOption.fs b/src/FsToolkit.ErrorHandling/AsyncOption.fs index f3725744..4b3001e9 100644 --- a/src/FsToolkit.ErrorHandling/AsyncOption.fs +++ b/src/FsToolkit.ErrorHandling/AsyncOption.fs @@ -76,3 +76,52 @@ module AsyncOption = : Async<'value> = asyncOption |> Async.map (Option.defaultWith defThunk) + + /// + /// Returns if it is Some, otherwise returns + /// + /// The value to use if is None + /// The input option. + /// + /// + /// + /// + /// None |> Async.singleton |> AsyncOption.orElse (AsyncOption.some "Second") // evaluates to Some ("Second") + /// None |> Async.singleton |> AsyncOption.orElse (None |> Async.singleton) // evaluates to None + /// AsyncOption.some "First" |> AsyncOption.orElse (AsyncOption.some "Second") // evaluates to Some ("First") + /// AsyncOption.some "First" |> AsyncOption.orElse (None |> Async.singleton) // evaluates to Some ("First") + /// + /// + /// + /// The option if the option is Some, else returns . + /// + let inline orElse + (ifNone: Async>) + (input: Async>) + : Async> = + Async.bind (Option.either some (fun _ -> ifNone)) input + + /// + /// Returns if it is Some, otherwise executes and returns the result. + /// + /// A function that provides an alternate option when evaluated. + /// The input option. + /// + /// is not executed unless is a None. + /// + /// + /// + /// None |> Async.singleton |> AsyncOption.orElseWith (fun _ -> AsyncOption.some "Second") // evaluates to Some ("Second") + /// None |> Async.singleton |> AsyncOption.orElseWith (fun _ -> None |> Async.singleton) // evaluates to None + /// AsyncOption.some "First" |> AsyncOption.orElseWith (fun _ -> AsyncOption.some "Second") // evaluates to Some ("First") + /// AsyncOption.some "First" |> AsyncOption.orElseWith (fun _ -> None |> Async.singleton) // evaluates to Ok ("First") + /// + /// + /// + /// The option if the option is Some, else the result of executing . + /// + let inline orElseWith + ([] ifNoneFunc: unit -> Async>) + (input: Async>) + : Async> = + Async.bind (Option.either some ifNoneFunc) input diff --git a/tests/FsToolkit.ErrorHandling.Tests/AsyncOption.fs b/tests/FsToolkit.ErrorHandling.Tests/AsyncOption.fs index a6c3bfa0..1d812a4e 100644 --- a/tests/FsToolkit.ErrorHandling.Tests/AsyncOption.fs +++ b/tests/FsToolkit.ErrorHandling.Tests/AsyncOption.fs @@ -169,6 +169,70 @@ let defaultWithTests = } ] +let orElseTests = + testList "AsyncOption.orElse Tests" [ + testCaseAsync "Some Some takes first Some" + <| async { + return! + AsyncOption.some "First" + |> AsyncOption.orElse (AsyncOption.some "Second") + |> Expect.hasAsyncSomeValue "First" + } + testCaseAsync "Some None takes first Some" + <| async { + return! + AsyncOption.some "First" + |> AsyncOption.orElse (Async.singleton None) + |> Expect.hasAsyncSomeValue "First" + } + testCaseAsync "None Some takes second Some" + <| async { + return! + Async.singleton None + |> AsyncOption.orElse (AsyncOption.some "Second") + |> Expect.hasAsyncSomeValue "Second" + } + testCaseAsync "None None returns None" + <| async { + return! + Async.singleton None + |> AsyncOption.orElse (Async.singleton None) + |> Expect.hasAsyncNoneValue + } + ] + +let orElseWithTests = + testList "AsyncOption.orElseWith Tests" [ + testCaseAsync "Some Some takes first Some" + <| async { + return! + AsyncOption.some "First" + |> AsyncOption.orElseWith (fun _ -> AsyncOption.some "Second") + |> Expect.hasAsyncSomeValue "First" + } + testCaseAsync "Some None takes first Some" + <| async { + return! + AsyncOption.some "First" + |> AsyncOption.orElseWith (fun _ -> Async.singleton None) + |> Expect.hasAsyncSomeValue "First" + } + testCaseAsync "None Some takes second Some" + <| async { + return! + Async.singleton None + |> AsyncOption.orElseWith (fun _ -> AsyncOption.some "Second") + |> Expect.hasAsyncSomeValue "Second" + } + testCaseAsync "None None returns None" + <| async { + return! + Async.singleton None + |> AsyncOption.orElseWith (fun _ -> Async.singleton None) + |> Expect.hasAsyncNoneValue + } + ] + let allTests = testList "Async Option Tests" [ mapTests @@ -179,4 +243,6 @@ let allTests = eitherTests defaultValueTests defaultWithTests + orElseTests + orElseWithTests ] From 3622aca02e399d35e328816b3ddd8ea90c550251 Mon Sep 17 00:00:00 2001 From: Matthew Watt Date: Tue, 4 Feb 2025 12:13:24 -0600 Subject: [PATCH 2/2] Cleanup typos in type signatures in documentation --- gitbook/asyncOption/map.md | 2 +- gitbook/asyncOption/orElseFunctions.md | 24 ++++++++++++------------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/gitbook/asyncOption/map.md b/gitbook/asyncOption/map.md index 3ca65c0e..5d860b16 100644 --- a/gitbook/asyncOption/map.md +++ b/gitbook/asyncOption/map.md @@ -7,7 +7,7 @@ Apply a function to the value of an async option if it is `Some`. If the option ## Function Signature ```fsharp -('TInput -> 'TOutput) -> AsyncOption<'TInput> -> AsyncOption<'TOutput> +('TInput -> 'TOutput) -> Async<'TInput option> -> Async<'TOutput option> ``` ## Examples diff --git a/gitbook/asyncOption/orElseFunctions.md b/gitbook/asyncOption/orElseFunctions.md index 723ac19a..46e467c3 100644 --- a/gitbook/asyncOption/orElseFunctions.md +++ b/gitbook/asyncOption/orElseFunctions.md @@ -9,8 +9,8 @@ Returns the option if the option is Some, otherwise returns the given option ### Function Signature ```fsharp -(ifNone : Option<'value>) -> (input : Option<'value>) - -> Option<'value> +(ifNone : Async<'value option>) -> (input : Async<'value option>) + -> Async<'value option> ``` ### Examples @@ -18,7 +18,7 @@ Returns the option if the option is Some, otherwise returns the given option #### Example 1 ```fsharp -let asyncOption : AsyncOption = +let asyncOption : Async = AsyncOption.some 1 |> AsyncOption.orElse (AsyncOption.some 2) @@ -28,7 +28,7 @@ let asyncOption : AsyncOption = #### Example 2 ```fsharp -let asyncOption : AsyncOption = +let asyncOption : Async = AsyncOption.some 1 |> AsyncOption.orElse (Async.singleton None) @@ -38,7 +38,7 @@ let asyncOption : AsyncOption = #### Example 3 ```fsharp -let asyncOption : AsyncOption = +let asyncOption : Async = Async.singleton None |> AsyncOption.orElse (Some 2) @@ -48,7 +48,7 @@ let asyncOption : AsyncOption = #### Example 4 ```fsharp -let asyncOption : AsyncOption = +let asyncOption : Async = Async.singleton None |> AsyncOption.orElse (Async.singleton None) @@ -64,8 +64,8 @@ Returns the option if the option is Some, otherwise evaluates the given function ### Function Signature ```fsharp -(ifNoneFunc : unit -> AsyncOption<'value>) -> (input : AsyncOption<'value>) - -> AsyncOption<'value> +(ifNoneFunc : unit -> Async<'value option>) -> (input : Async<'value option>) + -> Async<'value option> ``` ### Examples @@ -73,7 +73,7 @@ Returns the option if the option is Some, otherwise evaluates the given function #### Example 1 ```fsharp -let asyncOption : AsyncOption = +let asyncOption : Async = AsyncOption.some 1 |> AsyncOption.orElseWith (fun () -> AsyncOption.some 2) @@ -83,7 +83,7 @@ let asyncOption : AsyncOption = #### Example 2 ```fsharp -let asyncOption : AsyncOption = +let asyncOption : Async = AsyncOption.some 1 |> AsyncOption.orElseWith (fun () -> None) @@ -93,7 +93,7 @@ let asyncOption : AsyncOption = #### Example 3 ```fsharp -let asyncOption : AsyncOption = +let asyncOption : Async = Async.singleton None |> AsyncOption.orElseWith (fun () -> AsyncOption.some 2) @@ -103,7 +103,7 @@ let asyncOption : AsyncOption = #### Example 4 ```fsharp -let asyncOption : AsyncOption = +let asyncOption : Async = Async.singleton None |> AsyncOption.orElseWith (fun () -> Async.singleton None)