Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added cancellableTaskValidation feature, tests, and documentation #217

Merged
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
8 changes: 7 additions & 1 deletion gitbook/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@

* FsToolkit.ErrorHandling.AsyncSeq
* FsToolkit.ErrorHandling.IcedTasks
* TaskResult
* [CancellableTaskResult](cancellableTaskResult/index.md)
* [map](cancellableTaskResult/map.md)
* [map2](cancellableTaskResult/map2.md)
* [map3](cancellableTaskResult/map3.md)
Expand All @@ -94,6 +94,12 @@
* [Computation Expression](cancellableTaskResult/ce.md)
* [Operators](cancellableTaskResult/operators.md)
* [Other Functions](cancellableTaskResult/others.md)
* [CancellableTaskValidation](cancellableTaskValidation/index.md)
* [map2](cancellableTaskValidation/map2.md)
* [map3](cancellableTaskValidation/map3.md)
* [apply](cancellableTaskValidation/apply.md)
* [Computation Expression](cancellableTaskValidation/ce.md)
* [Operators](cancellableTaskValidation/operators.md)
* FsToolkit.ErrorHandling.JobResult
* JobResult
* [map](jobResult/map.md)
Expand Down
2 changes: 1 addition & 1 deletion gitbook/asyncValidation/map3.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
## AsyncAsyncValidation.map3
## AsyncValidation.map3
1eyewonder marked this conversation as resolved.
Show resolved Hide resolved

Namespace: `FsToolkit.ErrorHandling`

Expand Down
5 changes: 5 additions & 0 deletions gitbook/cancellableTaskResult/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
## CancellableTaskResult

Namespace: `FsToolkit.ErrorHandling`

This module provides utility functions and infix operators to work with `CancellableTask<Result<'a, 'b>>`.
13 changes: 13 additions & 0 deletions gitbook/cancellableTaskValidation/apply.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
## CancellableTaskValidation.apply

Namespace: `FsToolkit.ErrorHandling`

Function Signature:

```fsharp
CancellableTask<Result<('a -> 'b), 'c list>>
-> CancellableTask<Result<'a, 'c list>>
-> CancellableTask<Result<'b, 'c list>>
```

## Examples
41 changes: 41 additions & 0 deletions gitbook/cancellableTaskValidation/ce.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
## CancellableTaskValidation Computation Expression

Namespace: `FsToolkit.ErrorHandling`

The `CancellableTaskValidation` type is defined as:

```fsharp
type CancellableTaskValidation<'a,'err> = CancellableTask<Result<'a, 'err list>>
```

