Skip to content

Commit ab803c7

Browse files
authored
Merge pull request #22 from mta-solutions/async-extensions
Remove internal scoping and add Async extension methods for validation contexts
2 parents 718458c + 22742c9 commit ab803c7

File tree

7 files changed

+154
-5
lines changed

7 files changed

+154
-5
lines changed

.github/workflows/release.yml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ jobs:
66
build:
77

88
runs-on: ubuntu-latest
9-
9+
1010
steps:
1111
- name: Checkout
1212
uses: actions/checkout@v2
@@ -18,7 +18,7 @@ jobs:
1818
1919
- name: Set VERSION variable from tag
2020
run: echo "VERSION=${GITHUB_REF/refs\/tags\/v/}" >> $GITHUB_ENV
21-
21+
2222
- name: Setup .NET
2323
uses: actions/setup-dotnet@v1
2424
with:
@@ -37,7 +37,9 @@ jobs:
3737
run: dotnet pack --configuration Release /p:NuGetVersion=${VERSION} /p:PackageVersion=${VERSION} --include-symbols --output .
3838

3939
- name: Push
40-
run: dotnet nuget push FSharp.Data.Validation.${VERSION}.nupkg --source https://api.nuget.org/v3/index.json --api-key ${MTA_NUGET_KEY} --symbol-source https://symbols.nuget.org/download/symbols --symbol-api-key ${MTA_NUGET_KEY}
40+
run: |
41+
dotnet nuget push FSharp.Data.Validation.${VERSION}.nupkg --source https://api.nuget.org/v3/index.json --api-key ${MTA_NUGET_KEY} --symbol-source https://symbols.nuget.org/download/symbols --symbol-api-key ${MTA_NUGET_KEY}
42+
dotnet nuget push FSharp.Data.Validation.Async.${VERSION}.nupkg --source https://api.nuget.org/v3/index.json --api-key ${MTA_NUGET_KEY} --symbol-source https://symbols.nuget.org/download/symbols --symbol-api-key ${MTA_NUGET_KEY}
4143
4244
env:
4345
MTA_NUGET_KEY: ${{ secrets.MTA_NUGET_KEY }}

