Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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 gitbook/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,13 @@
* [map3](option/map3.md)
* [sequenceAsync](option/sequenceAsync.md)
* [sequenceResult](option/sequenceResult.md)
* [sequenceTask](option/sequenceTask.md)
* [sequenceTaskResult](option/sequenceTaskResult.md)
* [tee Functions](option/teeFunctions.md)
* [traverseAsync](option/traverseAsync.md)
* [traverseResult](option/traverseResult.md)
* [traverseTask](option/traverseTask.md)
* [traverseTaskResult](option/traverseTaskResult.md)
* [zip](option/zip.md)
* Lists
* [traverseOptionM](option/traverseOptionM.md)
Expand Down
27 changes: 27 additions & 0 deletions gitbook/option/sequenceTask.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
## Option.sequenceTask

Namespace: `FsToolkit.ErrorHandling`

Function Signature:

```fsharp
Task<'a> option -> Task<'a option>
```

Note that `sequence` is the same as `traverse id`. See also [Option.traverseTask](traverseTask.md).

See also Scott Wlaschin's [Understanding traverse and sequence](https://fsharpforfunandprofit.com/posts/elevated-world-4/).

## Examples

### Example 1

```fsharp
let a1 : Task<int option> =
Option.sequenceTask (Some (Task.singleton 42))
// async { return Some 42 }

let a2 : Task<int option> =
Option.sequenceTask None
// async { return None }
```
31 changes: 31 additions & 0 deletions gitbook/option/sequenceTaskResult.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
## Option.sequenceTaskResult

Namespace: `FsToolkit.ErrorHandling`

Function Signature:

```fsharp
Task<Result<'a, 'e>> option -> Task<Result<'a option>, 'e>
```

Note that `sequence` is the same as `traverse id`. See also [Option.traverseTaskResult](traverseTaskResult.md).

See also Scott Wlaschin's [Understanding traverse and sequence](https://fsharpforfunandprofit.com/posts/elevated-world-4/).

## Examples

### Example 1

```fsharp
let r1 : Task<Result<int option, string>> =
Some (task { return Ok 42 }) |> Option.sequenceTaskResult
// task { return Ok (Some 42) }

let r2 : Task<Result<int option, string>> =
Some (task { return Error "something went wrong" }) |> Option.sequenceTaskResult
// task { return Error "something went wrong" }

let r3 : Task<Result<int option, string>> =
None |> Option.sequenceTaskResult
// task { return Ok None }
```
45 changes: 45 additions & 0 deletions gitbook/option/traverseTask.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
## Option.traverseTask

Namespace: `FsToolkit.ErrorHandling`

Function Signature:

```fsharp
('a -> Task<'b>) -> 'a option -> Task<'b option>
```

Note that `traverse` is the same as `map >> sequence`. See also [Option.sequenceTask](sequenceTask.md).

See also Scott Wlaschin's [Understanding traverse and sequence](https://fsharpforfunandprofit.com/posts/elevated-world-4/).

## Examples

### Example 1

Let's assume we have a type `Customer`:

```fsharp
type Customer = {
Id : int
Email : string
}
```

And we have a function called `getCustomerByEmail` that retrieves a `Customer` by email address asynchronously from some external source -- a database, a web service, etc:

```fsharp
// string -> Task<Customer>
let getCustomerByEmail email : Task<Customer> = task {
return { Id = 1; Email = "[email protected]" } // return a constant for simplicity
}
```

If we have a value of type `string option` and want to call the `getCustomerByEmail` function, we can achieve it using the `traverseTask` function as below:

```fsharp
Some "[email protected]" |> Option.traverseTask getCustomerByEmail
// task { return Some { Id = 1; Email = "[email protected]" } }

None |> Option.traverseTask getCustomerByEmail
// task { return None }
```
44 changes: 44 additions & 0 deletions gitbook/option/traverseTaskResult.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
## Option.traverseTaskResult

Namespace: `FsToolkit.ErrorHandling`

Function Signature:

```fsharp
('a -> Task<Result<'b,'c>>) -> 'a option -> Task<Result<'b option, 'c>>
```

Note that `traverse` is the same as `map >> sequence`. See also [Option.sequenceTaskResult](sequenceTaskResult.md).