This CE can take advantage of the [and! operator](https://github.com/fsharp/fslang-suggestions/issues/579) to join multiple error results into a list.

## Examples

See [here](../validation/ce.md) for other validation-like examples

```fsharp
// Result<string, string> -> CancellableTask<Result<string, string>>
let downloadCancellableTask stuff = cancellableTask {
return stuff
}

// CancellableTaskValidation<string, string>
let result = cancellableTaskValidation {
let! x = downloadCancellableTask (Ok "I")
and! y = downloadCancellableTask (Ok "am")
and! z = downloadCancellableTask (Ok "cancellableTask!")
return sprintf "%s %s %s" x y z
}
// cancellableTask { return Ok "I am cancellableTask!" }

// CancellableTaskValidation<string, string>
let result = cancellableTaskValidation {
let! x = downloadCancellableTask (Error "Am")
and! y = downloadCancellableTask (Error "I")
and! z = downloadCancellableTask (Error "cancellableTask?")
return sprintf "%s %s %s" x y z
}

// cancellableTask { return Error [ "Am"; "I"; "cancellableTask?" ] }
```
5 changes: 5 additions & 0 deletions gitbook/cancellableTaskValidation/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
## CancellableTaskValidation

Namespace: `FsToolkit.ErrorHandling`

This module provides utility functions and infix operators to work with `CancellableTask<Result<'a, 'b list>>`.
16 changes: 16 additions & 0 deletions gitbook/cancellableTaskValidation/map2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
## CancellableTaskValidation.map2

Namespace: `FsToolkit.ErrorHandling`

Function Signature:

```fsharp
('a -> 'b -> 'c)
-> CancellableTask<Result<'a, 'd list>>
-> CancellableTask<Result<'b, 'd list>>
-> CancellableTask<Result<'c, 'd list>>
```

Like [Result.map2](../result/map2.md), but collects the errors from both arguments.

## Examples
17 changes: 17 additions & 0 deletions gitbook/cancellableTaskValidation/map3.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
## CancellableTaskValidation.map3

Namespace: `FsToolkit.ErrorHandling`

Function Signature:

```
('a -> 'b -> 'c -> 'd)
-> CancellableTask<Result<'a, 'e list>>
-> CancellableTask<Result<'b, 'e list>>
-> CancellableTask<Result<'c, 'e list>>
-> CancellableTask<Result<'d, 'e list>>
```

Like [Result.map3](../result/map3.md), but collects the errors from all arguments.

## Examples
13 changes: 13 additions & 0 deletions gitbook/cancellableTaskValidation/ofResult.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
## CancellableTaskValidation.ofResult

Namespace: `FsToolkit.ErrorHandling`

Function Signature:

```fsharp
Result<'a, 'b> -> CancellableTask<Result<'a, 'b list>>
```

Simply wraps the error in a list and makes the result a cancellable task.

## Examples
72 changes: 72 additions & 0 deletions gitbook/cancellableTaskValidation/operators.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
## CancellableTaskValidation Infix Operators

Namespace: `FsToolkit.ErrorHandling.Operator.CancellableTaskValidation`

FsToolkit.ErrorHandling provides the standard infix operators for `map` (`<!>`), `apply` (`<*>`), and `bind` (`>>=`) to work with `Result<'a, 'b list>`.

There are also variants of the `map` and `apply` operators (`<!^>` and `<*^>`) that accept `Result<'a, 'b>` (non-list) as the right-hand argument.

## Examples

### Example 1

Assume that we have following types and functions:

```fsharp
type Latitude = private Latitude of float with
// float -> CancellableTask<Result<Latitude, string list>>
static member TryCreate (lat : float) =
// ...

type Longitude = private Longitude of float with
// float -> CancellableTask<Result<Longitude, string list>>
static member TryCreate (lng : float) =
// ...

type Tweet = private Tweet of string with
// string -> CancellableTask<Result<Tweet, string list>>
static member TryCreate (tweet : string) =
// ...

// Latitude -> Longitude -> Tweet -> CreatePostRequest
let createPostRequest lat long tweet =
// ...
```

We can make use of the standard operators in the CancellableTaskValidation Operators module to perform the cancellableTaskValidation of the incoming request and capture all the errors as shown below:

```fsharp
open FsToolkit.ErrorHandling.Operator.CancellableTaskValidation

// float -> float -> string -> CancellableTask<Result<CreatePostRequest, string list>>
let validateCreatePostRequest lat lng tweet =
createPostRequest
<!> Latitude.TryCreate lat
<*> Longitude.TryCreate lng
<*> Tweet.TryCreate tweet
```

By using the `CancellableTaskValidation` operators instead of the `Result` operators, we collect all the errors:
```fsharp
validateCreatePostRequest 300. 400. ""
// Error
["300.0 is a invalid latitude value"
"400.0 is a invalid longitude value"
"Tweet shouldn't be empty"]
```

### Example 2

In the above example, all the `TryCreate` functions return a string list as the error type (`CancellableTask<Result<'a, string list>>`). If these functions instead returned `CancellableTask<Result<'a, string>>` (only a single error), we can use `<*^>` and `<!^>` to get the same result:


```fsharp
open FsToolkit.ErrorHandling.Operator.CancellableTaskValidation

// float -> float -> string -> CancellableTask<Result<CreatePostRequest, string list>>
let validateCreatePostRequest lat lng tweet =
createPostRequest
<!^> Latitude.TryCreate lat
<*^> Longitude.TryCreate lng
<*^> Tweet.TryCreate tweet
```
35 changes: 35 additions & 0 deletions gitbook/option/ce.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,38 @@ let addResult = option {
return add x y z
}
```

### Example 2

Example taken from [here](https://github.com/lukaszkrzywizna/learn-fsharp/blob/feature/9-finito/LearnFsharp/Lesson.fs#L436-L456)

```fsharp
// int -> int -> int option
let tryDivide x y =
match y with
| 0 -> None
| _ -> Some (x / y)

// int -> int -> int option
let multiplyIfEven x y =
match y % 2 with
| 0 -> Some <| x * y
| _ -> None

// int option
let resultNone = option {
let! result = tryDivide 5 3
let mapped = result * 5
return! multiplyIfEven 5 mapped
}
// result: None

// int option
let resultSome = option {
let! result = tryDivide 6 3
let mapped = result * 5
return! multiplyIfEven 5 mapped
}
// result: Some 50

```
1 change: 1 addition & 0 deletions gitbook/validation/ce.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ let addResult = validation {
and! z = tryParseInt "2"
return add x y z
}
// Ok 42
```

### Validation "Gotchas"
Expand Down
2 changes: 1 addition & 1 deletion paket.dependencies
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ nuget Microsoft.NET.Test.Sdk
nuget YoloDev.Expecto.TestSdk
nuget Fable.Mocha
nuget Fable.Core >= 4.0.0
nuget IcedTasks >= 0.5.1
nuget IcedTasks >= 0.7.0
framework: netstandard2.1, net7.0
storage: none
condition: netstandard2_1
Expand Down
4 changes: 2 additions & 2 deletions paket.lock
Original file line number Diff line number Diff line change
Expand Up @@ -969,10 +969,10 @@ NUGET
FSharp.Control.AsyncSeq (3.2.1)
FSharp.Core (>= 4.7.2)
Microsoft.Bcl.AsyncInterfaces (>= 5.0)
FSharp.Core (7.0)
FSharp.Core (7.0.300)
Hopac (0.5.1)
FSharp.Core (>= 4.5.2)
IcedTasks (0.5.1)
IcedTasks (0.7)
FSharp.Core (>= 7.0)
Microsoft.Bcl.AsyncInterfaces (6.0)
Microsoft.CodeCoverage (17.4) - restriction: || (== net7.0) (&& (== netstandard2.1) (>= net462)) (&& (== netstandard2.1) (>= netcoreapp3.1))
Expand Down
33 changes: 12 additions & 21 deletions src/FsToolkit.ErrorHandling.IcedTasks/CancellableTaskResultCE.fs
Original file line number Diff line number Diff line change
Expand Up @@ -607,13 +607,10 @@ module CancellableTaskResultCE =
)