FSharp.Data.Validation.sln

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "FSharp.Data.Validation.Test
1313
EndProject
1414
Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "FSharp.Data.Validation.Giraffe", "src\FSharp.Data.Validation.Giraffe\FSharp.Data.Validation.Giraffe.fsproj", "{99EA41F7-1E77-4DD2-9D2E-9F7BE20441FB}"
1515
EndProject
16+
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "FSharp.Data.Validation.Async", "src\FSharp.Data.Validation.Async\FSharp.Data.Validation.Async.fsproj", "{CF59CFDC-CE24-4F02-A454-2FE0CC1A9FA9}"
17+
EndProject
1618
Global
1719
GlobalSection(SolutionConfigurationPlatforms) = preSolution
1820
Debug|Any CPU = Debug|Any CPU
@@ -83,6 +85,18 @@ Global
8385
{99EA41F7-1E77-4DD2-9D2E-9F7BE20441FB}.Release|x64.Build.0 = Release|Any CPU
8486
{99EA41F7-1E77-4DD2-9D2E-9F7BE20441FB}.Release|x86.ActiveCfg = Release|Any CPU
8587
{99EA41F7-1E77-4DD2-9D2E-9F7BE20441FB}.Release|x86.Build.0 = Release|Any CPU
88+
{CF59CFDC-CE24-4F02-A454-2FE0CC1A9FA9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
89+
{CF59CFDC-CE24-4F02-A454-2FE0CC1A9FA9}.Debug|Any CPU.Build.0 = Debug|Any CPU
90+
{CF59CFDC-CE24-4F02-A454-2FE0CC1A9FA9}.Debug|x64.ActiveCfg = Debug|Any CPU
91+
{CF59CFDC-CE24-4F02-A454-2FE0CC1A9FA9}.Debug|x64.Build.0 = Debug|Any CPU
92+
{CF59CFDC-CE24-4F02-A454-2FE0CC1A9FA9}.Debug|x86.ActiveCfg = Debug|Any CPU
93+
{CF59CFDC-CE24-4F02-A454-2FE0CC1A9FA9}.Debug|x86.Build.0 = Debug|Any CPU
94+
{CF59CFDC-CE24-4F02-A454-2FE0CC1A9FA9}.Release|Any CPU.ActiveCfg = Release|Any CPU
95+
{CF59CFDC-CE24-4F02-A454-2FE0CC1A9FA9}.Release|Any CPU.Build.0 = Release|Any CPU
96+
{CF59CFDC-CE24-4F02-A454-2FE0CC1A9FA9}.Release|x64.ActiveCfg = Release|Any CPU
97+
{CF59CFDC-CE24-4F02-A454-2FE0CC1A9FA9}.Release|x64.Build.0 = Release|Any CPU
98+
{CF59CFDC-CE24-4F02-A454-2FE0CC1A9FA9}.Release|x86.ActiveCfg = Release|Any CPU
99+
{CF59CFDC-CE24-4F02-A454-2FE0CC1A9FA9}.Release|x86.Build.0 = Release|Any CPU
86100
EndGlobalSection
87101
GlobalSection(SolutionProperties) = preSolution
88102
HideSolutionNode = FALSE

README.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
- [Validating Nested Types](#validating-nested-types)
2222
- [Validating Collections](#validating-collections)
2323
- [Serializing The Proof Type](#serializing-the-proof-type)
24+
- [Validating Async Data](#validating-async-data)
2425
- [Validation Operations](#validation-operations)
2526
- [`refute*` Operations](#refute-operations)
2627
- [`refute`](#refute)
@@ -1065,6 +1066,52 @@ Here is an example of what it might look like.
10651066
}
10661067
```
10671068

1069+
### Validating Async Data
1070+
1071+
What if we need to validate data that is retrieved asynchronously?
1072+
There are three functions available in the `FSharp.Data.Validation.Async` package that can help with this:
1073+
1074+
- `bindToAsync`
1075+
- `bindAsync`
1076+
- `bindFromAsync`
1077+
1078+
The `bindToAsync` function is used to bind a value to an asynchronous computation.
1079+
The value is passed to the computation and the result is returned.
1080+
1081+
The `bindAsync` function is used to bind an asynchronous computation to a value.
1082+
The computation is executed and the result is passed to the function.
1083+
1084+
The `bindFromAsync` function is used to bind an asynchronous computation to another asynchronous computation.
1085+
The first computation is executed and the result is passed to the second computation.
1086+
1087+
Let's say we have a function that retrieves a user's data from a database.
1088+
We want to validate the data before we use it.
1089+
We can use the `bindToAsync` function to bind the data to a validation computation.
1090+
1091+
```fsharp
1092+
module Example
1093+
1094+
open FSharp.Data.Validation
1095+
open FSharp.Data.Validation.Async
1096+
1097+
let getUserData (id:int): Async<UserData> =
1098+
// get user data from database
1099+
1100+
let validateUserData (data:UserData): Proof<UserDataFailure, UserData> =
1101+
validation {
1102+
withValue data
1103+
// validate data
1104+
qed
1105+
} |> fromVCtx
1106+
1107+
let getUserDataAndValidate (id:int): Async<Proof<UserDataFailure, UserData>> =
1108+
getUserData id |> bindToAsync validateUserData
1109+
```
1110+
1111+
The `getUserDataAndValidate` function retrieves the user data and validates it.
1112+
The `bindToAsync` function is used to bind the data to the validation computation.
1113+
The result is an asynchronous computation that returns the validated data.
1114+
10681115
## Validation Operations
10691116

10701117
### `refute*` Operations
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net6.0</TargetFramework>
5+
<GenerateDocumentationFile>true</GenerateDocumentationFile>
6+
</PropertyGroup>
7+
8+
<ItemGroup>
9+
<Compile Include="VCtx.fs" />
10+
</ItemGroup>
11+
12+
<ItemGroup>
13+
<ProjectReference Include="..\FSharp.Data.Validation\FSharp.Data.Validation.fsproj" />
14+
</ItemGroup>
15+
16+
</Project>
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
namespace FSharp.Data.Validation
2+
3+
[<RequireQualifiedAccess>]
4+
module VCtx =
5+
/// <summary>
6+
/// Binds a function that returns an asynchronous computation to a validation context.
7+
/// </summary>
8+
/// <remarks>
9+
/// This function takes a function <c>fn</c> that transforms a value of type <c>'A</c> into an
10+
/// asynchronous computation of type <c>Async&lt;VCtx&lt;'F, 'B&gt;&gt;</c> and a validation context <c>c</c>
11+
/// of type <c>VCtx&lt;'F, 'A&gt;</c>. It returns an asynchronous computation of type <c>Async&lt;VCtx&lt;'F, 'B&gt;&gt;</c>.
12+
///
13+
/// The function handles the following cases:
14+
/// <list type="bullet">
15+
/// <item>
16+
/// <description><c>ValidCtx a</c>: Applies the function <c>fn</c> to <c>a</c> and returns the result.</description>
17+
/// </item>
18+
/// <item>
19+
/// <description><c>RefutedCtx (gfs, lfs)</c>: Returns the same <c>RefutedCtx</c> without applying the function.</description>
20+
/// </item>
21+
/// <item>
22+
/// <description><c>DisputedCtx (gfs, lfs, a)</c>: Applies the function <c>fn</c> to <c>a</c> and merges the results accordingly.</description>
23+
/// </item>
24+
/// </list>
25+
/// </remarks>
26+
/// <param name="fn">A function that takes a value of type <c>'A</c> and returns an asynchronous computation of type <c>Async&lt;VCtx&lt;'F, 'B&gt;&gt;</c>.</param>
27+
/// <param name="c">A validation context of type <c>VCtx&lt;'F, 'A&gt;</c>.</param>
28+
/// <returns>An asynchronous computation of type <c>Async&lt;VCtx&lt;'F, 'B&gt;&gt;</c>.</returns>
29+
let bindToAsync (fn:'A -> Async<VCtx<'F, 'B>>) (c: VCtx<'F, 'A>): Async<VCtx<'F, 'B>> =
30+
async {
31+
match c with
32+
| ValidCtx a -> return! fn a
33+
| RefutedCtx (gfs,lfs) -> return RefutedCtx (gfs,lfs)
34+
| DisputedCtx (gfs,lfs,a) ->
35+
let! b = fn a
36+
match b with
37+
| ValidCtx b -> return DisputedCtx (gfs,lfs,b)
38+
| DisputedCtx (gfs',lfs',b) -> return DisputedCtx (gfs @ gfs', Utilities.mergeFailures lfs lfs', b)
39+
| RefutedCtx (gfs',lfs') -> return RefutedCtx (gfs @ gfs', Utilities.mergeFailures lfs lfs')
40+
}
41+
42+
/// <summary>
43+
/// Binds a function that returns a validation context to an asynchronous computation.
44+
/// </summary>
45+
/// <remarks>
46+
/// This function takes a function <c>fn</c> that transforms a value of type <c>'A</c> into a validation context
47+
/// of type <c>VCtx&lt;'F, 'B&gt;</c> and an asynchronous computation <c>c</c> of type <c>Async&lt;VCtx&lt;'F, 'A&gt;&gt;</c>.
48+
/// It returns an asynchronous computation of type <c>Async&lt;VCtx&lt;'F, 'B&gt;&gt;</c>.
49+
/// </remarks>
50+
/// <param name="fn">A function that takes a value of type <c>'A</c> and returns a validation context of type <c>VCtx&lt;'F, 'B&gt;</c>.</param>
51+
/// <param name="c">An asynchronous computation of type <c>Async&lt;VCtx&lt;'F, 'A&gt;&gt;</c>.</param>
52+
/// <returns>An asynchronous computation of type <c>Async&lt;VCtx&lt;'F, 'B&gt;&gt;</c>.</returns>
53+
let bindAsync (fn:'A -> Async<VCtx<'F, 'B>>) (c: Async<VCtx<'F, 'A>>): Async<VCtx<'F, 'B>> =
54+
async {
55+
let! c' = c
56+
return! bindToAsync fn c'
57+
}
58+
59+
/// <summary>
60+
/// Binds a function that returns a validation context to a validation context.
61+
/// </summary>
62+
/// <remarks>
63+
/// This function takes a function <c>fn</c> that transforms a value of type <c>'A</c> into a validation context
64+
/// of type <c>VCtx&lt;'F, 'B&gt;</c> and a validation context <c>c</c> of type <c>VCtx&lt;'F, 'A&gt;</c>.
65+
/// It returns a validation context of type <c>VCtx&lt;'F, 'B&gt;</c>.
66+
/// </remarks>
67+
/// <param name="fn">A function that takes a value of type <c>'A</c> and returns a validation context of type <c>VCtx&lt;'F, 'B&gt;</c>.</param>
68+
/// <param name="c">A validation context of type <c>VCtx&lt;'F, 'A&gt;</c>.</param>
69+
/// <returns>A validation context of type <c>VCtx&lt;'F, 'B&gt;</c>.</returns>
70+
let bindFromAsync (fn:'A -> VCtx<'F, 'B>) (c: Async<VCtx<'F, 'A>>): Async<VCtx<'F, 'B>> =
71+
bindAsync (fn >> async.Return) c

src/FSharp.Data.Validation/Utilities.fs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
module internal FSharp.Data.Validation.Utilities
1+
module FSharp.Data.Validation.Utilities
22

33
// Given a sequence of options, return list of Some
44
let catOptions l = Seq.choose id l

src/FSharp.Data.Validation/VCtx.fs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ open System.Linq.Expressions
66
open FSharpPlus.Data
77

88
type VCtx<'F, 'A> =
9-
internal
109
| ValidCtx of 'A
1110
| DisputedCtx of 'F list * FailureMap<'F> * 'A
1211
| RefutedCtx of 'F list * FailureMap<'F>

0 commit comments

Comments
 (0)