Skip to content

Commit 9965d1f

Browse files
authored
Add TaskValueOption module, operators, and CE + tests and documentation (#329)
* Add TaskValueOption module, operators, and CE + tests and documentation * Fix FS3264 in ValueTaskOptionCE.fs
1 parent 2cdaff1 commit 9965d1f

18 files changed

+2361
-0
lines changed

gitbook/SUMMARY.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,15 @@
166166
* [map](taskOption/map.md)
167167
* [Other Functions](taskOption/others.md)
168168
* [zip](taskOption/zip.md)
169+
170+
* TaskValueOption
171+
* [apply](taskValueOption/apply.md)
172+
* [bind](taskValueOption/bind.md)
173+
* [Computation Expression](taskValueOption/ce.md)
174+
* [either](taskValueOption/either.md)
175+
* [map](taskValueOption/map.md)
176+
* [Other Functions](taskValueOption/others.md)
177+
* [zip](taskValueOption/zip.md)
169178

170179
* TaskResult
171180
* [apply](taskResult/apply.md)

gitbook/taskValueOption/apply.md

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# TaskValueOption.apply
2+
3+
Namespace: `FsToolkit.ErrorHandling`
4+
5+
Function Signature:
6+
7+
```fsharp
8+
Task<('a -> 'b) voption> -> Task<'a voption> -> Task<'b voption>
9+
```
10+
11+
## Examples
12+
13+
Take the following function for example
14+
15+
```fsharp
16+
// string -> int
17+
let characterCount (s: string) = s.Length
18+
```
19+
20+
### Example 1
21+
22+
```fsharp
23+
let result =
24+
TaskValueOption.valueSome "foo" // Task<string voption>
25+
|> TaskValueOption.apply (TaskValueOption.valueSome characterCount) // Task<int voption>
26+
27+
// task { ValueSome 3 }
28+
```
29+
30+
### Example 2
31+
32+
```fsharp
33+
let result =
34+
Task.singleton ValueNone // Task<string voption>
35+
|> TaskValueOption.apply (TaskValueOption.valueSome characterCount) // Task<int voption>
36+
37+
// task { ValueNone }
38+
```
39+
40+
### Example 3
41+
42+
```fsharp
43+
let result : Task<int voption> =
44+
TaskValueOption.valueSome "foo" // Task<string voption>
45+
|> TaskValueOption.apply (Task.singleton ValueNone) // Task<int voption>
46+
47+
// task { ValueNone }
48+
```

gitbook/taskValueOption/bind.md

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# TaskValueOption.bind
2+
3+
Namespace: `FsToolkit.ErrorHandling`
4+
5+
## Function Signature
6+
7+
```fsharp
8+
('input -> Task<'output voption>) -> Task<'input voption> -> Task<'output voption>
9+
```
10+
11+
## Examples
12+
13+
Take the following function for example
14+
15+
```fsharp
16+
type Account =
17+
{ EmailAddress : string
18+
Name : string }
19+
20+
// string -> Task<Account voption>
21+
let lookupAccountByEmail email = task {
22+
let john = { EmailAddress = "[email protected]"; Name = "John Johnson" }
23+
let jeff = { EmailAddress = "[email protected]"; Name = "Jeff Jefferson" }
24+
let jack = { EmailAddress = "[email protected]"; Name = "Jack Jackson" }
25+
26+
// Just a map lookup, but imagine we look up an account in our database
27+
let accounts = Map.ofList [
28+
29+
30+
31+
]
32+
33+
return
34+
accounts
35+
|> Map.tryFind email
36+
|> Option.toValueOption
37+
}
38+
```
39+
40+
### Example 1
41+
42+
```fsharp
43+
let taskOpt : Task<Account voption> =
44+
TaskValueOption.valueSome "[email protected]" // Task<string voption>
45+
|> TaskValueOption.bind lookupAccountByEmail // Task<Account voption>
46+
47+
// task { ValueSome { EmailAddress = "[email protected]"; Name = "John Johnson" } }
48+
```
49+
50+
### Example 2
51+
52+
```fsharp
53+
let taskVOpt : Task<Account voption> =
54+
TaskValueOption.some "[email protected]" // Task<string voption>
55+
|> TaskValueOption.bind lookupAccountByEmail // Task<Account voption>
56+
57+
// task { ValueNone }
58+
```
59+
60+
### Example 3
61+
62+
```fsharp
63+
let taskVOpt : Task<Account voption> =
64+
Task.singleton ValueNone // Task<string voption>
65+
|> TaskValueOption.bind lookupAccountByEmail // Task<Account voption>
66+
67+
// task { ValueNone }
68+
```

gitbook/taskValueOption/ce.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
## TaskValueOption Computation Expression
2+
3+
Namespace: `FsToolkit.ErrorHandling`
4+
5+
## Examples:
6+
7+
### Example 1
8+
9+
Given a personId and an age, find a person and update their age.
10+
11+
```fsharp
12+
tryParseInt : string -> int voption
13+
tryFindPersonById : int -> Task<Person voption>
14+
updatePerson : Person -> Task<unit>
15+
```
16+
17+
```fsharp
18+
// Task<unit voption>
19+
let addResult = taskValueOption {
20+
let! personId = tryParseInt "3001"
21+
let! age = tryParseInt "35"
22+
let! person = tryFindPersonById personId
23+
let person = { person with Age = age }
24+
do! updatePerson person
25+
}
26+
```

gitbook/taskValueOption/either.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# TaskValueOption.either
2+
3+
Namespace: `FsToolkit.ErrorHandling`
4+
5+
## Function Signature
6+
7+
Provide two functions to execute depending on the value of the voption. If the voption is `ValueSome`, the first function will be executed. If the voption is `ValueNone`, the second function will be executed.
8+
9+
```fsharp
10+
(onValueSome : 'T -> Task<'output>)
11+
-> (onValueNone : Task<'output>)
12+
-> (input : Task<'T voption>)
13+
-> Task<'output>
14+
```
15+
16+
## Examples
17+
18+
### Example 1
19+
20+
```fsharp
21+
TaskValueOption.either (fun x -> task { x * 2 }) (task { 0 }) (TaskValueOption.valueSome 5)
22+
23+
// task { 10 }
24+
```
25+
26+
### Example 2
27+
28+
```fsharp
29+
TaskValueOption.either (fun x -> task { x * 2 }) (task { 0 }) ValueNone
30+
31+
// task { 0 }
32+
```
33+

gitbook/taskValueOption/map.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# TaskValueOption.map
2+
3+
Namespace: `FsToolkit.ErrorHandling`
4+
5+
Apply a function to the value of a task voption if it is `ValueSome`. If the option is `ValueNone`, return `ValueNone`.
6+
7+
## Function Signature
8+
9+
```fsharp
10+
('input -> 'output) -> Task<'input voption> -> Task<'output voption>
11+
```
12+
13+
## Examples
14+
15+
### Example 1
16+
17+
```fsharp
18+
TaskValueOption.map (fun x -> x + 1) (TaskValueOption.valueSome 1)
19+
20+
// task { ValueSome 2 }
21+
```
22+
23+
### Example 2
24+
25+
```fsharp
26+
TaskValueOption.map (fun x -> x + 1) (Task.singleton ValueNone)
27+
28+
// task { ValueNone }
29+
```
30+

gitbook/taskValueOption/others.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Other TaskValueOption Functions
2+
3+
## defaultValue
4+
5+
Returns the contained value if ValueSome, otherwise returns the provided value
6+
7+
### Function Signature
8+
9+
```fsharp
10+
'a -> Task<'a voption> -> Task<'a>
11+
```
12+
13+
## defaultWith
14+
15+
Returns the contained value if ValueSome, otherwise evaluates the given function and returns the result.
16+
17+
### Function Signature
18+
19+
```fsharp
20+
(unit -> 'a) -> Task<'a voption> -> Task<'a>
21+
```
22+
23+
## valueSome
24+
25+
Wraps the provided value in an Task<'a voption>
26+
27+
### Function Signature
28+
29+
```fsharp
30+
'a -> Task<'a voption>
31+
```
32+
33+

gitbook/taskValueOption/zip.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# TaskValueOption.zip
2+
3+
Namespace: `FsToolkit.ErrorHandling`
4+
5+
Takes two voptions and returns a tuple of the pair or ValueNone if either are ValueNone
6+
7+
## Function Signature
8+
9+
```fsharp
10+
Task<'left voption> -> Task<'right voption> -> Task<('left * 'right) voption>
11+
```
12+
13+
## Examples
14+
15+
### Example 1
16+
17+
```fsharp
18+
let left = TaskValueOption.valueSome 123
19+
let right = TaskValueOption.valueSome "abc"
20+
21+
TaskValueOption.zip left right
22+
// task { ValueSome (123, "abc") }
23+
```
24+
25+
### Example 2
26+
27+
```fsharp
28+
let left = TaskValueOption.valueSome 123
29+
let right = Task.singleton ValueNone
30+
31+
TaskValueOption.zip left right
32+
// task { ValueNone }
33+
```

src/FsToolkit.ErrorHandling/FsToolkit.ErrorHandling.fsproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,11 @@
4040
<Compile Include="AsyncOptionCE.fs" />
4141
<Compile Include="AsyncOptionOp.fs" />
4242
<Compile Include="TaskOption.fs" Condition="'$(FABLE_COMPILER)' != 'true'" />
43+
<Compile Include="TaskValueOption.fs" Condition="'$(FABLE_COMPILER)' != 'true'" />
4344
<Compile Include="TaskOptionCE.fs" Condition="'$(FABLE_COMPILER)' != 'true'" />
45+
<Compile Include="TaskValueOptionCE.fs" Condition="'$(FABLE_COMPILER)' != 'true'" />
4446
<Compile Include="TaskOptionOp.fs" Condition="'$(FABLE_COMPILER)' != 'true'" />
47+
<Compile Include="TaskValueOptionOp.fs" Condition="'$(FABLE_COMPILER)' != 'true'" />
4548
<Compile Include="AsyncResultOption.fs" />
4649
<Compile Include="AsyncResultOptionCE.fs" />
4750
<Compile Include="AsyncResultOptionOp.fs" />
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
namespace FsToolkit.ErrorHandling
2+
3+
open System.Threading.Tasks
4+
5+
6+
[<RequireQualifiedAccess>]
7+
module TaskValueOption =
8+
9+
let inline map ([<InlineIfLambda>] f) ar = Task.map (ValueOption.map f) ar
10+
11+
let inline bind ([<InlineIfLambda>] f) (ar: Task<_>) =
12+
task {
13+
let! opt = ar
14+
15+
let t =
16+
match opt with
17+
| ValueSome x -> f x
18+
| ValueNone -> task { return ValueNone }
19+
20+
return! t
21+
}
22+
23+
let inline valueSome x = task { return ValueSome x }
24+
25+
let inline apply f x =
26+
bind (fun f' -> bind (fun x' -> valueSome (f' x')) x) f
27+
28+
let inline zip x1 x2 =
29+
Task.zip x1 x2
30+
|> Task.map (fun (r1, r2) -> ValueOption.zip r1 r2)
31+
32+
33+
/// <summary>
34+
/// Returns result of running <paramref name="onValueSome"/> if it is <c>ValueSome</c>, otherwise returns result of running <paramref name="onValueNone"/>
35+
/// </summary>
36+
/// <param name="onValueSome">The function to run if <paramref name="input"/> is <c>ValueSome</c></param>
37+
/// <param name="onValueNone">The function to run if <paramref name="input"/> is <c>ValueNone</c></param>
38+
/// <param name="input">The input voption.</param>
39+
/// <returns>
40+
/// The result of running <paramref name="onValueSome"/> if the input is <c>ValueSome</c>, else returns result of running <paramref name="onValueNone"/>.
41+
/// </returns>
42+
let inline either
43+
([<InlineIfLambda>] onValueSome: 'input -> Task<'output>)
44+
([<InlineIfLambda>] onValueNone: unit -> Task<'output>)
45+
(input: Task<'input voption>)
46+
: Task<'output> =
47+
input
48+
|> Task.bind (
49+
function
50+
| ValueSome v -> onValueSome v
51+
| ValueNone -> onValueNone ()
52+
)
53+
54+
/// <summary>
55+
/// Gets the value of the option if the option is <c>Some</c>, otherwise returns the specified default value.
56+
/// </summary>
57+
/// <param name="value">The specified default value.</param>
58+
/// <param name="taskValueOption">The input option.</param>
59+
/// <returns>
60+
/// The option if the option is <c>Some</c>, else the default value.
61+
/// </returns>
62+
let inline defaultValue (value: 'value) (taskValueOption: Task<'value voption>) =
63+
taskValueOption
64+
|> Task.map (ValueOption.defaultValue value)
65+
66+
/// <summary>
67+
/// Gets the value of the voption if the voption is <c>ValueSome</c>, otherwise evaluates <paramref name="defThunk"/> and returns the result.
68+
/// </summary>
69+
/// <param name="defThunk">A thunk that provides a default value when evaluated.</param>
70+
/// <param name="taskValueOption">The input voption.</param>
71+
/// <returns>
72+
/// The voption if the option is <c>ValueSome</c>, else the result of evaluating <paramref name="defThunk"/>.
73+
/// </returns>
74+
let inline defaultWith
75+
([<InlineIfLambda>] defThunk: unit -> 'value)
76+
(taskValueOption: Task<'value voption>)
77+
: Task<'value> =
78+
taskValueOption
79+
|> Task.map (ValueOption.defaultWith defThunk)

0 commit comments

Comments
 (0)