[<NoEagerConstraintApplication>]
member inline this.Source< ^TaskLike, ^Awaiter, 'T, 'Error
when ^TaskLike: (member GetAwaiter: unit -> ^Awaiter)
and ^Awaiter :> ICriticalNotifyCompletion
and ^Awaiter: (member get_IsCompleted: unit -> bool)
and ^Awaiter: (member GetResult: unit -> 'T)>
(t: ^TaskLike)
: CancellableTaskResult<'T, 'Error> =
member inline this.Source<'Awaitable, 'Awaiter, 'TResult, 'Error
when Awaitable<'Awaitable, 'Awaiter, 'TResult>>
(t: 'Awaitable)
: CancellableTaskResult<'TResult, 'Error> =

cancellableTask {
let! r = t
Expand All @@ -622,13 +619,10 @@ module CancellableTaskResultCE =


[<NoEagerConstraintApplication>]
member inline this.Source< ^TaskLike, ^Awaiter, 'T, 'Error
when ^TaskLike: (member GetAwaiter: unit -> ^Awaiter)
and ^Awaiter :> ICriticalNotifyCompletion
and ^Awaiter: (member get_IsCompleted: unit -> bool)
and ^Awaiter: (member GetResult: unit -> 'T)>
(t: unit -> ^TaskLike)
: CancellableTaskResult<'T, 'Error> =
member inline this.Source<'Awaitable, 'Awaiter, 'TResult, 'Error
when Awaitable<'Awaitable, 'Awaiter, 'TResult>>
(t: unit -> 'Awaitable)
: CancellableTaskResult<'TResult, 'Error> =

cancellableTask {
let! r = t
Expand All @@ -637,13 +631,10 @@ module CancellableTaskResultCE =


[<NoEagerConstraintApplication>]
member inline this.Source< ^TaskLike, ^Awaiter, 'T, 'Error
when ^TaskLike: (member GetAwaiter: unit -> ^Awaiter)
and ^Awaiter :> ICriticalNotifyCompletion
and ^Awaiter: (member get_IsCompleted: unit -> bool)
and ^Awaiter: (member GetResult: unit -> 'T)>
(t: CancellationToken -> ^TaskLike)
: CancellableTaskResult<'T, 'Error> =
member inline this.Source<'Awaitable, 'Awaiter, 'TResult, 'Error
when Awaitable<'Awaitable, 'Awaiter, 'TResult>>
(t: CancellationToken -> 'Awaitable)
: CancellableTaskResult<'TResult, 'Error> =

cancellableTask {
let! r = t
Expand Down
Loading