See also Scott Wlaschin's [Understanding traverse and sequence](https://fsharpforfunandprofit.com/posts/elevated-world-4/).

## Examples

### Example 1

Say we have a function to get a number from a database (asynchronously), and multiply our input by that number if it's found:

```fsharp
let tryMultiplyWithDatabaseValue: float -> Task<Result<float, string>> = // ...
```

If we start with an optional value, then we could map this function using `Option.traverseTaskResult` as follows:

```fsharp
let input = Some 1.234

input // float option
|> Option.traverseTaskResult tryMultiplyWithDatabaseValue // Task<Result<float option, string>>
```

If we combine this with the [TaskResult computation expression](../taskResult/ce.md), we could directly `let!` the output:

```fsharp
taskResult {
let input = Some 1.234

let! output = // float option
input // float option
|> Option.traverseTaskResult tryMultiplyWithDatabaseValue // Task<Result<float option, string>>
}
```
65 changes: 65 additions & 0 deletions src/FsToolkit.ErrorHandling/Option.fs
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,71 @@ module Option =

opt

#if !FABLE_COMPILER
open System.Threading.Tasks

/// <summary>
/// Converts an <c>Option&lt;Task&lt;_&gt;&gt;</c> to an <c>Task&lt;Option&lt;_&gt;&gt;</c><br />
///
/// Documentation is found here: <see href="https://demystifyfp.gitbook.io/fstoolkit-errorhandling/fstoolkit.errorhandling/option/sequencetask" />
/// </summary>
let inline sequenceTask (optTask: Option<Task<'T>>) : Task<Option<'T>> =
task {
match optTask with
| Some tsk ->
let! x = tsk
return Some x
| None -> return None
}

/// <summary>
/// Maps a <c>Task</c> function over an <c>option</c>, returning a <c>Task&lt;'T option&gt;</c><br/>
///
/// Documentation is found here: <href>https://demystifyfp.gitbook.io/fstoolkit-errorhandling/fstoolkit.errorhandling/option/traversetask</href>
/// </summary>
/// <param name="f">The function to map over the <c>option</c>.</param>
/// <param name="opt">The <c>option</c> to map over.</param>
/// <returns>A <c>Task&lt;'T option&gt;</c> with the mapped value.</returns>
let inline traverseTask
([<InlineIfLambda>] f: 'T -> Task<'T>)
(opt: Option<'T>)
: Task<Option<'T>> =
sequenceTask ((map f) opt)

/// <summary>
/// Converts an <c>Async&lt;Result&lt;'ok,'error&gt;&gt; option</c> to an <c>Async&lt;Result&lt;'ok option,'error&gt;&gt;</c><br />
///
/// Documentation is found here: <see href="https://demystifyfp.gitbook.io/fstoolkit-errorhandling/fstoolkit.errorhandling/option/sequencetaskresult" />
/// </summary>
let inline sequenceTaskResult
(optTaskResult: Task<Result<'T, 'E>> option)
: Task<Result<'T option, 'E>> =
task {
match optTaskResult with
| Some taskRes ->
let! xRes = taskRes

return
xRes
|> Result.map Some
| None -> return Ok None
}

/// <summary>
/// Maps a <c>TaskResult</c> function over an <c>option</c>, returning a <c>Task&lt;Result&lt;'U option, 'E&gt;&gt;</c>.<br />
///
/// Documentation is found here: <see href="https://demystifyfp.gitbook.io/fstoolkit-errorhandling/fstoolkit.errorhandling/option/traversetaskresult" />
/// </summary>
/// <param name="f">The function to map over the <c>option</c>.</param>
/// <param name="opt">The <c>option</c> to map over.</param>
/// <returns>A <c>Task&lt;Result&lt;'U option, 'E&gt;&gt;</c> with the mapped value.</returns>
let inline traverseTaskResult
([<InlineIfLambda>] f: 'T -> Task<Result<'U, 'E>>)
(opt: 'T option)
: Task<Result<'U option, 'E>> =
sequenceTaskResult ((map f) opt)
#endif

/// <summary>
/// Converts a Option<Async<_>> to an Async<Option<_>>
///
Expand Down
Loading
Loading