Skip to content
Merged
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 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>Task&lt;Result&lt;'ok,'error&gt;&gt; option</c> to an <c>Task&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