mirror of
https://github.com/IBM/fp-go.git
synced 2025-09-01 19:56:12 +02:00
Compare commits
200 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
391754e5a6 | ||
|
598a7b261b | ||
|
f0f1a48965 | ||
|
e39e5e0920 | ||
|
9c7a5bb24b | ||
|
89bda4f500 | ||
|
15dffb3256 | ||
|
95fbd93696 | ||
|
6e0d5704bc | ||
|
0aa95e656b | ||
|
b3544a32fc | ||
|
9ad9b4a9bf | ||
|
74763bdadc | ||
|
9aa2ae041f | ||
|
d356fa3c89 | ||
|
f61507254d | ||
|
85f8071c75 | ||
|
76ae6f2bec | ||
|
f4f4fb306c | ||
|
747f477a96 | ||
|
045f4e8849 | ||
|
65e09e0e90 | ||
|
6ab6ff094b | ||
|
e6e35d643c | ||
|
01d490b710 | ||
|
01786a054b | ||
|
d0e4984b60 | ||
|
51ed1693a5 | ||
|
0afedbd7fe | ||
|
3f1bde219a | ||
|
6f91e91eb9 | ||
|
9f6b6d4968 | ||
|
79652d8474 | ||
|
a774d63e66 | ||
|
d86cf55a3d | ||
|
8150ae2a68 | ||
|
7daf65effc | ||
|
909f7c3bce | ||
|
5f0c644c6d | ||
|
9b3d9c6930 | ||
|
59381c1e50 | ||
|
358573cc20 | ||
|
e166806d1b | ||
|
02ec50c91d | ||
|
9e04974d0e | ||
|
2f99ca471a | ||
|
3e09a19d68 | ||
|
839ef47054 | ||
|
144b27233b | ||
|
668eb85aea | ||
|
b077fed094 | ||
|
7d3759619c | ||
|
c73467caf5 | ||
|
ca606767f3 | ||
|
fb82af9a69 | ||
|
57ad8c6466 | ||
|
5a9f405362 | ||
|
89c34254a9 | ||
|
a14feff1d6 | ||
|
f3642bad60 | ||
|
997627b318 | ||
|
1e1411c003 | ||
|
9ed9f8a171 | ||
|
e7428549e4 | ||
|
aef0048119 | ||
|
709d74b135 | ||
|
38c6541254 | ||
|
813b83b423 | ||
|
9139dedbbe | ||
|
5e15119653 | ||
|
986aa21055 | ||
|
3a4c46ec1e | ||
|
96c3ee20ff | ||
|
7af9acfd99 | ||
|
36eefbcd27 | ||
|
973138c822 | ||
|
12ef79184b | ||
|
5ac47440a1 | ||
|
3aa55c74d4 | ||
|
a6f55a199c | ||
|
2b500d15da | ||
|
599b8256b6 | ||
|
cf70b47984 | ||
|
7bceb856f8 | ||
|
49e89de783 | ||
|
a87de2f644 | ||
|
6d043d2752 | ||
|
1d02f21ff5 | ||
|
e82575fe08 | ||
|
5fcd0b1595 | ||
|
5caabf478c | ||
|
b7ec18c83e | ||
|
96686425fb | ||
|
1f675e08fa | ||
|
4d2f410c98 | ||
|
8f49c1328c | ||
|
2a1d5821db | ||
|
6abbdc5ee1 | ||
|
5fea9858a9 | ||
|
e6426c90c0 | ||
|
54ce59105e | ||
|
8bb006c741 | ||
|
b6efa35b03 | ||
|
35848900c0 | ||
|
3d54f99739 | ||
|
ffd9418cac | ||
|
acfcea59f4 | ||
|
b4bf511f03 | ||
|
211340952b | ||
|
56860425c4 | ||
|
c0b16c675b | ||
|
4b68e66528 | ||
|
5b7e5b153b | ||
|
d43fbeb375 | ||
|
57d507c1ba | ||
|
aa19857127 | ||
|
7766787cc1 | ||
|
6a9f38f990 | ||
|
e9584bc247 | ||
|
55252c5680 | ||
|
88bf35c4af | ||
|
da3c9683eb | ||
|
08d9fed9af | ||
|
83a0c6cdef | ||
|
9da484b79e | ||
|
e46cfd89d4 | ||
|
6a4cdfa891 | ||
|
93a7e9964b | ||
|
9ef98e1ec4 | ||
|
f1a730998d | ||
|
f129297045 | ||
|
b434f1fbf4 | ||
|
756e1336dc | ||
|
c629d18bb3 | ||
|
1eefc28ba6 | ||
|
af2915d98a | ||
|
e9f9c2777f | ||
|
895862a67e | ||
|
caf0574742 | ||
|
bace6f01eb | ||
|
254c63a16f | ||
|
655c8a9b1d | ||
|
c6d6be66e0 | ||
|
e313f95865 | ||
|
5f25317f97 | ||
|
f5aa2d6c15 | ||
|
7262820624 | ||
|
c8fc10358b | ||
|
1cd167541d | ||
|
ebe94d71dc | ||
|
7ef6eb524b | ||
|
7b82e87bf3 | ||
|
e8fdbe9f87 | ||
|
226c7039de | ||
|
943ae8e009 | ||
|
44c8441b07 | ||
|
600aeae770 | ||
|
f74a407294 | ||
|
b15ab38861 | ||
|
6532a83e82 | ||
|
7c12b72db1 | ||
|
dc894ad643 | ||
|
c902058320 | ||
|
bf33f4fb66 | ||
|
b4d2a5c6be | ||
|
705b71d95c | ||
|
34844bcfc2 | ||
|
9a9d13b066 | ||
|
da1449e680 | ||
|
865d9fe064 | ||
|
c5b1bae65a | ||
|
0a395f63ff | ||
|
26a7066de0 | ||
|
52823e2c8e | ||
|
503021c65e | ||
|
a2a6a41993 | ||
|
7da9de6f41 | ||
|
ff1b6faf84 | ||
|
38120764e7 | ||
|
a83c2aec49 | ||
|
03debd37c8 | ||
|
4f04344cda | ||
|
cf1c886f48 | ||
|
3e0eb99b88 | ||
|
cb15a3d9fc | ||
|
16535605f5 | ||
|
53f4e5ebd7 | ||
|
fb91fd5dc8 | ||
|
3ccafb5302 | ||
|
52b71ef4f3 | ||
|
5d77d5bb3d | ||
|
8ba8f852fa | ||
|
29d9882d2a | ||
|
f80ca31e14 | ||
|
8692078972 | ||
|
12a4f6801c | ||
|
8650a8a600 | ||
|
fb3b1f115c | ||
|
ce66cf2295 | ||
|
80e579dd0b |
18
.github/workflows/build.yml
vendored
18
.github/workflows/build.yml
vendored
@@ -17,8 +17,8 @@ on:
|
|||||||
env:
|
env:
|
||||||
# Currently no way to detect automatically
|
# Currently no way to detect automatically
|
||||||
DEFAULT_BRANCH: main
|
DEFAULT_BRANCH: main
|
||||||
GO_VERSION: 1.20.6 # renovate: datasource=golang-version depName=golang
|
GO_VERSION: 1.21.6 # renovate: datasource=golang-version depName=golang
|
||||||
NODE_VERSION: 18
|
NODE_VERSION: 20
|
||||||
DRY_RUN: true
|
DRY_RUN: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
@@ -26,14 +26,14 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
go-version: [ '1.20.x', '1.21.x' ]
|
go-version: [ '1.20.x', '1.21.x', '1.22.x']
|
||||||
steps:
|
steps:
|
||||||
# full checkout for semantic-release
|
# full checkout for semantic-release
|
||||||
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Set up go ${{ matrix.go-version }}
|
- name: Set up go ${{ matrix.go-version }}
|
||||||
uses: actions/setup-go@v4
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ${{ matrix.go-version }}
|
go-version: ${{ matrix.go-version }}
|
||||||
-
|
-
|
||||||
@@ -55,17 +55,17 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
# full checkout for semantic-release
|
# full checkout for semantic-release
|
||||||
- name: Full checkout
|
- name: Full checkout
|
||||||
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Set up Node.js ${{ env.NODE_VERSION }}
|
- name: Set up Node.js ${{ env.NODE_VERSION }}
|
||||||
uses: actions/setup-node@e33196f7422957bea03ed53f6fbb155025ffc7b8 # v3.7.0
|
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
|
||||||
with:
|
with:
|
||||||
node-version: ${{ env.NODE_VERSION }}
|
node-version: ${{ env.NODE_VERSION }}
|
||||||
|
|
||||||
- name: Set up go ${{env.GO_VERSION}}
|
- name: Set up go ${{env.GO_VERSION}}
|
||||||
uses: actions/setup-go@v4
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ${{env.GO_VERSION}}
|
go-version: ${{env.GO_VERSION}}
|
||||||
|
|
||||||
@@ -82,7 +82,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Semantic Release
|
- name: Semantic Release
|
||||||
run: |
|
run: |
|
||||||
npx -p conventional-changelog-conventionalcommits -p semantic-release semantic-release --dry-run ${{env.DRY_RUN}}
|
npx -p "conventional-changelog-conventionalcommits@<8" -p semantic-release semantic-release --dry-run ${{env.DRY_RUN}}
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,3 +1,4 @@
|
|||||||
fp-go.exe
|
fp-go.exe
|
||||||
main.exe
|
main.exe
|
||||||
build/
|
build/
|
||||||
|
.idea
|
13
README.md
13
README.md
@@ -14,6 +14,8 @@ go get github.com/IBM/fp-go
|
|||||||
|
|
||||||
Refer to the [samples](./samples/).
|
Refer to the [samples](./samples/).
|
||||||
|
|
||||||
|
Find API documentation [here](https://pkg.go.dev/github.com/IBM/fp-go)
|
||||||
|
|
||||||
## Design Goal
|
## Design Goal
|
||||||
|
|
||||||
This library aims to provide a set of data types and functions that make it easy and fun to write maintainable and testable code in golang. It encourages the following patterns:
|
This library aims to provide a set of data types and functions that make it easy and fun to write maintainable and testable code in golang. It encourages the following patterns:
|
||||||
@@ -192,3 +194,14 @@ this would be the completely generic method signature for all possible monads. I
|
|||||||
This FP library addresses this by introducing the HKTs as individual types, e.g. `HKT[A]` would be represented as a new generic type `HKTA`. This loses the correlation to the type `A` but allows to implement generic algorithms, at the price of readability.
|
This FP library addresses this by introducing the HKTs as individual types, e.g. `HKT[A]` would be represented as a new generic type `HKTA`. This loses the correlation to the type `A` but allows to implement generic algorithms, at the price of readability.
|
||||||
|
|
||||||
For that reason these implementations are kept in the `internal` package. These are meant to be used by the library itself or by extensions, not by end users.
|
For that reason these implementations are kept in the `internal` package. These are meant to be used by the library itself or by extensions, not by end users.
|
||||||
|
|
||||||
|
## Map/Ap/Flap
|
||||||
|
|
||||||
|
The following table lists the relationship between some selected operators
|
||||||
|
|
||||||
|
| Operator | Parameter | Monad | Result |
|
||||||
|
| -------- | ---------------- | --------------- | -------- |
|
||||||
|
| Map | `func(A) B` | `HKT[A]` | `HKT[B]` |
|
||||||
|
| Chain | `func(A) HKT[B]` | `HKT[A]` | `HKT[B]` |
|
||||||
|
| Ap | `HKT[A]` | `HKT[func(A)B]` | `HKT[B]` |
|
||||||
|
| Flap | `A` | `HKT[func(A)B]` | `HKT[B]` |
|
||||||
|
30
array/any.go
Normal file
30
array/any.go
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package array
|
||||||
|
|
||||||
|
import (
|
||||||
|
G "github.com/IBM/fp-go/array/generic"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AnyWithIndex tests if any of the elements in the array matches the predicate
|
||||||
|
func AnyWithIndex[A any](pred func(int, A) bool) func([]A) bool {
|
||||||
|
return G.AnyWithIndex[[]A](pred)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Any tests if any of the elements in the array matches the predicate
|
||||||
|
func Any[A any](pred func(A) bool) func([]A) bool {
|
||||||
|
return G.Any[[]A](pred)
|
||||||
|
}
|
30
array/any_test.go
Normal file
30
array/any_test.go
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package array
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
F "github.com/IBM/fp-go/function"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAny(t *testing.T) {
|
||||||
|
anyBool := Any(F.Identity[bool])
|
||||||
|
|
||||||
|
assert.True(t, anyBool(From(false, true, false)))
|
||||||
|
assert.False(t, anyBool(From(false, false, false)))
|
||||||
|
}
|
@@ -17,6 +17,7 @@ package array
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
G "github.com/IBM/fp-go/array/generic"
|
G "github.com/IBM/fp-go/array/generic"
|
||||||
|
EM "github.com/IBM/fp-go/endomorphism"
|
||||||
F "github.com/IBM/fp-go/function"
|
F "github.com/IBM/fp-go/function"
|
||||||
"github.com/IBM/fp-go/internal/array"
|
"github.com/IBM/fp-go/internal/array"
|
||||||
M "github.com/IBM/fp-go/monoid"
|
M "github.com/IBM/fp-go/monoid"
|
||||||
@@ -52,26 +53,18 @@ func MonadMapRef[A, B any](as []A, f func(a *A) B) []B {
|
|||||||
return bs
|
return bs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func MapWithIndex[A, B any](f func(int, A) B) func([]A) []B {
|
||||||
|
return G.MapWithIndex[[]A, []B](f)
|
||||||
|
}
|
||||||
|
|
||||||
func Map[A, B any](f func(a A) B) func([]A) []B {
|
func Map[A, B any](f func(a A) B) func([]A) []B {
|
||||||
return F.Bind2nd(MonadMap[A, B], f)
|
return G.Map[[]A, []B, A, B](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
func MapRef[A, B any](f func(a *A) B) func([]A) []B {
|
func MapRef[A, B any](f func(a *A) B) func([]A) []B {
|
||||||
return F.Bind2nd(MonadMapRef[A, B], f)
|
return F.Bind2nd(MonadMapRef[A, B], f)
|
||||||
}
|
}
|
||||||
|
|
||||||
func filter[A any](fa []A, pred func(A) bool) []A {
|
|
||||||
var result []A
|
|
||||||
count := len(fa)
|
|
||||||
for i := 0; i < count; i++ {
|
|
||||||
a := fa[i]
|
|
||||||
if pred(a) {
|
|
||||||
result = append(result, a)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func filterRef[A any](fa []A, pred func(a *A) bool) []A {
|
func filterRef[A any](fa []A, pred func(a *A) bool) []A {
|
||||||
var result []A
|
var result []A
|
||||||
count := len(fa)
|
count := len(fa)
|
||||||
@@ -96,23 +89,38 @@ func filterMapRef[A, B any](fa []A, pred func(a *A) bool, f func(a *A) B) []B {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func Filter[A any](pred func(A) bool) func([]A) []A {
|
// Filter returns a new array with all elements from the original array that match a predicate
|
||||||
return F.Bind2nd(filter[A], pred)
|
func Filter[A any](pred func(A) bool) EM.Endomorphism[[]A] {
|
||||||
|
return G.Filter[[]A](pred)
|
||||||
}
|
}
|
||||||
|
|
||||||
func FilterRef[A any](pred func(*A) bool) func([]A) []A {
|
// FilterWithIndex returns a new array with all elements from the original array that match a predicate
|
||||||
|
func FilterWithIndex[A any](pred func(int, A) bool) EM.Endomorphism[[]A] {
|
||||||
|
return G.FilterWithIndex[[]A](pred)
|
||||||
|
}
|
||||||
|
|
||||||
|
func FilterRef[A any](pred func(*A) bool) EM.Endomorphism[[]A] {
|
||||||
return F.Bind2nd(filterRef[A], pred)
|
return F.Bind2nd(filterRef[A], pred)
|
||||||
}
|
}
|
||||||
|
|
||||||
func MonadFilterMap[A, B any](fa []A, f func(a A) O.Option[B]) []B {
|
func MonadFilterMap[A, B any](fa []A, f func(A) O.Option[B]) []B {
|
||||||
return G.MonadFilterMap[[]A, []B](fa, f)
|
return G.MonadFilterMap[[]A, []B](fa, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FilterChain maps an array with an iterating function that returns an [O.Option] and it keeps only the Some values discarding the Nones.
|
func MonadFilterMapWithIndex[A, B any](fa []A, f func(int, A) O.Option[B]) []B {
|
||||||
func FilterMap[A, B any](f func(a A) O.Option[B]) func([]A) []B {
|
return G.MonadFilterMapWithIndex[[]A, []B](fa, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FilterMap maps an array with an iterating function that returns an [O.Option] and it keeps only the Some values discarding the Nones.
|
||||||
|
func FilterMap[A, B any](f func(A) O.Option[B]) func([]A) []B {
|
||||||
return G.FilterMap[[]A, []B](f)
|
return G.FilterMap[[]A, []B](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FilterMapWithIndex maps an array with an iterating function that returns an [O.Option] and it keeps only the Some values discarding the Nones.
|
||||||
|
func FilterMapWithIndex[A, B any](f func(int, A) O.Option[B]) func([]A) []B {
|
||||||
|
return G.FilterMapWithIndex[[]A, []B](f)
|
||||||
|
}
|
||||||
|
|
||||||
// FilterChain maps an array with an iterating function that returns an [O.Option] of an array. It keeps only the Some values discarding the Nones and then flattens the result.
|
// FilterChain maps an array with an iterating function that returns an [O.Option] of an array. It keeps only the Some values discarding the Nones and then flattens the result.
|
||||||
func FilterChain[A, B any](f func(A) O.Option[[]B]) func([]A) []B {
|
func FilterChain[A, B any](f func(A) O.Option[[]B]) func([]A) []B {
|
||||||
return G.FilterChain[[]A](f)
|
return G.FilterChain[[]A](f)
|
||||||
@@ -134,9 +142,19 @@ func reduceRef[A, B any](fa []A, f func(B, *A) B, initial B) B {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Reduce[A, B any](f func(B, A) B, initial B) func([]A) B {
|
func Reduce[A, B any](f func(B, A) B, initial B) func([]A) B {
|
||||||
return func(as []A) B {
|
return G.Reduce[[]A](f, initial)
|
||||||
return array.Reduce(as, f, initial)
|
}
|
||||||
}
|
|
||||||
|
func ReduceWithIndex[A, B any](f func(int, B, A) B, initial B) func([]A) B {
|
||||||
|
return G.ReduceWithIndex[[]A](f, initial)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReduceRight[A, B any](f func(A, B) B, initial B) func([]A) B {
|
||||||
|
return G.ReduceRight[[]A](f, initial)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReduceRightWithIndex[A, B any](f func(int, A, B) B, initial B) func([]A) B {
|
||||||
|
return G.ReduceRightWithIndex[[]A](f, initial)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ReduceRef[A, B any](f func(B, *A) B, initial B) func([]A) B {
|
func ReduceRef[A, B any](f func(B, *A) B, initial B) func([]A) B {
|
||||||
@@ -210,7 +228,7 @@ func Last[A any](as []A) O.Option[A] {
|
|||||||
return G.Last(as)
|
return G.Last(as)
|
||||||
}
|
}
|
||||||
|
|
||||||
func PrependAll[A any](middle A) func([]A) []A {
|
func PrependAll[A any](middle A) EM.Endomorphism[[]A] {
|
||||||
return func(as []A) []A {
|
return func(as []A) []A {
|
||||||
count := len(as)
|
count := len(as)
|
||||||
dst := count * 2
|
dst := count * 2
|
||||||
@@ -225,7 +243,7 @@ func PrependAll[A any](middle A) func([]A) []A {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Intersperse[A any](middle A) func([]A) []A {
|
func Intersperse[A any](middle A) EM.Endomorphism[[]A] {
|
||||||
prepend := PrependAll(middle)
|
prepend := PrependAll(middle)
|
||||||
return func(as []A) []A {
|
return func(as []A) []A {
|
||||||
if IsEmpty(as) {
|
if IsEmpty(as) {
|
||||||
@@ -254,7 +272,7 @@ func Lookup[A any](idx int) func([]A) O.Option[A] {
|
|||||||
return G.Lookup[[]A](idx)
|
return G.Lookup[[]A](idx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func UpsertAt[A any](a A) func([]A) []A {
|
func UpsertAt[A any](a A) EM.Endomorphism[[]A] {
|
||||||
return G.UpsertAt[[]A](a)
|
return G.UpsertAt[[]A](a)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -287,24 +305,47 @@ func ConstNil[A any]() []A {
|
|||||||
return array.ConstNil[[]A]()
|
return array.ConstNil[[]A]()
|
||||||
}
|
}
|
||||||
|
|
||||||
func SliceRight[A any](start int) func([]A) []A {
|
func SliceRight[A any](start int) EM.Endomorphism[[]A] {
|
||||||
return G.SliceRight[[]A](start)
|
return G.SliceRight[[]A](start)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Copy creates a shallow copy of the array
|
||||||
func Copy[A any](b []A) []A {
|
func Copy[A any](b []A) []A {
|
||||||
return G.Copy(b)
|
return G.Copy(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clone creates a deep copy of the array using the provided endomorphism to clone the values
|
||||||
|
func Clone[A any](f func(A) A) func(as []A) []A {
|
||||||
|
return G.Clone[[]A](f)
|
||||||
|
}
|
||||||
|
|
||||||
// FoldMap maps and folds an array. Map the Array passing each value to the iterating function. Then fold the results using the provided Monoid.
|
// FoldMap maps and folds an array. Map the Array passing each value to the iterating function. Then fold the results using the provided Monoid.
|
||||||
func FoldMap[A, B any](m M.Monoid[B]) func(func(A) B) func([]A) B {
|
func FoldMap[A, B any](m M.Monoid[B]) func(func(A) B) func([]A) B {
|
||||||
return G.FoldMap[[]A](m)
|
return G.FoldMap[[]A](m)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FoldMapWithIndex maps and folds an array. Map the Array passing each value to the iterating function. Then fold the results using the provided Monoid.
|
||||||
|
func FoldMapWithIndex[A, B any](m M.Monoid[B]) func(func(int, A) B) func([]A) B {
|
||||||
|
return G.FoldMapWithIndex[[]A](m)
|
||||||
|
}
|
||||||
|
|
||||||
// Fold folds the array using the provided Monoid.
|
// Fold folds the array using the provided Monoid.
|
||||||
func Fold[A any](m M.Monoid[A]) func([]A) A {
|
func Fold[A any](m M.Monoid[A]) func([]A) A {
|
||||||
return G.Fold[[]A](m)
|
return G.Fold[[]A](m)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Push[A any](a A) func([]A) []A {
|
func Push[A any](a A) EM.Endomorphism[[]A] {
|
||||||
return G.Push[[]A](a)
|
return G.Push[EM.Endomorphism[[]A]](a)
|
||||||
|
}
|
||||||
|
|
||||||
|
func MonadFlap[B, A any](fab []func(A) B, a A) []B {
|
||||||
|
return G.MonadFlap[func(A) B, []func(A) B, []B, A, B](fab, a)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Flap[B, A any](a A) func([]func(A) B) []B {
|
||||||
|
return G.Flap[func(A) B, []func(A) B, []B, A, B](a)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Prepend[A any](head A) EM.Endomorphism[[]A] {
|
||||||
|
return G.Prepend[EM.Endomorphism[[]A]](head)
|
||||||
}
|
}
|
||||||
|
@@ -59,6 +59,17 @@ func TestMap(t *testing.T) {
|
|||||||
assert.Equal(t, dst, []string{"A", "B", "C"})
|
assert.Equal(t, dst, []string{"A", "B", "C"})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestReduceRight(t *testing.T) {
|
||||||
|
values := From("a", "b", "c")
|
||||||
|
f := func(a, acc string) string {
|
||||||
|
return fmt.Sprintf("%s%s", acc, a)
|
||||||
|
}
|
||||||
|
b := ""
|
||||||
|
|
||||||
|
assert.Equal(t, "cba", ReduceRight(f, b)(values))
|
||||||
|
assert.Equal(t, "", ReduceRight(f, b)(Empty[string]()))
|
||||||
|
}
|
||||||
|
|
||||||
func TestReduce(t *testing.T) {
|
func TestReduce(t *testing.T) {
|
||||||
|
|
||||||
values := MakeBy(101, F.Identity[int])
|
values := MakeBy(101, F.Identity[int])
|
||||||
|
66
array/bind.go
Normal file
66
array/bind.go
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package array
|
||||||
|
|
||||||
|
import (
|
||||||
|
G "github.com/IBM/fp-go/array/generic"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Bind creates an empty context of type [S] to be used with the [Bind] operation
|
||||||
|
func Do[S any](
|
||||||
|
empty S,
|
||||||
|
) []S {
|
||||||
|
return G.Do[[]S, S](empty)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bind attaches the result of a computation to a context [S1] to produce a context [S2]
|
||||||
|
func Bind[S1, S2, T any](
|
||||||
|
setter func(T) func(S1) S2,
|
||||||
|
f func(S1) []T,
|
||||||
|
) func([]S1) []S2 {
|
||||||
|
return G.Bind[[]S1, []S2, []T, S1, S2, T](setter, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Let attaches the result of a computation to a context [S1] to produce a context [S2]
|
||||||
|
func Let[S1, S2, T any](
|
||||||
|
setter func(T) func(S1) S2,
|
||||||
|
f func(S1) T,
|
||||||
|
) func([]S1) []S2 {
|
||||||
|
return G.Let[[]S1, []S2, S1, S2, T](setter, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LetTo attaches the a value to a context [S1] to produce a context [S2]
|
||||||
|
func LetTo[S1, S2, T any](
|
||||||
|
setter func(T) func(S1) S2,
|
||||||
|
b T,
|
||||||
|
) func([]S1) []S2 {
|
||||||
|
return G.LetTo[[]S1, []S2, S1, S2, T](setter, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BindTo initializes a new state [S1] from a value [T]
|
||||||
|
func BindTo[S1, T any](
|
||||||
|
setter func(T) S1,
|
||||||
|
) func([]T) []S1 {
|
||||||
|
return G.BindTo[[]S1, []T, S1, T](setter)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApS attaches a value to a context [S1] to produce a context [S2] by considering the context and the value concurrently
|
||||||
|
func ApS[S1, S2, T any](
|
||||||
|
setter func(T) func(S1) S2,
|
||||||
|
fa []T,
|
||||||
|
) func([]S1) []S2 {
|
||||||
|
return G.ApS[[]S1, []S2, []T, S1, S2, T](setter, fa)
|
||||||
|
}
|
56
array/bind_test.go
Normal file
56
array/bind_test.go
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package array
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
F "github.com/IBM/fp-go/function"
|
||||||
|
"github.com/IBM/fp-go/internal/utils"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getLastName(s utils.Initial) []string {
|
||||||
|
return Of("Doe")
|
||||||
|
}
|
||||||
|
|
||||||
|
func getGivenName(s utils.WithLastName) []string {
|
||||||
|
return Of("John")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBind(t *testing.T) {
|
||||||
|
|
||||||
|
res := F.Pipe3(
|
||||||
|
Do(utils.Empty),
|
||||||
|
Bind(utils.SetLastName, getLastName),
|
||||||
|
Bind(utils.SetGivenName, getGivenName),
|
||||||
|
Map(utils.GetFullName),
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.Equal(t, res, Of("John Doe"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestApS(t *testing.T) {
|
||||||
|
|
||||||
|
res := F.Pipe3(
|
||||||
|
Do(utils.Empty),
|
||||||
|
ApS(utils.SetLastName, Of("Doe")),
|
||||||
|
ApS(utils.SetGivenName, Of("John")),
|
||||||
|
Map(utils.GetFullName),
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.Equal(t, res, Of("John Doe"))
|
||||||
|
}
|
77
array/example_any_test.go
Normal file
77
array/example_any_test.go
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package array
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
F "github.com/IBM/fp-go/function"
|
||||||
|
O "github.com/IBM/fp-go/option"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Example_any() {
|
||||||
|
|
||||||
|
pred := func(val int) bool {
|
||||||
|
return val&2 == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
data1 := From(1, 2, 3)
|
||||||
|
|
||||||
|
fmt.Println(Any(pred)(data1))
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// true
|
||||||
|
}
|
||||||
|
|
||||||
|
func Example_any_filter() {
|
||||||
|
|
||||||
|
pred := func(val int) bool {
|
||||||
|
return val&2 == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
data1 := From(1, 2, 3)
|
||||||
|
|
||||||
|
// Any tests if any of the entries in the array matches the condition
|
||||||
|
Any := F.Flow2(
|
||||||
|
Filter(pred),
|
||||||
|
IsNonEmpty[int],
|
||||||
|
)
|
||||||
|
|
||||||
|
fmt.Println(Any(data1))
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// true
|
||||||
|
}
|
||||||
|
|
||||||
|
func Example_any_find() {
|
||||||
|
|
||||||
|
pred := func(val int) bool {
|
||||||
|
return val&2 == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
data1 := From(1, 2, 3)
|
||||||
|
|
||||||
|
// Any tests if any of the entries in the array matches the condition
|
||||||
|
Any := F.Flow2(
|
||||||
|
FindFirst(pred),
|
||||||
|
O.IsSome[int],
|
||||||
|
)
|
||||||
|
|
||||||
|
fmt.Println(Any(data1))
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// true
|
||||||
|
}
|
55
array/example_find_test.go
Normal file
55
array/example_find_test.go
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package array
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
F "github.com/IBM/fp-go/function"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Example_find() {
|
||||||
|
|
||||||
|
pred := func(val int) bool {
|
||||||
|
return val&2 == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
data1 := From(1, 2, 3)
|
||||||
|
|
||||||
|
fmt.Println(FindFirst(pred)(data1))
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// Some[int](1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Example_find_filter() {
|
||||||
|
|
||||||
|
pred := func(val int) bool {
|
||||||
|
return val&2 == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
data1 := From(1, 2, 3)
|
||||||
|
|
||||||
|
Find := F.Flow2(
|
||||||
|
Filter(pred),
|
||||||
|
Head[int],
|
||||||
|
)
|
||||||
|
|
||||||
|
fmt.Println(Find(data1))
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// Some[int](1)
|
||||||
|
}
|
61
array/find.go
Normal file
61
array/find.go
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package array
|
||||||
|
|
||||||
|
import (
|
||||||
|
G "github.com/IBM/fp-go/array/generic"
|
||||||
|
O "github.com/IBM/fp-go/option"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FindFirst finds the first element which satisfies a predicate (or a refinement) function
|
||||||
|
func FindFirst[A any](pred func(A) bool) func([]A) O.Option[A] {
|
||||||
|
return G.FindFirst[[]A](pred)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindFirstWithIndex finds the first element which satisfies a predicate (or a refinement) function
|
||||||
|
func FindFirstWithIndex[A any](pred func(int, A) bool) func([]A) O.Option[A] {
|
||||||
|
return G.FindFirstWithIndex[[]A](pred)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindFirstMap finds the first element returned by an [O.Option] based selector function
|
||||||
|
func FindFirstMap[A, B any](sel func(A) O.Option[B]) func([]A) O.Option[B] {
|
||||||
|
return G.FindFirstMap[[]A](sel)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindFirstMapWithIndex finds the first element returned by an [O.Option] based selector function
|
||||||
|
func FindFirstMapWithIndex[A, B any](sel func(int, A) O.Option[B]) func([]A) O.Option[B] {
|
||||||
|
return G.FindFirstMapWithIndex[[]A](sel)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindLast finds the Last element which satisfies a predicate (or a refinement) function
|
||||||
|
func FindLast[A any](pred func(A) bool) func([]A) O.Option[A] {
|
||||||
|
return G.FindLast[[]A](pred)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindLastWithIndex finds the Last element which satisfies a predicate (or a refinement) function
|
||||||
|
func FindLastWithIndex[A any](pred func(int, A) bool) func([]A) O.Option[A] {
|
||||||
|
return G.FindLastWithIndex[[]A](pred)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindLastMap finds the Last element returned by an [O.Option] based selector function
|
||||||
|
func FindLastMap[A, B any](sel func(A) O.Option[B]) func([]A) O.Option[B] {
|
||||||
|
return G.FindLastMap[[]A](sel)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindLastMapWithIndex finds the Last element returned by an [O.Option] based selector function
|
||||||
|
func FindLastMapWithIndex[A, B any](sel func(int, A) O.Option[B]) func([]A) O.Option[B] {
|
||||||
|
return G.FindLastMapWithIndex[[]A](sel)
|
||||||
|
}
|
34
array/generic/any.go
Normal file
34
array/generic/any.go
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package generic
|
||||||
|
|
||||||
|
import (
|
||||||
|
F "github.com/IBM/fp-go/function"
|
||||||
|
O "github.com/IBM/fp-go/option"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AnyWithIndex tests if any of the elements in the array matches the predicate
|
||||||
|
func AnyWithIndex[AS ~[]A, PRED ~func(int, A) bool, A any](pred PRED) func(AS) bool {
|
||||||
|
return F.Flow2(
|
||||||
|
FindFirstWithIndex[AS](pred),
|
||||||
|
O.IsSome[A],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Any tests if any of the elements in the array matches the predicate
|
||||||
|
func Any[AS ~[]A, PRED ~func(A) bool, A any](pred PRED) func(AS) bool {
|
||||||
|
return AnyWithIndex[AS](F.Ignore1of2[int](pred))
|
||||||
|
}
|
@@ -18,6 +18,7 @@ package generic
|
|||||||
import (
|
import (
|
||||||
F "github.com/IBM/fp-go/function"
|
F "github.com/IBM/fp-go/function"
|
||||||
"github.com/IBM/fp-go/internal/array"
|
"github.com/IBM/fp-go/internal/array"
|
||||||
|
FC "github.com/IBM/fp-go/internal/functor"
|
||||||
M "github.com/IBM/fp-go/monoid"
|
M "github.com/IBM/fp-go/monoid"
|
||||||
O "github.com/IBM/fp-go/option"
|
O "github.com/IBM/fp-go/option"
|
||||||
"github.com/IBM/fp-go/tuple"
|
"github.com/IBM/fp-go/tuple"
|
||||||
@@ -28,14 +29,46 @@ func Of[GA ~[]A, A any](value A) GA {
|
|||||||
return GA{value}
|
return GA{value}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Reduce[GA ~[]A, A, B any](fa GA, f func(B, A) B, initial B) B {
|
func Reduce[GA ~[]A, A, B any](f func(B, A) B, initial B) func(GA) B {
|
||||||
|
return func(as GA) B {
|
||||||
|
return MonadReduce[GA](as, f, initial)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReduceWithIndex[GA ~[]A, A, B any](f func(int, B, A) B, initial B) func(GA) B {
|
||||||
|
return func(as GA) B {
|
||||||
|
return MonadReduceWithIndex[GA](as, f, initial)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReduceRight[GA ~[]A, A, B any](f func(A, B) B, initial B) func(GA) B {
|
||||||
|
return func(as GA) B {
|
||||||
|
return MonadReduceRight[GA](as, f, initial)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReduceRightWithIndex[GA ~[]A, A, B any](f func(int, A, B) B, initial B) func(GA) B {
|
||||||
|
return func(as GA) B {
|
||||||
|
return MonadReduceRightWithIndex[GA](as, f, initial)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func MonadReduce[GA ~[]A, A, B any](fa GA, f func(B, A) B, initial B) B {
|
||||||
return array.Reduce(fa, f, initial)
|
return array.Reduce(fa, f, initial)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ReduceWithIndex[GA ~[]A, A, B any](fa GA, f func(int, B, A) B, initial B) B {
|
func MonadReduceWithIndex[GA ~[]A, A, B any](fa GA, f func(int, B, A) B, initial B) B {
|
||||||
return array.ReduceWithIndex(fa, f, initial)
|
return array.ReduceWithIndex(fa, f, initial)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func MonadReduceRight[GA ~[]A, A, B any](fa GA, f func(A, B) B, initial B) B {
|
||||||
|
return array.ReduceRight(fa, f, initial)
|
||||||
|
}
|
||||||
|
|
||||||
|
func MonadReduceRightWithIndex[GA ~[]A, A, B any](fa GA, f func(int, A, B) B, initial B) B {
|
||||||
|
return array.ReduceRightWithIndex(fa, f, initial)
|
||||||
|
}
|
||||||
|
|
||||||
// From constructs an array from a set of variadic arguments
|
// From constructs an array from a set of variadic arguments
|
||||||
func From[GA ~[]A, A any](data ...A) GA {
|
func From[GA ~[]A, A any](data ...A) GA {
|
||||||
return data
|
return data
|
||||||
@@ -114,23 +147,59 @@ func MonadMap[GA ~[]A, GB ~[]B, A, B any](as GA, f func(a A) B) GB {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Map[GA ~[]A, GB ~[]B, A, B any](f func(a A) B) func(GA) GB {
|
func Map[GA ~[]A, GB ~[]B, A, B any](f func(a A) B) func(GA) GB {
|
||||||
return F.Bind2nd(MonadMap[GA, GB, A, B], f)
|
return array.Map[GA, GB](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func MonadMapWithIndex[GA ~[]A, GB ~[]B, A, B any](as GA, f func(int, A) B) GB {
|
||||||
|
return array.MonadMapWithIndex[GA, GB](as, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func MapWithIndex[GA ~[]A, GB ~[]B, A, B any](f func(int, A) B) func(GA) GB {
|
||||||
|
return F.Bind2nd(MonadMapWithIndex[GA, GB, A, B], f)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Size[GA ~[]A, A any](as GA) int {
|
func Size[GA ~[]A, A any](as GA) int {
|
||||||
return len(as)
|
return len(as)
|
||||||
}
|
}
|
||||||
|
|
||||||
func filterMap[GA ~[]A, GB ~[]B, A, B any](fa GA, f func(a A) O.Option[B]) GB {
|
func filterMap[GA ~[]A, GB ~[]B, A, B any](fa GA, f func(A) O.Option[B]) GB {
|
||||||
return array.Reduce(fa, func(bs GB, a A) GB {
|
return array.Reduce(fa, func(bs GB, a A) GB {
|
||||||
return O.MonadFold(f(a), F.Constant(bs), F.Bind1st(Append[GB, B], bs))
|
return O.MonadFold(f(a), F.Constant(bs), F.Bind1st(Append[GB, B], bs))
|
||||||
}, Empty[GB]())
|
}, Empty[GB]())
|
||||||
}
|
}
|
||||||
|
|
||||||
func MonadFilterMap[GA ~[]A, GB ~[]B, A, B any](fa GA, f func(a A) O.Option[B]) GB {
|
func filterMapWithIndex[GA ~[]A, GB ~[]B, A, B any](fa GA, f func(int, A) O.Option[B]) GB {
|
||||||
|
return array.ReduceWithIndex(fa, func(idx int, bs GB, a A) GB {
|
||||||
|
return O.MonadFold(f(idx, a), F.Constant(bs), F.Bind1st(Append[GB, B], bs))
|
||||||
|
}, Empty[GB]())
|
||||||
|
}
|
||||||
|
|
||||||
|
func MonadFilterMap[GA ~[]A, GB ~[]B, A, B any](fa GA, f func(A) O.Option[B]) GB {
|
||||||
return filterMap[GA, GB](fa, f)
|
return filterMap[GA, GB](fa, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func MonadFilterMapWithIndex[GA ~[]A, GB ~[]B, A, B any](fa GA, f func(int, A) O.Option[B]) GB {
|
||||||
|
return filterMapWithIndex[GA, GB](fa, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func filterWithIndex[AS ~[]A, PRED ~func(int, A) bool, A any](fa AS, pred PRED) AS {
|
||||||
|
result := make(AS, 0, len(fa))
|
||||||
|
for i, a := range fa {
|
||||||
|
if pred(i, a) {
|
||||||
|
result = append(result, a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func FilterWithIndex[AS ~[]A, PRED ~func(int, A) bool, A any](pred PRED) func(AS) AS {
|
||||||
|
return F.Bind2nd(filterWithIndex[AS, PRED, A], pred)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Filter[AS ~[]A, PRED ~func(A) bool, A any](pred PRED) func(AS) AS {
|
||||||
|
return FilterWithIndex[AS](F.Ignore1of2[int](pred))
|
||||||
|
}
|
||||||
|
|
||||||
func FilterChain[GA ~[]A, GB ~[]B, A, B any](f func(a A) O.Option[GB]) func(GA) GB {
|
func FilterChain[GA ~[]A, GB ~[]B, A, B any](f func(a A) O.Option[GB]) func(GA) GB {
|
||||||
return F.Flow2(
|
return F.Flow2(
|
||||||
FilterMap[GA, []GB](f),
|
FilterMap[GA, []GB](f),
|
||||||
@@ -142,10 +211,14 @@ func Flatten[GAA ~[]GA, GA ~[]A, A any](mma GAA) GA {
|
|||||||
return MonadChain(mma, F.Identity[GA])
|
return MonadChain(mma, F.Identity[GA])
|
||||||
}
|
}
|
||||||
|
|
||||||
func FilterMap[GA ~[]A, GB ~[]B, A, B any](f func(a A) O.Option[B]) func(GA) GB {
|
func FilterMap[GA ~[]A, GB ~[]B, A, B any](f func(A) O.Option[B]) func(GA) GB {
|
||||||
return F.Bind2nd(MonadFilterMap[GA, GB, A, B], f)
|
return F.Bind2nd(MonadFilterMap[GA, GB, A, B], f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func FilterMapWithIndex[GA ~[]A, GB ~[]B, A, B any](f func(int, A) O.Option[B]) func(GA) GB {
|
||||||
|
return F.Bind2nd(MonadFilterMapWithIndex[GA, GB, A, B], f)
|
||||||
|
}
|
||||||
|
|
||||||
func MonadPartition[GA ~[]A, A any](as GA, pred func(A) bool) tuple.Tuple2[GA, GA] {
|
func MonadPartition[GA ~[]A, A any](as GA, pred func(A) bool) tuple.Tuple2[GA, GA] {
|
||||||
left := Empty[GA]()
|
left := Empty[GA]()
|
||||||
right := Empty[GA]()
|
right := Empty[GA]()
|
||||||
@@ -187,6 +260,14 @@ func IsEmpty[AS ~[]A, A any](as AS) bool {
|
|||||||
return array.IsEmpty(as)
|
return array.IsEmpty(as)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func IsNil[GA ~[]A, A any](as GA) bool {
|
||||||
|
return array.IsNil(as)
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsNonNil[GA ~[]A, A any](as GA) bool {
|
||||||
|
return array.IsNonNil(as)
|
||||||
|
}
|
||||||
|
|
||||||
func Match[AS ~[]A, A, B any](onEmpty func() B, onNonEmpty func(AS) B) func(AS) B {
|
func Match[AS ~[]A, A, B any](onEmpty func() B, onNonEmpty func(AS) B) func(AS) B {
|
||||||
return func(as AS) B {
|
return func(as AS) B {
|
||||||
if IsEmpty(as) {
|
if IsEmpty(as) {
|
||||||
@@ -223,6 +304,11 @@ func Copy[AS ~[]A, A any](b AS) AS {
|
|||||||
return buf
|
return buf
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Clone[AS ~[]A, A any](f func(A) A) func(as AS) AS {
|
||||||
|
// implementation assumes that map does not optimize for the empty array
|
||||||
|
return Map[AS, AS](f)
|
||||||
|
}
|
||||||
|
|
||||||
func FoldMap[AS ~[]A, A, B any](m M.Monoid[B]) func(func(A) B) func(AS) B {
|
func FoldMap[AS ~[]A, A, B any](m M.Monoid[B]) func(func(A) B) func(AS) B {
|
||||||
return func(f func(A) B) func(AS) B {
|
return func(f func(A) B) func(AS) B {
|
||||||
return func(as AS) B {
|
return func(as AS) B {
|
||||||
@@ -233,12 +319,34 @@ func FoldMap[AS ~[]A, A, B any](m M.Monoid[B]) func(func(A) B) func(AS) B {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func FoldMapWithIndex[AS ~[]A, A, B any](m M.Monoid[B]) func(func(int, A) B) func(AS) B {
|
||||||
|
return func(f func(int, A) B) func(AS) B {
|
||||||
|
return func(as AS) B {
|
||||||
|
return array.ReduceWithIndex(as, func(idx int, cur B, a A) B {
|
||||||
|
return m.Concat(cur, f(idx, a))
|
||||||
|
}, m.Empty())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func Fold[AS ~[]A, A any](m M.Monoid[A]) func(AS) A {
|
func Fold[AS ~[]A, A any](m M.Monoid[A]) func(AS) A {
|
||||||
return func(as AS) A {
|
return func(as AS) A {
|
||||||
return array.Reduce(as, m.Concat, m.Empty())
|
return array.Reduce(as, m.Concat, m.Empty())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Push[GA ~[]A, A any](a A) func(GA) GA {
|
func Push[ENDO ~func(GA) GA, GA ~[]A, A any](a A) ENDO {
|
||||||
return F.Bind2nd(array.Push[GA, A], a)
|
return F.Bind2nd(array.Push[GA, A], a)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func MonadFlap[FAB ~func(A) B, GFAB ~[]FAB, GB ~[]B, A, B any](fab GFAB, a A) GB {
|
||||||
|
return FC.MonadFlap(MonadMap[GFAB, GB], fab, a)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Flap[FAB ~func(A) B, GFAB ~[]FAB, GB ~[]B, A, B any](a A) func(GFAB) GB {
|
||||||
|
return FC.Flap(Map[GFAB, GB], a)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Prepend[ENDO ~func(AS) AS, AS []A, A any](head A) ENDO {
|
||||||
|
return array.Prepend[ENDO](head)
|
||||||
|
}
|
||||||
|
89
array/generic/bind.go
Normal file
89
array/generic/bind.go
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package generic
|
||||||
|
|
||||||
|
import (
|
||||||
|
A "github.com/IBM/fp-go/internal/apply"
|
||||||
|
C "github.com/IBM/fp-go/internal/chain"
|
||||||
|
F "github.com/IBM/fp-go/internal/functor"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Bind creates an empty context of type [S] to be used with the [Bind] operation
|
||||||
|
func Do[GS ~[]S, S any](
|
||||||
|
empty S,
|
||||||
|
) GS {
|
||||||
|
return Of[GS](empty)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bind attaches the result of a computation to a context [S1] to produce a context [S2]
|
||||||
|
func Bind[GS1 ~[]S1, GS2 ~[]S2, GT ~[]T, S1, S2, T any](
|
||||||
|
setter func(T) func(S1) S2,
|
||||||
|
f func(S1) GT,
|
||||||
|
) func(GS1) GS2 {
|
||||||
|
return C.Bind(
|
||||||
|
Chain[GS1, GS2, S1, S2],
|
||||||
|
Map[GT, GS2, T, S2],
|
||||||
|
setter,
|
||||||
|
f,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Let attaches the result of a computation to a context [S1] to produce a context [S2]
|
||||||
|
func Let[GS1 ~[]S1, GS2 ~[]S2, S1, S2, T any](
|
||||||
|
key func(T) func(S1) S2,
|
||||||
|
f func(S1) T,
|
||||||
|
) func(GS1) GS2 {
|
||||||
|
return F.Let(
|
||||||
|
Map[GS1, GS2, S1, S2],
|
||||||
|
key,
|
||||||
|
f,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LetTo attaches the a value to a context [S1] to produce a context [S2]
|
||||||
|
func LetTo[GS1 ~[]S1, GS2 ~[]S2, S1, S2, B any](
|
||||||
|
key func(B) func(S1) S2,
|
||||||
|
b B,
|
||||||
|
) func(GS1) GS2 {
|
||||||
|
return F.LetTo(
|
||||||
|
Map[GS1, GS2, S1, S2],
|
||||||
|
key,
|
||||||
|
b,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BindTo initializes a new state [S1] from a value [T]
|
||||||
|
func BindTo[GS1 ~[]S1, GT ~[]T, S1, T any](
|
||||||
|
setter func(T) S1,
|
||||||
|
) func(GT) GS1 {
|
||||||
|
return C.BindTo(
|
||||||
|
Map[GT, GS1, T, S1],
|
||||||
|
setter,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApS attaches a value to a context [S1] to produce a context [S2] by considering the context and the value concurrently
|
||||||
|
func ApS[GS1 ~[]S1, GS2 ~[]S2, GT ~[]T, S1, S2, T any](
|
||||||
|
setter func(T) func(S1) S2,
|
||||||
|
fa GT,
|
||||||
|
) func(GS1) GS2 {
|
||||||
|
return A.ApS(
|
||||||
|
Ap[GS2, []func(T) S2, GT, S2, T],
|
||||||
|
Map[GS1, []func(T) S2, S1, func(T) S2],
|
||||||
|
setter,
|
||||||
|
fa,
|
||||||
|
)
|
||||||
|
}
|
97
array/generic/find.go
Normal file
97
array/generic/find.go
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package generic
|
||||||
|
|
||||||
|
import (
|
||||||
|
F "github.com/IBM/fp-go/function"
|
||||||
|
O "github.com/IBM/fp-go/option"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FindFirstWithIndex finds the first element which satisfies a predicate (or a refinement) function
|
||||||
|
func FindFirstWithIndex[AS ~[]A, PRED ~func(int, A) bool, A any](pred PRED) func(AS) O.Option[A] {
|
||||||
|
none := O.None[A]()
|
||||||
|
return func(as AS) O.Option[A] {
|
||||||
|
for i, a := range as {
|
||||||
|
if pred(i, a) {
|
||||||
|
return O.Some(a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return none
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindFirst finds the first element which satisfies a predicate (or a refinement) function
|
||||||
|
func FindFirst[AS ~[]A, PRED ~func(A) bool, A any](pred PRED) func(AS) O.Option[A] {
|
||||||
|
return FindFirstWithIndex[AS](F.Ignore1of2[int](pred))
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindFirstMapWithIndex finds the first element returned by an [O.Option] based selector function
|
||||||
|
func FindFirstMapWithIndex[AS ~[]A, PRED ~func(int, A) O.Option[B], A, B any](pred PRED) func(AS) O.Option[B] {
|
||||||
|
none := O.None[B]()
|
||||||
|
return func(as AS) O.Option[B] {
|
||||||
|
count := len(as)
|
||||||
|
for i := 0; i < count; i++ {
|
||||||
|
out := pred(i, as[i])
|
||||||
|
if O.IsSome(out) {
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return none
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindFirstMap finds the first element returned by an [O.Option] based selector function
|
||||||
|
func FindFirstMap[AS ~[]A, PRED ~func(A) O.Option[B], A, B any](pred PRED) func(AS) O.Option[B] {
|
||||||
|
return FindFirstMapWithIndex[AS](F.Ignore1of2[int](pred))
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindLastWithIndex finds the first element which satisfies a predicate (or a refinement) function
|
||||||
|
func FindLastWithIndex[AS ~[]A, PRED ~func(int, A) bool, A any](pred PRED) func(AS) O.Option[A] {
|
||||||
|
none := O.None[A]()
|
||||||
|
return func(as AS) O.Option[A] {
|
||||||
|
for i := len(as) - 1; i >= 0; i-- {
|
||||||
|
a := as[i]
|
||||||
|
if pred(i, a) {
|
||||||
|
return O.Some(a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return none
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindLast finds the first element which satisfies a predicate (or a refinement) function
|
||||||
|
func FindLast[AS ~[]A, PRED ~func(A) bool, A any](pred PRED) func(AS) O.Option[A] {
|
||||||
|
return FindLastWithIndex[AS](F.Ignore1of2[int](pred))
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindLastMapWithIndex finds the first element returned by an [O.Option] based selector function
|
||||||
|
func FindLastMapWithIndex[AS ~[]A, PRED ~func(int, A) O.Option[B], A, B any](pred PRED) func(AS) O.Option[B] {
|
||||||
|
none := O.None[B]()
|
||||||
|
return func(as AS) O.Option[B] {
|
||||||
|
for i := len(as) - 1; i >= 0; i-- {
|
||||||
|
out := pred(i, as[i])
|
||||||
|
if O.IsSome(out) {
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return none
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindLastMap finds the first element returned by an [O.Option] based selector function
|
||||||
|
func FindLastMap[AS ~[]A, PRED ~func(A) O.Option[B], A, B any](pred PRED) func(AS) O.Option[B] {
|
||||||
|
return FindLastMapWithIndex[AS](F.Ignore1of2[int](pred))
|
||||||
|
}
|
43
array/generic/monad.go
Normal file
43
array/generic/monad.go
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
// Copyright (c) 2024 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package generic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/IBM/fp-go/internal/monad"
|
||||||
|
)
|
||||||
|
|
||||||
|
type arrayMonad[A, B any, GA ~[]A, GB ~[]B, GAB ~[]func(A) B] struct{}
|
||||||
|
|
||||||
|
func (o *arrayMonad[A, B, GA, GB, GAB]) Of(a A) GA {
|
||||||
|
return Of[GA, A](a)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *arrayMonad[A, B, GA, GB, GAB]) Map(f func(A) B) func(GA) GB {
|
||||||
|
return Map[GA, GB, A, B](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *arrayMonad[A, B, GA, GB, GAB]) Chain(f func(A) GB) func(GA) GB {
|
||||||
|
return Chain[GA, GB, A, B](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *arrayMonad[A, B, GA, GB, GAB]) Ap(fa GA) func(GAB) GB {
|
||||||
|
return Ap[GB, GAB, GA, B, A](fa)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Monad implements the monadic operations for an array
|
||||||
|
func Monad[A, B any, GA ~[]A, GB ~[]B, GAB ~[]func(A) B]() monad.Monad[A, B, GA, GB, GAB] {
|
||||||
|
return &arrayMonad[A, B, GA, GB, GAB]{}
|
||||||
|
}
|
32
array/generic/uniq.go
Normal file
32
array/generic/uniq.go
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
package generic
|
||||||
|
|
||||||
|
import F "github.com/IBM/fp-go/function"
|
||||||
|
|
||||||
|
// StrictUniq converts an array of arbitrary items into an array or unique items
|
||||||
|
// where uniqueness is determined by the built-in uniqueness constraint
|
||||||
|
func StrictUniq[AS ~[]A, A comparable](as AS) AS {
|
||||||
|
return Uniq[AS](F.Identity[A])(as)
|
||||||
|
}
|
||||||
|
|
||||||
|
// uniquePredUnsafe returns a predicate on a map for uniqueness
|
||||||
|
func uniquePredUnsafe[PRED ~func(A) K, A any, K comparable](f PRED) func(int, A) bool {
|
||||||
|
lookup := make(map[K]bool)
|
||||||
|
return func(_ int, a A) bool {
|
||||||
|
k := f(a)
|
||||||
|
_, has := lookup[k]
|
||||||
|
if has {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
lookup[k] = true
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uniq converts an array of arbitrary items into an array or unique items
|
||||||
|
// where uniqueness is determined based on a key extractor function
|
||||||
|
func Uniq[AS ~[]A, PRED ~func(A) K, A any, K comparable](f PRED) func(as AS) AS {
|
||||||
|
return func(as AS) AS {
|
||||||
|
// we need to create a new predicate for each iteration
|
||||||
|
return filterWithIndex(as, uniquePredUnsafe(f))
|
||||||
|
}
|
||||||
|
}
|
26
array/monad.go
Normal file
26
array/monad.go
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
// Copyright (c) 2024 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package array
|
||||||
|
|
||||||
|
import (
|
||||||
|
G "github.com/IBM/fp-go/array/generic"
|
||||||
|
"github.com/IBM/fp-go/internal/monad"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Monad returns the monadic operations for an array
|
||||||
|
func Monad[A, B any]() monad.Monad[A, B, []A, []B, []func(A) B] {
|
||||||
|
return G.Monad[A, B, []A, []B, []func(A) B]()
|
||||||
|
}
|
@@ -18,15 +18,16 @@ package array
|
|||||||
import (
|
import (
|
||||||
"github.com/IBM/fp-go/internal/array"
|
"github.com/IBM/fp-go/internal/array"
|
||||||
M "github.com/IBM/fp-go/monoid"
|
M "github.com/IBM/fp-go/monoid"
|
||||||
|
S "github.com/IBM/fp-go/semigroup"
|
||||||
)
|
)
|
||||||
|
|
||||||
func concat[T any](left, right []T) []T {
|
func concat[T any](left, right []T) []T {
|
||||||
// some performance checks
|
// some performance checks
|
||||||
ll := len(left)
|
ll := len(left)
|
||||||
lr := len(right)
|
|
||||||
if ll == 0 {
|
if ll == 0 {
|
||||||
return right
|
return right
|
||||||
}
|
}
|
||||||
|
lr := len(right)
|
||||||
if lr == 0 {
|
if lr == 0 {
|
||||||
return left
|
return left
|
||||||
}
|
}
|
||||||
@@ -40,6 +41,10 @@ func Monoid[T any]() M.Monoid[[]T] {
|
|||||||
return M.MakeMonoid(concat[T], Empty[T]())
|
return M.MakeMonoid(concat[T], Empty[T]())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Semigroup[T any]() S.Semigroup[[]T] {
|
||||||
|
return S.MakeSemigroup(concat[T])
|
||||||
|
}
|
||||||
|
|
||||||
func addLen[A any](count int, data []A) int {
|
func addLen[A any](count int, data []A) int {
|
||||||
return count + len(data)
|
return count + len(data)
|
||||||
}
|
}
|
||||||
|
136
array/nonempty/array.go
Normal file
136
array/nonempty/array.go
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package nonempty
|
||||||
|
|
||||||
|
import (
|
||||||
|
G "github.com/IBM/fp-go/array/generic"
|
||||||
|
EM "github.com/IBM/fp-go/endomorphism"
|
||||||
|
F "github.com/IBM/fp-go/function"
|
||||||
|
"github.com/IBM/fp-go/internal/array"
|
||||||
|
S "github.com/IBM/fp-go/semigroup"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NonEmptyArray represents an array with at least one element
|
||||||
|
type NonEmptyArray[A any] []A
|
||||||
|
|
||||||
|
// Of constructs a single element array
|
||||||
|
func Of[A any](first A) NonEmptyArray[A] {
|
||||||
|
return G.Of[NonEmptyArray[A]](first)
|
||||||
|
}
|
||||||
|
|
||||||
|
// From constructs a [NonEmptyArray] from a set of variadic arguments
|
||||||
|
func From[A any](first A, data ...A) NonEmptyArray[A] {
|
||||||
|
count := len(data)
|
||||||
|
if count == 0 {
|
||||||
|
return Of(first)
|
||||||
|
}
|
||||||
|
// allocate the requested buffer
|
||||||
|
buffer := make(NonEmptyArray[A], count+1)
|
||||||
|
buffer[0] = first
|
||||||
|
copy(buffer[1:], data)
|
||||||
|
return buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsEmpty[A any](_ NonEmptyArray[A]) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsNonEmpty[A any](_ NonEmptyArray[A]) bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func MonadMap[A, B any](as NonEmptyArray[A], f func(a A) B) NonEmptyArray[B] {
|
||||||
|
return G.MonadMap[NonEmptyArray[A], NonEmptyArray[B]](as, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Map[A, B any](f func(a A) B) func(NonEmptyArray[A]) NonEmptyArray[B] {
|
||||||
|
return F.Bind2nd(MonadMap[A, B], f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Reduce[A, B any](f func(B, A) B, initial B) func(NonEmptyArray[A]) B {
|
||||||
|
return func(as NonEmptyArray[A]) B {
|
||||||
|
return array.Reduce(as, f, initial)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReduceRight[A, B any](f func(A, B) B, initial B) func(NonEmptyArray[A]) B {
|
||||||
|
return func(as NonEmptyArray[A]) B {
|
||||||
|
return array.ReduceRight(as, f, initial)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Tail[A any](as NonEmptyArray[A]) []A {
|
||||||
|
return as[1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
func Head[A any](as NonEmptyArray[A]) A {
|
||||||
|
return as[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
func First[A any](as NonEmptyArray[A]) A {
|
||||||
|
return as[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
func Last[A any](as NonEmptyArray[A]) A {
|
||||||
|
return as[len(as)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
func Size[A any](as NonEmptyArray[A]) int {
|
||||||
|
return G.Size(as)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Flatten[A any](mma NonEmptyArray[NonEmptyArray[A]]) NonEmptyArray[A] {
|
||||||
|
return G.Flatten(mma)
|
||||||
|
}
|
||||||
|
|
||||||
|
func MonadChain[A, B any](fa NonEmptyArray[A], f func(a A) NonEmptyArray[B]) NonEmptyArray[B] {
|
||||||
|
return G.MonadChain[NonEmptyArray[A], NonEmptyArray[B]](fa, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Chain[A, B any](f func(A) NonEmptyArray[B]) func(NonEmptyArray[A]) NonEmptyArray[B] {
|
||||||
|
return G.Chain[NonEmptyArray[A], NonEmptyArray[B]](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func MonadAp[B, A any](fab NonEmptyArray[func(A) B], fa NonEmptyArray[A]) NonEmptyArray[B] {
|
||||||
|
return G.MonadAp[NonEmptyArray[B]](fab, fa)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Ap[B, A any](fa NonEmptyArray[A]) func(NonEmptyArray[func(A) B]) NonEmptyArray[B] {
|
||||||
|
return G.Ap[NonEmptyArray[B], NonEmptyArray[func(A) B]](fa)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FoldMap maps and folds a [NonEmptyArray]. Map the [NonEmptyArray] passing each value to the iterating function. Then fold the results using the provided [Semigroup].
|
||||||
|
func FoldMap[A, B any](s S.Semigroup[B]) func(func(A) B) func(NonEmptyArray[A]) B {
|
||||||
|
return func(f func(A) B) func(NonEmptyArray[A]) B {
|
||||||
|
return func(as NonEmptyArray[A]) B {
|
||||||
|
return array.Reduce(Tail(as), func(cur B, a A) B {
|
||||||
|
return s.Concat(cur, f(a))
|
||||||
|
}, f(Head(as)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fold folds the [NonEmptyArray] using the provided [Semigroup].
|
||||||
|
func Fold[A any](s S.Semigroup[A]) func(NonEmptyArray[A]) A {
|
||||||
|
return func(as NonEmptyArray[A]) A {
|
||||||
|
return array.Reduce(Tail(as), s.Concat, Head(as))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepend prepends a single value to an array
|
||||||
|
func Prepend[A any](head A) EM.Endomorphism[NonEmptyArray[A]] {
|
||||||
|
return array.Prepend[EM.Endomorphism[NonEmptyArray[A]]](head)
|
||||||
|
}
|
74
array/testing/laws.go
Normal file
74
array/testing/laws.go
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package testing
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
RA "github.com/IBM/fp-go/array"
|
||||||
|
EQ "github.com/IBM/fp-go/eq"
|
||||||
|
L "github.com/IBM/fp-go/internal/monad/testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AssertLaws asserts the apply monad laws for the array monad
|
||||||
|
func AssertLaws[A, B, C any](t *testing.T,
|
||||||
|
eqa EQ.Eq[A],
|
||||||
|
eqb EQ.Eq[B],
|
||||||
|
eqc EQ.Eq[C],
|
||||||
|
|
||||||
|
ab func(A) B,
|
||||||
|
bc func(B) C,
|
||||||
|
) func(a A) bool {
|
||||||
|
|
||||||
|
return L.AssertLaws(t,
|
||||||
|
RA.Eq(eqa),
|
||||||
|
RA.Eq(eqb),
|
||||||
|
RA.Eq(eqc),
|
||||||
|
|
||||||
|
RA.Of[A],
|
||||||
|
RA.Of[B],
|
||||||
|
RA.Of[C],
|
||||||
|
|
||||||
|
RA.Of[func(A) A],
|
||||||
|
RA.Of[func(A) B],
|
||||||
|
RA.Of[func(B) C],
|
||||||
|
RA.Of[func(func(A) B) B],
|
||||||
|
|
||||||
|
RA.MonadMap[A, A],
|
||||||
|
RA.MonadMap[A, B],
|
||||||
|
RA.MonadMap[A, C],
|
||||||
|
RA.MonadMap[B, C],
|
||||||
|
|
||||||
|
RA.MonadMap[func(B) C, func(func(A) B) func(A) C],
|
||||||
|
|
||||||
|
RA.MonadChain[A, A],
|
||||||
|
RA.MonadChain[A, B],
|
||||||
|
RA.MonadChain[A, C],
|
||||||
|
RA.MonadChain[B, C],
|
||||||
|
|
||||||
|
RA.MonadAp[A, A],
|
||||||
|
RA.MonadAp[B, A],
|
||||||
|
RA.MonadAp[C, B],
|
||||||
|
RA.MonadAp[C, A],
|
||||||
|
|
||||||
|
RA.MonadAp[B, func(A) B],
|
||||||
|
RA.MonadAp[func(A) C, func(A) B],
|
||||||
|
|
||||||
|
ab,
|
||||||
|
bc,
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
47
array/testing/laws_test.go
Normal file
47
array/testing/laws_test.go
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package testing
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
EQ "github.com/IBM/fp-go/eq"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMonadLaws(t *testing.T) {
|
||||||
|
// some comparison
|
||||||
|
eqa := EQ.FromStrictEquals[bool]()
|
||||||
|
eqb := EQ.FromStrictEquals[int]()
|
||||||
|
eqc := EQ.FromStrictEquals[string]()
|
||||||
|
|
||||||
|
ab := func(a bool) int {
|
||||||
|
if a {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
bc := func(b int) string {
|
||||||
|
return fmt.Sprintf("value %d", b)
|
||||||
|
}
|
||||||
|
|
||||||
|
laws := AssertLaws(t, eqa, eqb, eqc, ab, bc)
|
||||||
|
|
||||||
|
assert.True(t, laws(true))
|
||||||
|
assert.True(t, laws(false))
|
||||||
|
}
|
17
array/uniq.go
Normal file
17
array/uniq.go
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package array
|
||||||
|
|
||||||
|
import (
|
||||||
|
G "github.com/IBM/fp-go/array/generic"
|
||||||
|
)
|
||||||
|
|
||||||
|
// StrictUniq converts an array of arbitrary items into an array or unique items
|
||||||
|
// where uniqueness is determined by the built-in uniqueness constraint
|
||||||
|
func StrictUniq[A comparable](as []A) []A {
|
||||||
|
return G.StrictUniq[[]A](as)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uniq converts an array of arbitrary items into an array or unique items
|
||||||
|
// where uniqueness is determined based on a key extractor function
|
||||||
|
func Uniq[A any, K comparable](f func(A) K) func(as []A) []A {
|
||||||
|
return G.Uniq[[]A](f)
|
||||||
|
}
|
14
array/uniq_test.go
Normal file
14
array/uniq_test.go
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
package array
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestUniq(t *testing.T) {
|
||||||
|
data := From(1, 2, 3, 2, 4, 1)
|
||||||
|
|
||||||
|
uniq := StrictUniq(data)
|
||||||
|
assert.Equal(t, From(1, 2, 3, 4), uniq)
|
||||||
|
}
|
109
assert/assert_test.go
Normal file
109
assert/assert_test.go
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package assert
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
E "github.com/IBM/fp-go/either"
|
||||||
|
EQ "github.com/IBM/fp-go/eq"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errTest = fmt.Errorf("test failure")
|
||||||
|
|
||||||
|
// Eq is the equal predicate checking if objects are equal
|
||||||
|
Eq = EQ.FromEquals(assert.ObjectsAreEqual)
|
||||||
|
)
|
||||||
|
|
||||||
|
func wrap1[T any](wrapped func(t assert.TestingT, expected, actual any, msgAndArgs ...any) bool, t *testing.T, expected T) func(actual T) E.Either[error, T] {
|
||||||
|
return func(actual T) E.Either[error, T] {
|
||||||
|
ok := wrapped(t, expected, actual)
|
||||||
|
if ok {
|
||||||
|
return E.Of[error](actual)
|
||||||
|
}
|
||||||
|
return E.Left[T](errTest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NotEqual tests if the expected and the actual values are not equal
|
||||||
|
func NotEqual[T any](t *testing.T, expected T) func(actual T) E.Either[error, T] {
|
||||||
|
return wrap1(assert.NotEqual, t, expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equal tests if the expected and the actual values are equal
|
||||||
|
func Equal[T any](t *testing.T, expected T) func(actual T) E.Either[error, T] {
|
||||||
|
return wrap1(assert.Equal, t, expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Length tests if an array has the expected length
|
||||||
|
func Length[T any](t *testing.T, expected int) func(actual []T) E.Either[error, []T] {
|
||||||
|
return func(actual []T) E.Either[error, []T] {
|
||||||
|
ok := assert.Len(t, actual, expected)
|
||||||
|
if ok {
|
||||||
|
return E.Of[error](actual)
|
||||||
|
}
|
||||||
|
return E.Left[[]T](errTest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NoError validates that there is no error
|
||||||
|
func NoError[T any](t *testing.T) func(actual E.Either[error, T]) E.Either[error, T] {
|
||||||
|
return func(actual E.Either[error, T]) E.Either[error, T] {
|
||||||
|
return E.MonadFold(actual, func(e error) E.Either[error, T] {
|
||||||
|
assert.NoError(t, e)
|
||||||
|
return E.Left[T](e)
|
||||||
|
}, func(value T) E.Either[error, T] {
|
||||||
|
assert.NoError(t, nil)
|
||||||
|
return E.Right[error](value)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ArrayContains tests if a value is contained in an array
|
||||||
|
func ArrayContains[T any](t *testing.T, expected T) func(actual []T) E.Either[error, []T] {
|
||||||
|
return func(actual []T) E.Either[error, []T] {
|
||||||
|
ok := assert.Contains(t, actual, expected)
|
||||||
|
if ok {
|
||||||
|
return E.Of[error](actual)
|
||||||
|
}
|
||||||
|
return E.Left[[]T](errTest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainsKey tests if a key is contained in a map
|
||||||
|
func ContainsKey[T any, K comparable](t *testing.T, expected K) func(actual map[K]T) E.Either[error, map[K]T] {
|
||||||
|
return func(actual map[K]T) E.Either[error, map[K]T] {
|
||||||
|
ok := assert.Contains(t, actual, expected)
|
||||||
|
if ok {
|
||||||
|
return E.Of[error](actual)
|
||||||
|
}
|
||||||
|
return E.Left[map[K]T](errTest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NotContainsKey tests if a key is not contained in a map
|
||||||
|
func NotContainsKey[T any, K comparable](t *testing.T, expected K) func(actual map[K]T) E.Either[error, map[K]T] {
|
||||||
|
return func(actual map[K]T) E.Either[error, map[K]T] {
|
||||||
|
ok := assert.NotContains(t, actual, expected)
|
||||||
|
if ok {
|
||||||
|
return E.Of[error](actual)
|
||||||
|
}
|
||||||
|
return E.Left[map[K]T](errTest)
|
||||||
|
}
|
||||||
|
}
|
59
boolean/boolean.go
Normal file
59
boolean/boolean.go
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package boolean
|
||||||
|
|
||||||
|
import (
|
||||||
|
EQ "github.com/IBM/fp-go/eq"
|
||||||
|
M "github.com/IBM/fp-go/monoid"
|
||||||
|
O "github.com/IBM/fp-go/ord"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// MonoidAny is the boolean [M.Monoid] under disjunction
|
||||||
|
MonoidAny = M.MakeMonoid(
|
||||||
|
func(l, r bool) bool {
|
||||||
|
return l || r
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
|
||||||
|
// MonoidAll is the boolean [M.Monoid] under conjuction
|
||||||
|
MonoidAll = M.MakeMonoid(
|
||||||
|
func(l, r bool) bool {
|
||||||
|
return l && r
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
|
||||||
|
// Eq is the equals predicate for boolean
|
||||||
|
Eq = EQ.FromStrictEquals[bool]()
|
||||||
|
|
||||||
|
// Ord is the strict ordering for boolean
|
||||||
|
Ord = O.MakeOrd(func(l, r bool) int {
|
||||||
|
if l {
|
||||||
|
if r {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return +1
|
||||||
|
}
|
||||||
|
if r {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}, func(l, r bool) bool {
|
||||||
|
return l == r
|
||||||
|
})
|
||||||
|
)
|
@@ -32,20 +32,20 @@ type bounded[T any] struct {
|
|||||||
b T
|
b T
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self bounded[T]) Equals(x, y T) bool {
|
func (b bounded[T]) Equals(x, y T) bool {
|
||||||
return self.e(x, y)
|
return b.e(x, y)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self bounded[T]) Compare(x, y T) int {
|
func (b bounded[T]) Compare(x, y T) int {
|
||||||
return self.c(x, y)
|
return b.c(x, y)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self bounded[T]) Top() T {
|
func (b bounded[T]) Top() T {
|
||||||
return self.t
|
return b.t
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self bounded[T]) Bottom() T {
|
func (b bounded[T]) Bottom() T {
|
||||||
return self.b
|
return b.b
|
||||||
}
|
}
|
||||||
|
|
||||||
// MakeBounded creates an instance of a bounded type
|
// MakeBounded creates an instance of a bounded type
|
||||||
|
@@ -15,6 +15,10 @@
|
|||||||
|
|
||||||
package bytes
|
package bytes
|
||||||
|
|
||||||
|
func Empty() []byte {
|
||||||
|
return Monoid.Empty()
|
||||||
|
}
|
||||||
|
|
||||||
func ToString(a []byte) string {
|
func ToString(a []byte) string {
|
||||||
return string(a)
|
return string(a)
|
||||||
}
|
}
|
||||||
|
@@ -34,5 +34,6 @@ func Commands() []*C.Command {
|
|||||||
IOEitherCommand(),
|
IOEitherCommand(),
|
||||||
IOCommand(),
|
IOCommand(),
|
||||||
IOOptionCommand(),
|
IOOptionCommand(),
|
||||||
|
DICommand(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -365,6 +365,70 @@ func generateContextReaderIOEitherEitherize(f, fg *os.File, i int) {
|
|||||||
fmt.Fprintln(fg, "}")
|
fmt.Fprintln(fg, "}")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func generateContextReaderIOEitherUneitherize(f, fg *os.File, i int) {
|
||||||
|
// non generic version
|
||||||
|
fmt.Fprintf(f, "\n// Uneitherize%d converts a function with %d parameters returning a [ReaderIOEither[R]] into a function with %d parameters returning a tuple.\n// The first parameter is considered to be the [context.Context].\n", i, i+1, i)
|
||||||
|
fmt.Fprintf(f, "func Uneitherize%d[F ~func(", i)
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") ReaderIOEither[R]")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, ", T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ", R any](f F) func(context.Context")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, ", T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") (R, error) {\n")
|
||||||
|
fmt.Fprintf(f, " return G.Uneitherize%d[ReaderIOEither[R]", i)
|
||||||
|
|
||||||
|
fmt.Fprintf(f, ", func(context.Context")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, ", T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ")(R, error)](f)\n")
|
||||||
|
fmt.Fprintln(f, "}")
|
||||||
|
|
||||||
|
// generic version
|
||||||
|
fmt.Fprintf(fg, "\n// Uneitherize%d converts a function with %d parameters returning a [GRA] into a function with %d parameters returning a tuple.\n// The first parameter is considered to be the [context.Context].\n", i, i, i)
|
||||||
|
fmt.Fprintf(fg, "func Uneitherize%d[GRA ~func(context.Context) GIOA, F ~func(context.Context", i)
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(fg, ", T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, ") (R, error), GIOA ~func() E.Either[error, R]")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(fg, ", T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, ", R any](f func(")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(fg, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, "T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, ") GRA) F {\n")
|
||||||
|
|
||||||
|
fmt.Fprintf(fg, " return func(c context.Context")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(fg, ", t%d T%d", j, j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, ") (R, error) {\n")
|
||||||
|
fmt.Fprintf(fg, " return E.UnwrapError(f(")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(fg, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, "t%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, ")(c)())\n")
|
||||||
|
fmt.Fprintf(fg, " }\n")
|
||||||
|
fmt.Fprintf(fg, "}\n")
|
||||||
|
}
|
||||||
|
|
||||||
func generateContextReaderIOEitherHelpers(filename string, count int) error {
|
func generateContextReaderIOEitherHelpers(filename string, count int) error {
|
||||||
dir, err := os.Getwd()
|
dir, err := os.Getwd()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -420,10 +484,12 @@ import (
|
|||||||
`)
|
`)
|
||||||
|
|
||||||
generateContextReaderIOEitherEitherize(f, fg, 0)
|
generateContextReaderIOEitherEitherize(f, fg, 0)
|
||||||
|
generateContextReaderIOEitherUneitherize(f, fg, 0)
|
||||||
|
|
||||||
for i := 1; i <= count; i++ {
|
for i := 1; i <= count; i++ {
|
||||||
// eitherize
|
// eitherize
|
||||||
generateContextReaderIOEitherEitherize(f, fg, i)
|
generateContextReaderIOEitherEitherize(f, fg, i)
|
||||||
|
generateContextReaderIOEitherUneitherize(f, fg, i)
|
||||||
// sequenceT
|
// sequenceT
|
||||||
generateContextReaderIOEitherSequenceT("")(f, fg, i)
|
generateContextReaderIOEitherSequenceT("")(f, fg, i)
|
||||||
generateContextReaderIOEitherSequenceT("Seq")(f, fg, i)
|
generateContextReaderIOEitherSequenceT("Seq")(f, fg, i)
|
||||||
|
231
cli/di.go
Normal file
231
cli/di.go
Normal file
@@ -0,0 +1,231 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
C "github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func generateMakeProvider(f *os.File, i int) {
|
||||||
|
// non generic version
|
||||||
|
fmt.Fprintf(f, "\n// MakeProvider%d creates a [DIE.Provider] for an [InjectionToken] from a function with %d dependencies\n", i, i)
|
||||||
|
fmt.Fprintf(f, "func MakeProvider%d[", i)
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T%d", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, " any, R any](\n")
|
||||||
|
fmt.Fprintf(f, " token InjectionToken[R],\n")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, " d%d Dependency[T%d],\n", j+1, j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, " f func(")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T%d", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") IOE.IOEither[error, R],\n")
|
||||||
|
fmt.Fprintf(f, ") DIE.Provider {\n")
|
||||||
|
fmt.Fprint(f, " return DIE.MakeProvider(\n")
|
||||||
|
fmt.Fprint(f, " token,\n")
|
||||||
|
fmt.Fprintf(f, " MakeProviderFactory%d(\n", i)
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, " d%d,\n", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprint(f, " f,\n")
|
||||||
|
fmt.Fprint(f, " ))\n")
|
||||||
|
fmt.Fprintf(f, "}\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateMakeTokenWithDefault(f *os.File, i int) {
|
||||||
|
// non generic version
|
||||||
|
fmt.Fprintf(f, "\n// MakeTokenWithDefault%d creates an [InjectionToken] with a default implementation with %d dependencies\n", i, i)
|
||||||
|
fmt.Fprintf(f, "func MakeTokenWithDefault%d[", i)
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T%d", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, " any, R any](\n")
|
||||||
|
fmt.Fprintf(f, " name string,\n")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, " d%d Dependency[T%d],\n", j+1, j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, " f func(")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T%d", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") IOE.IOEither[error, R],\n")
|
||||||
|
fmt.Fprintf(f, ") InjectionToken[R] {\n")
|
||||||
|
fmt.Fprintf(f, " return MakeTokenWithDefault[R](name, MakeProviderFactory%d(\n", i)
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, " d%d,\n", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprint(f, " f,\n")
|
||||||
|
fmt.Fprint(f, " ))\n")
|
||||||
|
fmt.Fprintf(f, "}\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateMakeProviderFactory(f *os.File, i int) {
|
||||||
|
// non generic version
|
||||||
|
fmt.Fprintf(f, "\n// MakeProviderFactory%d creates a [DIE.ProviderFactory] from a function with %d arguments and %d dependencies\n", i, i, i)
|
||||||
|
fmt.Fprintf(f, "func MakeProviderFactory%d[", i)
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T%d", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, " any, R any](\n")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, " d%d Dependency[T%d],\n", j+1, j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, " f func(")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T%d", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") IOE.IOEither[error, R],\n")
|
||||||
|
fmt.Fprintf(f, ") DIE.ProviderFactory {\n")
|
||||||
|
fmt.Fprint(f, " return DIE.MakeProviderFactory(\n")
|
||||||
|
fmt.Fprint(f, " A.From[DIE.Dependency](\n")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, " d%d,\n", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprint(f, " ),\n")
|
||||||
|
fmt.Fprintf(f, " eraseProviderFactory%d(\n", i)
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, " d%d,\n", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprint(f, " f,\n")
|
||||||
|
fmt.Fprint(f, " ),\n")
|
||||||
|
fmt.Fprint(f, " )\n")
|
||||||
|
fmt.Fprintf(f, "}\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateEraseProviderFactory(f *os.File, i int) {
|
||||||
|
// non generic version
|
||||||
|
fmt.Fprintf(f, "\n// eraseProviderFactory%d creates a function that takes a variadic number of untyped arguments and from a function of %d strongly typed arguments and %d dependencies\n", i, i, i)
|
||||||
|
fmt.Fprintf(f, "func eraseProviderFactory%d[", i)
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T%d", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, " any, R any](\n")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, " d%d Dependency[T%d],\n", j+1, j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, " f func(")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T%d", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") IOE.IOEither[error, R]) func(params ...any) IOE.IOEither[error, any] {\n")
|
||||||
|
fmt.Fprintf(f, " ft := eraseTuple(T.Tupled%d(f))\n", i)
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, " t%d := lookupAt[T%d](%d, d%d)\n", j+1, j+1, j, j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprint(f, " return func(params ...any) IOE.IOEither[error, any] {\n")
|
||||||
|
fmt.Fprintf(f, " return ft(E.SequenceT%d(\n", i)
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, " t%d(params),\n", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprint(f, " ))\n")
|
||||||
|
fmt.Fprint(f, " }\n")
|
||||||
|
fmt.Fprintf(f, "}\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateDIHelpers(filename string, count int) error {
|
||||||
|
dir, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
absDir, err := filepath.Abs(dir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
pkg := filepath.Base(absDir)
|
||||||
|
f, err := os.Create(filepath.Clean(filename))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
// log
|
||||||
|
log.Printf("Generating code in [%s] for package [%s] with [%d] repetitions ...", filename, pkg, count)
|
||||||
|
|
||||||
|
// some header
|
||||||
|
fmt.Fprintln(f, "// Code generated by go generate; DO NOT EDIT.")
|
||||||
|
fmt.Fprintln(f, "// This file was generated by robots at")
|
||||||
|
fmt.Fprintf(f, "// %s\n\n", time.Now())
|
||||||
|
|
||||||
|
fmt.Fprintf(f, "package %s\n\n", pkg)
|
||||||
|
|
||||||
|
fmt.Fprint(f, `
|
||||||
|
import (
|
||||||
|
E "github.com/IBM/fp-go/either"
|
||||||
|
IOE "github.com/IBM/fp-go/ioeither"
|
||||||
|
T "github.com/IBM/fp-go/tuple"
|
||||||
|
A "github.com/IBM/fp-go/array"
|
||||||
|
DIE "github.com/IBM/fp-go/di/erasure"
|
||||||
|
)
|
||||||
|
`)
|
||||||
|
|
||||||
|
for i := 1; i <= count; i++ {
|
||||||
|
generateEraseProviderFactory(f, i)
|
||||||
|
generateMakeProviderFactory(f, i)
|
||||||
|
generateMakeTokenWithDefault(f, i)
|
||||||
|
generateMakeProvider(f, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func DICommand() *C.Command {
|
||||||
|
return &C.Command{
|
||||||
|
Name: "di",
|
||||||
|
Usage: "generate code for the dependency injection package",
|
||||||
|
Flags: []C.Flag{
|
||||||
|
flagCount,
|
||||||
|
flagFilename,
|
||||||
|
},
|
||||||
|
Action: func(ctx *C.Context) error {
|
||||||
|
return generateDIHelpers(
|
||||||
|
ctx.String(keyFilename),
|
||||||
|
ctx.Int(keyCount),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
@@ -115,16 +115,14 @@ func generateEitherize(f *os.File, i int) {
|
|||||||
fmt.Fprintf(f, "t%d T%d", j, j)
|
fmt.Fprintf(f, "t%d T%d", j, j)
|
||||||
}
|
}
|
||||||
fmt.Fprintf(f, ") Either[error, R] {\n")
|
fmt.Fprintf(f, ") Either[error, R] {\n")
|
||||||
fmt.Fprintf(f, " return TryCatchError(func() (R, error) {\n")
|
fmt.Fprintf(f, " return TryCatchError(f(")
|
||||||
fmt.Fprintf(f, " return f(")
|
|
||||||
for j := 0; j < i; j++ {
|
for j := 0; j < i; j++ {
|
||||||
if j > 0 {
|
if j > 0 {
|
||||||
fmt.Fprintf(f, ", ")
|
fmt.Fprintf(f, ", ")
|
||||||
}
|
}
|
||||||
fmt.Fprintf(f, "t%d", j)
|
fmt.Fprintf(f, "t%d", j)
|
||||||
}
|
}
|
||||||
fmt.Fprintln(f, ")")
|
fmt.Fprintln(f, "))")
|
||||||
fmt.Fprintln(f, " })")
|
|
||||||
fmt.Fprintln(f, " }")
|
fmt.Fprintln(f, " }")
|
||||||
fmt.Fprintln(f, "}")
|
fmt.Fprintln(f, "}")
|
||||||
}
|
}
|
||||||
|
81
cli/pipe.go
81
cli/pipe.go
@@ -25,6 +25,40 @@ import (
|
|||||||
C "github.com/urfave/cli/v2"
|
C "github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func generateUnsliced(f *os.File, i int) {
|
||||||
|
// Create the optionize version
|
||||||
|
fmt.Fprintf(f, "\n// Unsliced%d converts a function taking a slice parameter into a function with %d parameters\n", i, i)
|
||||||
|
fmt.Fprintf(f, "func Unsliced%d[F ~func([]T) R, T, R any](f F) func(", i)
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") R {\n")
|
||||||
|
fmt.Fprintf(f, " return func(")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "t%d", j+1)
|
||||||
|
}
|
||||||
|
if i > 0 {
|
||||||
|
fmt.Fprintf(f, " T")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") R {\n")
|
||||||
|
fmt.Fprintf(f, " return f([]T{")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "t%d", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintln(f, "})")
|
||||||
|
fmt.Fprintln(f, " }")
|
||||||
|
fmt.Fprintln(f, "}")
|
||||||
|
}
|
||||||
|
|
||||||
func generateVariadic(f *os.File, i int) {
|
func generateVariadic(f *os.File, i int) {
|
||||||
// Create the nullary version
|
// Create the nullary version
|
||||||
fmt.Fprintf(f, "\n// Variadic%d converts a function taking %d parameters and a final slice into a function with %d parameters but a final variadic argument\n", i, i, i)
|
fmt.Fprintf(f, "\n// Variadic%d converts a function taking %d parameters and a final slice into a function with %d parameters but a final variadic argument\n", i, i, i)
|
||||||
@@ -83,7 +117,7 @@ func generateVariadic(f *os.File, i int) {
|
|||||||
fmt.Fprintf(f, "v)\n")
|
fmt.Fprintf(f, "v)\n")
|
||||||
fmt.Fprintf(f, " }\n")
|
fmt.Fprintf(f, " }\n")
|
||||||
|
|
||||||
fmt.Fprintf(f, "}")
|
fmt.Fprintf(f, "}\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateUnvariadic(f *os.File, i int) {
|
func generateUnvariadic(f *os.File, i int) {
|
||||||
@@ -144,7 +178,7 @@ func generateUnvariadic(f *os.File, i int) {
|
|||||||
fmt.Fprintf(f, "v...)\n")
|
fmt.Fprintf(f, "v...)\n")
|
||||||
fmt.Fprintf(f, " }\n")
|
fmt.Fprintf(f, " }\n")
|
||||||
|
|
||||||
fmt.Fprintf(f, "}")
|
fmt.Fprintf(f, "}\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateNullary(f *os.File, i int) {
|
func generateNullary(f *os.File, i int) {
|
||||||
@@ -231,10 +265,15 @@ func generatePipe(f *os.File, i int) {
|
|||||||
fmt.Fprintf(f, ", f%d F%d", j, j)
|
fmt.Fprintf(f, ", f%d F%d", j, j)
|
||||||
}
|
}
|
||||||
fmt.Fprintf(f, ") T%d {\n", i)
|
fmt.Fprintf(f, ") T%d {\n", i)
|
||||||
for j := 1; j <= i; j++ {
|
fmt.Fprintf(f, " return ")
|
||||||
fmt.Fprintf(f, " t%d := f%d(t%d)\n", j, j, j-1)
|
for j := i; j >= 1; j-- {
|
||||||
|
fmt.Fprintf(f, "f%d(", j)
|
||||||
}
|
}
|
||||||
fmt.Fprintf(f, " return t%d\n", i)
|
fmt.Fprintf(f, "t0")
|
||||||
|
for j := 1; j <= i; j++ {
|
||||||
|
fmt.Fprintf(f, ")")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "\n")
|
||||||
fmt.Fprintln(f, "}")
|
fmt.Fprintln(f, "}")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -261,15 +300,16 @@ func recurseCurry(f *os.File, indent string, total, count int) {
|
|||||||
func generateCurry(f *os.File, i int) {
|
func generateCurry(f *os.File, i int) {
|
||||||
// Create the curry version
|
// Create the curry version
|
||||||
fmt.Fprintf(f, "\n// Curry%d takes a function with %d parameters and returns a cascade of functions each taking only one parameter.\n// The inverse function is [Uncurry%d]\n", i, i, i)
|
fmt.Fprintf(f, "\n// Curry%d takes a function with %d parameters and returns a cascade of functions each taking only one parameter.\n// The inverse function is [Uncurry%d]\n", i, i, i)
|
||||||
fmt.Fprintf(f, "func Curry%d[T0", i)
|
fmt.Fprintf(f, "func Curry%d[FCT ~func(T0", i)
|
||||||
for j := 1; j <= i; j++ {
|
for j := 1; j < i; j++ {
|
||||||
fmt.Fprintf(f, ", T%d", j)
|
fmt.Fprintf(f, ", T%d", j)
|
||||||
}
|
}
|
||||||
fmt.Fprintf(f, " any](f func(T0")
|
fmt.Fprintf(f, ") T%d", i)
|
||||||
for j := 2; j <= i; j++ {
|
// type arguments
|
||||||
fmt.Fprintf(f, ", T%d", j-1)
|
for j := 0; j <= i; j++ {
|
||||||
|
fmt.Fprintf(f, ", T%d", j)
|
||||||
}
|
}
|
||||||
fmt.Fprintf(f, ") T%d) func(T0)", i)
|
fmt.Fprintf(f, " any](f FCT) func(T0)")
|
||||||
for j := 2; j <= i; j++ {
|
for j := 2; j <= i; j++ {
|
||||||
fmt.Fprintf(f, " func(T%d)", j-1)
|
fmt.Fprintf(f, " func(T%d)", j-1)
|
||||||
}
|
}
|
||||||
@@ -281,15 +321,16 @@ func generateCurry(f *os.File, i int) {
|
|||||||
func generateUncurry(f *os.File, i int) {
|
func generateUncurry(f *os.File, i int) {
|
||||||
// Create the uncurry version
|
// Create the uncurry version
|
||||||
fmt.Fprintf(f, "\n// Uncurry%d takes a cascade of %d functions each taking only one parameter and returns a function with %d parameters .\n// The inverse function is [Curry%d]\n", i, i, i, i)
|
fmt.Fprintf(f, "\n// Uncurry%d takes a cascade of %d functions each taking only one parameter and returns a function with %d parameters .\n// The inverse function is [Curry%d]\n", i, i, i, i)
|
||||||
fmt.Fprintf(f, "func Uncurry%d[T0", i)
|
fmt.Fprintf(f, "func Uncurry%d[FCT ~func(T0)", i)
|
||||||
for j := 1; j <= i; j++ {
|
for j := 1; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, " func(T%d)", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, " T%d", i)
|
||||||
|
// the type parameters
|
||||||
|
for j := 0; j <= i; j++ {
|
||||||
fmt.Fprintf(f, ", T%d", j)
|
fmt.Fprintf(f, ", T%d", j)
|
||||||
}
|
}
|
||||||
fmt.Fprintf(f, " any](f")
|
fmt.Fprintf(f, " any](f FCT) func(")
|
||||||
for j := 1; j <= i; j++ {
|
|
||||||
fmt.Fprintf(f, " func(T%d)", j-1)
|
|
||||||
}
|
|
||||||
fmt.Fprintf(f, " T%d) func(", i)
|
|
||||||
for j := 1; j <= i; j++ {
|
for j := 1; j <= i; j++ {
|
||||||
if j > 1 {
|
if j > 1 {
|
||||||
fmt.Fprintf(f, ", ")
|
fmt.Fprintf(f, ", ")
|
||||||
@@ -347,6 +388,8 @@ func generatePipeHelpers(filename string, count int) error {
|
|||||||
generateVariadic(f, 0)
|
generateVariadic(f, 0)
|
||||||
// unvariadic
|
// unvariadic
|
||||||
generateUnvariadic(f, 0)
|
generateUnvariadic(f, 0)
|
||||||
|
// unsliced
|
||||||
|
generateUnsliced(f, 0)
|
||||||
|
|
||||||
for i := 1; i <= count; i++ {
|
for i := 1; i <= count; i++ {
|
||||||
|
|
||||||
@@ -364,6 +407,8 @@ func generatePipeHelpers(filename string, count int) error {
|
|||||||
generateVariadic(f, i)
|
generateVariadic(f, i)
|
||||||
// unvariadic
|
// unvariadic
|
||||||
generateUnvariadic(f, i)
|
generateUnvariadic(f, i)
|
||||||
|
// unsliced
|
||||||
|
generateUnsliced(f, i)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@@ -136,6 +136,70 @@ func generateReaderIOEitherEitherize(f, fg *os.File, i int) {
|
|||||||
fmt.Fprintf(fg, "}\n")
|
fmt.Fprintf(fg, "}\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func generateReaderIOEitherUneitherize(f, fg *os.File, i int) {
|
||||||
|
// non generic version
|
||||||
|
fmt.Fprintf(f, "\n// Uneitherize%d converts a function with %d parameters returning a [ReaderIOEither[C, error, R]] into a function with %d parameters returning a tuple.\n// The first parameter is considered to be the context [C].\n", i, i+1, i)
|
||||||
|
fmt.Fprintf(f, "func Uneitherize%d[F ~func(", i)
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") ReaderIOEither[C, error, R]")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, ", T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ", C, R any](f F) func(C")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, ", T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") (R, error) {\n")
|
||||||
|
fmt.Fprintf(f, " return G.Uneitherize%d[ReaderIOEither[C, error, R]", i)
|
||||||
|
|
||||||
|
fmt.Fprintf(f, ", func(C")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, ", T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ")(R, error)](f)\n")
|
||||||
|
fmt.Fprintln(f, "}")
|
||||||
|
|
||||||
|
// generic version
|
||||||
|
fmt.Fprintf(fg, "\n// Uneitherize%d converts a function with %d parameters returning a [GRA] into a function with %d parameters returning a tuple.\n// The first parameter is considered to be the context [C].\n", i, i, i)
|
||||||
|
fmt.Fprintf(fg, "func Uneitherize%d[GRA ~func(C) GIOA, F ~func(C", i)
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(fg, ", T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, ") (R, error), GIOA ~func() E.Either[error, R]")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(fg, ", T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, ", C, R any](f func(")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(fg, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, "T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, ") GRA) F {\n")
|
||||||
|
|
||||||
|
fmt.Fprintf(fg, " return func(c C")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(fg, ", t%d T%d", j, j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, ") (R, error) {\n")
|
||||||
|
fmt.Fprintf(fg, " return E.UnwrapError(f(")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(fg, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, "t%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, ")(c)())\n")
|
||||||
|
fmt.Fprintf(fg, " }\n")
|
||||||
|
fmt.Fprintf(fg, "}\n")
|
||||||
|
}
|
||||||
|
|
||||||
func generateReaderIOEitherHelpers(filename string, count int) error {
|
func generateReaderIOEitherHelpers(filename string, count int) error {
|
||||||
dir, err := os.Getwd()
|
dir, err := os.Getwd()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -197,12 +261,16 @@ import (
|
|||||||
generateReaderIOEitherFrom(f, fg, 0)
|
generateReaderIOEitherFrom(f, fg, 0)
|
||||||
// eitherize
|
// eitherize
|
||||||
generateReaderIOEitherEitherize(f, fg, 0)
|
generateReaderIOEitherEitherize(f, fg, 0)
|
||||||
|
// uneitherize
|
||||||
|
generateReaderIOEitherUneitherize(f, fg, 0)
|
||||||
|
|
||||||
for i := 1; i <= count; i++ {
|
for i := 1; i <= count; i++ {
|
||||||
// from
|
// from
|
||||||
generateReaderIOEitherFrom(f, fg, i)
|
generateReaderIOEitherFrom(f, fg, i)
|
||||||
// eitherize
|
// eitherize
|
||||||
generateReaderIOEitherEitherize(f, fg, i)
|
generateReaderIOEitherEitherize(f, fg, i)
|
||||||
|
// uneitherize
|
||||||
|
generateReaderIOEitherUneitherize(f, fg, i)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
15
cli/templates/functions.go
Normal file
15
cli/templates/functions.go
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
package templates
|
||||||
|
|
||||||
|
import (
|
||||||
|
"text/template"
|
||||||
|
|
||||||
|
E "github.com/IBM/fp-go/either"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
templateFunctions = template.FuncMap{}
|
||||||
|
)
|
||||||
|
|
||||||
|
func Parse(name, tmpl string) E.Either[error, *template.Template] {
|
||||||
|
return E.TryCatchError(template.New(name).Funcs(templateFunctions).Parse(tmpl))
|
||||||
|
}
|
42
cli/tuple.go
42
cli/tuple.go
@@ -338,7 +338,7 @@ func generateUntupled(f *os.File, i int) {
|
|||||||
|
|
||||||
func generateTupled(f *os.File, i int) {
|
func generateTupled(f *os.File, i int) {
|
||||||
// Create the optionize version
|
// Create the optionize version
|
||||||
fmt.Fprintf(f, "\n// Tupled%d converts a function with %d parameters returning into a function taking a Tuple%d\n// The inverse function is [Untupled%d]\n", i, i, i, i)
|
fmt.Fprintf(f, "\n// Tupled%d converts a function with %d parameters into a function taking a Tuple%d\n// The inverse function is [Untupled%d]\n", i, i, i, i)
|
||||||
fmt.Fprintf(f, "func Tupled%d[F ~func(", i)
|
fmt.Fprintf(f, "func Tupled%d[F ~func(", i)
|
||||||
for j := 0; j < i; j++ {
|
for j := 0; j < i; j++ {
|
||||||
if j > 0 {
|
if j > 0 {
|
||||||
@@ -405,8 +405,6 @@ func generateTupleHelpers(filename string, count int) error {
|
|||||||
|
|
||||||
fmt.Fprintf(f, `
|
fmt.Fprintf(f, `
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"encoding/json"
|
|
||||||
M "github.com/IBM/fp-go/monoid"
|
M "github.com/IBM/fp-go/monoid"
|
||||||
O "github.com/IBM/fp-go/ord"
|
O "github.com/IBM/fp-go/ord"
|
||||||
)
|
)
|
||||||
@@ -457,7 +455,7 @@ func generateTupleMarshal(f *os.File, i int) {
|
|||||||
fmt.Fprintf(f, "func (t ")
|
fmt.Fprintf(f, "func (t ")
|
||||||
writeTupleType(f, "T", i)
|
writeTupleType(f, "T", i)
|
||||||
fmt.Fprintf(f, ") MarshalJSON() ([]byte, error) {\n")
|
fmt.Fprintf(f, ") MarshalJSON() ([]byte, error) {\n")
|
||||||
fmt.Fprintf(f, " return json.Marshal([]any{")
|
fmt.Fprintf(f, " return tupleMarshalJSON(")
|
||||||
// function prototypes
|
// function prototypes
|
||||||
for j := 1; j <= i; j++ {
|
for j := 1; j <= i; j++ {
|
||||||
if j > 1 {
|
if j > 1 {
|
||||||
@@ -465,7 +463,7 @@ func generateTupleMarshal(f *os.File, i int) {
|
|||||||
}
|
}
|
||||||
fmt.Fprintf(f, "t.F%d", j)
|
fmt.Fprintf(f, "t.F%d", j)
|
||||||
}
|
}
|
||||||
fmt.Fprintf(f, "})\n")
|
fmt.Fprintf(f, ")\n")
|
||||||
fmt.Fprintf(f, "}\n")
|
fmt.Fprintf(f, "}\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -475,19 +473,12 @@ func generateTupleUnmarshal(f *os.File, i int) {
|
|||||||
fmt.Fprintf(f, "func (t *")
|
fmt.Fprintf(f, "func (t *")
|
||||||
writeTupleType(f, "T", i)
|
writeTupleType(f, "T", i)
|
||||||
fmt.Fprintf(f, ") UnmarshalJSON(data []byte) error {\n")
|
fmt.Fprintf(f, ") UnmarshalJSON(data []byte) error {\n")
|
||||||
fmt.Fprintf(f, " var tmp []json.RawMessage\n")
|
fmt.Fprintf(f, " return tupleUnmarshalJSON(data")
|
||||||
fmt.Fprintf(f, " if err := json.Unmarshal(data, &tmp); err != nil {return err}\n")
|
// function prototypes
|
||||||
fmt.Fprintf(f, " l := len(tmp)\n")
|
|
||||||
// unmarshal fields
|
|
||||||
for j := 1; j <= i; j++ {
|
for j := 1; j <= i; j++ {
|
||||||
fmt.Fprintf(f, " if l > %d {\n", j-1)
|
fmt.Fprintf(f, ", &t.F%d", j)
|
||||||
fmt.Fprintf(f, " if err := json.Unmarshal(tmp[%d], &t.F%d); err != nil {return err}\n", j-1, j)
|
|
||||||
}
|
}
|
||||||
fmt.Fprintf(f, " ")
|
fmt.Fprintf(f, ")\n")
|
||||||
for j := 1; j <= i; j++ {
|
|
||||||
fmt.Fprintf(f, "}")
|
|
||||||
}
|
|
||||||
fmt.Fprintf(f, "\n return nil\n")
|
|
||||||
fmt.Fprintf(f, "}\n")
|
fmt.Fprintf(f, "}\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -570,30 +561,13 @@ func generateTupleString(f *os.File, i int) {
|
|||||||
writeTupleType(f, "T", i)
|
writeTupleType(f, "T", i)
|
||||||
fmt.Fprintf(f, ") String() string {\n")
|
fmt.Fprintf(f, ") String() string {\n")
|
||||||
// convert to string
|
// convert to string
|
||||||
fmt.Fprintf(f, " return fmt.Sprintf(\"Tuple%d[", i)
|
fmt.Fprint(f, " return tupleString(")
|
||||||
for j := 1; j <= i; j++ {
|
|
||||||
if j > 1 {
|
|
||||||
fmt.Fprintf(f, ", ")
|
|
||||||
}
|
|
||||||
fmt.Fprintf(f, "%s", "%T")
|
|
||||||
}
|
|
||||||
fmt.Fprintf(f, "](")
|
|
||||||
for j := 1; j <= i; j++ {
|
|
||||||
if j > 1 {
|
|
||||||
fmt.Fprintf(f, ", ")
|
|
||||||
}
|
|
||||||
fmt.Fprintf(f, "%s", "%v")
|
|
||||||
}
|
|
||||||
fmt.Fprintf(f, ")\", ")
|
|
||||||
for j := 1; j <= i; j++ {
|
for j := 1; j <= i; j++ {
|
||||||
if j > 1 {
|
if j > 1 {
|
||||||
fmt.Fprintf(f, ", ")
|
fmt.Fprintf(f, ", ")
|
||||||
}
|
}
|
||||||
fmt.Fprintf(f, "t.F%d", j)
|
fmt.Fprintf(f, "t.F%d", j)
|
||||||
}
|
}
|
||||||
for j := 1; j <= i; j++ {
|
|
||||||
fmt.Fprintf(f, ", t.F%d", j)
|
|
||||||
}
|
|
||||||
fmt.Fprintf(f, ")\n")
|
fmt.Fprintf(f, ")\n")
|
||||||
fmt.Fprintf(f, "}\n")
|
fmt.Fprintf(f, "}\n")
|
||||||
}
|
}
|
||||||
|
@@ -22,30 +22,28 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Const[E, A any] struct {
|
type Const[E, A any] struct {
|
||||||
Value E
|
value E
|
||||||
}
|
}
|
||||||
|
|
||||||
func Make[E, A any](e E) Const[E, A] {
|
func Make[E, A any](e E) Const[E, A] {
|
||||||
return Const[E, A]{Value: e}
|
return Const[E, A]{value: e}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Unwrap[E, A any](c Const[E, A]) E {
|
func Unwrap[E, A any](c Const[E, A]) E {
|
||||||
return c.Value
|
return c.value
|
||||||
}
|
}
|
||||||
|
|
||||||
func Of[E, A any](m M.Monoid[E]) func(A) Const[E, A] {
|
func Of[E, A any](m M.Monoid[E]) func(A) Const[E, A] {
|
||||||
return func(a A) Const[E, A] {
|
return F.Constant1[A](Make[E, A](m.Empty()))
|
||||||
return Make[E, A](m.Empty())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func MonadMap[E, A, B any](fa Const[E, A], f func(A) B) Const[E, B] {
|
func MonadMap[E, A, B any](fa Const[E, A], _ func(A) B) Const[E, B] {
|
||||||
return Make[E, B](fa.Value)
|
return Make[E, B](fa.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
func MonadAp[E, A, B any](s S.Semigroup[E]) func(fab Const[E, func(A) B], fa Const[E, A]) Const[E, B] {
|
func MonadAp[E, A, B any](s S.Semigroup[E]) func(fab Const[E, func(A) B], fa Const[E, A]) Const[E, B] {
|
||||||
return func(fab Const[E, func(A) B], fa Const[E, A]) Const[E, B] {
|
return func(fab Const[E, func(A) B], fa Const[E, A]) Const[E, B] {
|
||||||
return Make[E, B](s.Concat(fab.Value, fa.Value))
|
return Make[E, B](s.Concat(fab.value, fa.value))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -19,15 +19,14 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
|
|
||||||
E "github.com/IBM/fp-go/either"
|
E "github.com/IBM/fp-go/either"
|
||||||
ET "github.com/IBM/fp-go/either"
|
|
||||||
IOE "github.com/IBM/fp-go/ioeither/generic"
|
IOE "github.com/IBM/fp-go/ioeither/generic"
|
||||||
)
|
)
|
||||||
|
|
||||||
// withContext wraps an existing IOEither and performs a context check for cancellation before delegating
|
// WithContext wraps an existing IOEither and performs a context check for cancellation before delegating
|
||||||
func WithContext[GIO ~func() E.Either[error, A], A any](ctx context.Context, ma GIO) GIO {
|
func WithContext[GIO ~func() E.Either[error, A], A any](ctx context.Context, ma GIO) GIO {
|
||||||
return IOE.MakeIO[GIO](func() E.Either[error, A] {
|
return IOE.MakeIO[GIO](func() E.Either[error, A] {
|
||||||
if err := context.Cause(ctx); err != nil {
|
if err := context.Cause(ctx); err != nil {
|
||||||
return ET.Left[A](err)
|
return E.Left[A](err)
|
||||||
}
|
}
|
||||||
return ma()
|
return ma()
|
||||||
})
|
})
|
||||||
|
@@ -1,53 +0,0 @@
|
|||||||
// Copyright (c) 2023 IBM Corp.
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package reader
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
|
|
||||||
R "github.com/IBM/fp-go/reader/generic"
|
|
||||||
)
|
|
||||||
|
|
||||||
// these functions curry a golang function with the context as the firsr parameter into a either reader with the context as the last parameter
|
|
||||||
// this goes back to the advice in https://pkg.go.dev/context to put the context as a first parameter as a convention
|
|
||||||
|
|
||||||
func Curry0[A any](f func(context.Context) A) Reader[A] {
|
|
||||||
return R.Curry0[Reader[A]](f)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Curry1[T1, A any](f func(context.Context, T1) A) func(T1) Reader[A] {
|
|
||||||
return R.Curry1[Reader[A]](f)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Curry2[T1, T2, A any](f func(context.Context, T1, T2) A) func(T1) func(T2) Reader[A] {
|
|
||||||
return R.Curry2[Reader[A]](f)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Curry3[T1, T2, T3, A any](f func(context.Context, T1, T2, T3) A) func(T1) func(T2) func(T3) Reader[A] {
|
|
||||||
return R.Curry3[Reader[A]](f)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Uncurry1[T1, A any](f func(T1) Reader[A]) func(context.Context, T1) A {
|
|
||||||
return R.Uncurry1(f)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Uncurry2[T1, T2, A any](f func(T1) func(T2) Reader[A]) func(context.Context, T1, T2) A {
|
|
||||||
return R.Uncurry2(f)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Uncurry3[T1, T2, T3, A any](f func(T1) func(T2) func(T3) Reader[A]) func(context.Context, T1, T2, T3) A {
|
|
||||||
return R.Uncurry3(f)
|
|
||||||
}
|
|
@@ -1,41 +0,0 @@
|
|||||||
// Copyright (c) 2023 IBM Corp.
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package reader
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
|
|
||||||
R "github.com/IBM/fp-go/reader/generic"
|
|
||||||
)
|
|
||||||
|
|
||||||
// these functions curry a golang function with the context as the firsr parameter into a either reader with the context as the last parameter
|
|
||||||
// this goes back to the advice in https://pkg.go.dev/context to put the context as a first parameter as a convention
|
|
||||||
|
|
||||||
func From0[A any](f func(context.Context) A) func() Reader[A] {
|
|
||||||
return R.From0[Reader[A]](f)
|
|
||||||
}
|
|
||||||
|
|
||||||
func From1[T1, A any](f func(context.Context, T1) A) func(T1) Reader[A] {
|
|
||||||
return R.From1[Reader[A]](f)
|
|
||||||
}
|
|
||||||
|
|
||||||
func From2[T1, T2, A any](f func(context.Context, T1, T2) A) func(T1, T2) Reader[A] {
|
|
||||||
return R.From2[Reader[A]](f)
|
|
||||||
}
|
|
||||||
|
|
||||||
func From3[T1, T2, T3, A any](f func(context.Context, T1, T2, T3) A) func(T1, T2, T3) Reader[A] {
|
|
||||||
return R.From3[Reader[A]](f)
|
|
||||||
}
|
|
@@ -1,58 +0,0 @@
|
|||||||
// Copyright (c) 2023 IBM Corp.
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package reader
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
|
|
||||||
R "github.com/IBM/fp-go/reader/generic"
|
|
||||||
)
|
|
||||||
|
|
||||||
func MonadMap[A, B any](fa Reader[A], f func(A) B) Reader[B] {
|
|
||||||
return R.MonadMap[Reader[A], Reader[B]](fa, f)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Map[A, B any](f func(A) B) func(Reader[A]) Reader[B] {
|
|
||||||
return R.Map[Reader[A], Reader[B]](f)
|
|
||||||
}
|
|
||||||
|
|
||||||
func MonadChain[A, B any](ma Reader[A], f func(A) Reader[B]) Reader[B] {
|
|
||||||
return R.MonadChain(ma, f)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Chain[A, B any](f func(A) Reader[B]) func(Reader[A]) Reader[B] {
|
|
||||||
return R.Chain[Reader[A]](f)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Of[A any](a A) Reader[A] {
|
|
||||||
return R.Of[Reader[A]](a)
|
|
||||||
}
|
|
||||||
|
|
||||||
func MonadAp[A, B any](fab Reader[func(A) B], fa Reader[A]) Reader[B] {
|
|
||||||
return R.MonadAp[Reader[A], Reader[B]](fab, fa)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Ap[A, B any](fa Reader[A]) func(Reader[func(A) B]) Reader[B] {
|
|
||||||
return R.Ap[Reader[A], Reader[B], Reader[func(A) B]](fa)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Ask() Reader[context.Context] {
|
|
||||||
return R.Ask[Reader[context.Context]]()
|
|
||||||
}
|
|
||||||
|
|
||||||
func Asks[A any](r Reader[A]) Reader[A] {
|
|
||||||
return R.Asks(r)
|
|
||||||
}
|
|
@@ -1,75 +0,0 @@
|
|||||||
// Copyright (c) 2023 IBM Corp.
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package reader
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
F "github.com/IBM/fp-go/function"
|
|
||||||
T "github.com/IBM/fp-go/tuple"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func GoFunction(ctx context.Context, data string) string {
|
|
||||||
return strings.ToUpper(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
func GoIntFunction(ctx context.Context, data string, number int) string {
|
|
||||||
return fmt.Sprintf("%s: %d", data, number)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestReaderFrom(t *testing.T) {
|
|
||||||
ctx := context.Background()
|
|
||||||
f := From1(GoFunction)
|
|
||||||
|
|
||||||
result := f("input")(ctx)
|
|
||||||
|
|
||||||
assert.Equal(t, result, "INPUT")
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func MyFinalResult(left, right string) string {
|
|
||||||
return fmt.Sprintf("%s-%s", left, right)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestReadersFrom(t *testing.T) {
|
|
||||||
ctx := context.Background()
|
|
||||||
|
|
||||||
f1 := From1(GoFunction)
|
|
||||||
f2 := From2(GoIntFunction)
|
|
||||||
|
|
||||||
result1 := f1("input")(ctx)
|
|
||||||
result2 := f2("input", 10)(ctx)
|
|
||||||
|
|
||||||
result3 := MyFinalResult(result1, result2)
|
|
||||||
|
|
||||||
h := F.Pipe1(
|
|
||||||
SequenceT2(f1("input"), f2("input", 10)),
|
|
||||||
Map(T.Tupled2(MyFinalResult)),
|
|
||||||
)
|
|
||||||
|
|
||||||
composedResult := h(ctx)
|
|
||||||
|
|
||||||
assert.Equal(t, result1, "INPUT")
|
|
||||||
assert.Equal(t, result2, "input: 10")
|
|
||||||
assert.Equal(t, result3, "INPUT-input: 10")
|
|
||||||
|
|
||||||
assert.Equal(t, composedResult, "INPUT-input: 10")
|
|
||||||
|
|
||||||
}
|
|
@@ -1,57 +0,0 @@
|
|||||||
// Copyright (c) 2023 IBM Corp.
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package reader
|
|
||||||
|
|
||||||
import (
|
|
||||||
R "github.com/IBM/fp-go/reader/generic"
|
|
||||||
T "github.com/IBM/fp-go/tuple"
|
|
||||||
)
|
|
||||||
|
|
||||||
// SequenceT converts n inputs of higher kinded types into a higher kinded types of n strongly typed values, represented as a tuple
|
|
||||||
|
|
||||||
func SequenceT1[A any](a Reader[A]) Reader[T.Tuple1[A]] {
|
|
||||||
return R.SequenceT1[
|
|
||||||
Reader[A],
|
|
||||||
Reader[T.Tuple1[A]],
|
|
||||||
](a)
|
|
||||||
}
|
|
||||||
|
|
||||||
func SequenceT2[A, B any](a Reader[A], b Reader[B]) Reader[T.Tuple2[A, B]] {
|
|
||||||
return R.SequenceT2[
|
|
||||||
Reader[A],
|
|
||||||
Reader[B],
|
|
||||||
Reader[T.Tuple2[A, B]],
|
|
||||||
](a, b)
|
|
||||||
}
|
|
||||||
|
|
||||||
func SequenceT3[A, B, C any](a Reader[A], b Reader[B], c Reader[C]) Reader[T.Tuple3[A, B, C]] {
|
|
||||||
return R.SequenceT3[
|
|
||||||
Reader[A],
|
|
||||||
Reader[B],
|
|
||||||
Reader[C],
|
|
||||||
Reader[T.Tuple3[A, B, C]],
|
|
||||||
](a, b, c)
|
|
||||||
}
|
|
||||||
|
|
||||||
func SequenceT4[A, B, C, D any](a Reader[A], b Reader[B], c Reader[C], d Reader[D]) Reader[T.Tuple4[A, B, C, D]] {
|
|
||||||
return R.SequenceT4[
|
|
||||||
Reader[A],
|
|
||||||
Reader[B],
|
|
||||||
Reader[C],
|
|
||||||
Reader[D],
|
|
||||||
Reader[T.Tuple4[A, B, C, D]],
|
|
||||||
](a, b, c, d)
|
|
||||||
}
|
|
@@ -24,6 +24,11 @@ func TraverseArray[A, B any](f func(A) ReaderEither[B]) func([]A) ReaderEither[[
|
|||||||
return RE.TraverseArray[ReaderEither[B], ReaderEither[[]B], []A](f)
|
return RE.TraverseArray[ReaderEither[B], ReaderEither[[]B], []A](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TraverseArrayWithIndex transforms an array
|
||||||
|
func TraverseArrayWithIndex[A, B any](f func(int, A) ReaderEither[B]) func([]A) ReaderEither[[]B] {
|
||||||
|
return RE.TraverseArrayWithIndex[ReaderEither[B], ReaderEither[[]B], []A](f)
|
||||||
|
}
|
||||||
|
|
||||||
// SequenceArray converts a homogeneous sequence of either into an either of sequence
|
// SequenceArray converts a homogeneous sequence of either into an either of sequence
|
||||||
func SequenceArray[A any](ma []ReaderEither[A]) ReaderEither[[]A] {
|
func SequenceArray[A any](ma []ReaderEither[A]) ReaderEither[[]A] {
|
||||||
return RE.SequenceArray[ReaderEither[A], ReaderEither[[]A]](ma)
|
return RE.SequenceArray[ReaderEither[A], ReaderEither[[]A]](ma)
|
||||||
|
68
context/readereither/bind.go
Normal file
68
context/readereither/bind.go
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package readereither
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
G "github.com/IBM/fp-go/readereither/generic"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Bind creates an empty context of type [S] to be used with the [Bind] operation
|
||||||
|
func Do[S any](
|
||||||
|
empty S,
|
||||||
|
) ReaderEither[S] {
|
||||||
|
return G.Do[ReaderEither[S], context.Context, error, S](empty)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bind attaches the result of a computation to a context [S1] to produce a context [S2]
|
||||||
|
func Bind[S1, S2, T any](
|
||||||
|
setter func(T) func(S1) S2,
|
||||||
|
f func(S1) ReaderEither[T],
|
||||||
|
) func(ReaderEither[S1]) ReaderEither[S2] {
|
||||||
|
return G.Bind[ReaderEither[S1], ReaderEither[S2], ReaderEither[T], context.Context, error, S1, S2, T](setter, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Let attaches the result of a computation to a context [S1] to produce a context [S2]
|
||||||
|
func Let[S1, S2, T any](
|
||||||
|
setter func(T) func(S1) S2,
|
||||||
|
f func(S1) T,
|
||||||
|
) func(ReaderEither[S1]) ReaderEither[S2] {
|
||||||
|
return G.Let[ReaderEither[S1], ReaderEither[S2], context.Context, error, S1, S2, T](setter, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LetTo attaches the a value to a context [S1] to produce a context [S2]
|
||||||
|
func LetTo[S1, S2, T any](
|
||||||
|
setter func(T) func(S1) S2,
|
||||||
|
b T,
|
||||||
|
) func(ReaderEither[S1]) ReaderEither[S2] {
|
||||||
|
return G.LetTo[ReaderEither[S1], ReaderEither[S2], context.Context, error, S1, S2, T](setter, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BindTo initializes a new state [S1] from a value [T]
|
||||||
|
func BindTo[S1, T any](
|
||||||
|
setter func(T) S1,
|
||||||
|
) func(ReaderEither[T]) ReaderEither[S1] {
|
||||||
|
return G.BindTo[ReaderEither[S1], ReaderEither[T], context.Context, error, S1, T](setter)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApS attaches a value to a context [S1] to produce a context [S2] by considering the context and the value concurrently
|
||||||
|
func ApS[S1, S2, T any](
|
||||||
|
setter func(T) func(S1) S2,
|
||||||
|
fa ReaderEither[T],
|
||||||
|
) func(ReaderEither[S1]) ReaderEither[S2] {
|
||||||
|
return G.ApS[ReaderEither[S1], ReaderEither[S2], ReaderEither[T], context.Context, error, S1, S2, T](setter, fa)
|
||||||
|
}
|
58
context/readereither/bind_test.go
Normal file
58
context/readereither/bind_test.go
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package readereither
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
E "github.com/IBM/fp-go/either"
|
||||||
|
F "github.com/IBM/fp-go/function"
|
||||||
|
"github.com/IBM/fp-go/internal/utils"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getLastName(s utils.Initial) ReaderEither[string] {
|
||||||
|
return Of("Doe")
|
||||||
|
}
|
||||||
|
|
||||||
|
func getGivenName(s utils.WithLastName) ReaderEither[string] {
|
||||||
|
return Of("John")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBind(t *testing.T) {
|
||||||
|
|
||||||
|
res := F.Pipe3(
|
||||||
|
Do(utils.Empty),
|
||||||
|
Bind(utils.SetLastName, getLastName),
|
||||||
|
Bind(utils.SetGivenName, getGivenName),
|
||||||
|
Map(utils.GetFullName),
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.Equal(t, res(context.Background()), E.Of[error]("John Doe"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestApS(t *testing.T) {
|
||||||
|
|
||||||
|
res := F.Pipe3(
|
||||||
|
Do(utils.Empty),
|
||||||
|
ApS(utils.SetLastName, Of("Doe")),
|
||||||
|
ApS(utils.SetGivenName, Of("John")),
|
||||||
|
Map(utils.GetFullName),
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.Equal(t, res(context.Background()), E.Of[error]("John Doe"))
|
||||||
|
}
|
@@ -34,8 +34,6 @@ var (
|
|||||||
|
|
||||||
func command(name string, args []string, in []byte) RE.ReaderEither[exec.CommandOutput] {
|
func command(name string, args []string, in []byte) RE.ReaderEither[exec.CommandOutput] {
|
||||||
return func(ctx context.Context) E.Either[error, exec.CommandOutput] {
|
return func(ctx context.Context) E.Either[error, exec.CommandOutput] {
|
||||||
return E.TryCatchError(func() (exec.CommandOutput, error) {
|
return E.TryCatchError(GE.Exec(ctx, name, args, in))
|
||||||
return GE.Exec(ctx, name, args, in)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -18,7 +18,6 @@ package readereither
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
R "github.com/IBM/fp-go/context/reader"
|
|
||||||
ET "github.com/IBM/fp-go/either"
|
ET "github.com/IBM/fp-go/either"
|
||||||
O "github.com/IBM/fp-go/option"
|
O "github.com/IBM/fp-go/option"
|
||||||
RE "github.com/IBM/fp-go/readereither/generic"
|
RE "github.com/IBM/fp-go/readereither/generic"
|
||||||
@@ -32,14 +31,6 @@ func FromEither[A any](e ET.Either[error, A]) ReaderEither[A] {
|
|||||||
return RE.FromEither[ReaderEither[A]](e)
|
return RE.FromEither[ReaderEither[A]](e)
|
||||||
}
|
}
|
||||||
|
|
||||||
func RightReader[A any](r R.Reader[A]) ReaderEither[A] {
|
|
||||||
return RE.RightReader[R.Reader[A], ReaderEither[A]](r)
|
|
||||||
}
|
|
||||||
|
|
||||||
func LeftReader[A any](l R.Reader[error]) ReaderEither[A] {
|
|
||||||
return RE.LeftReader[R.Reader[error], ReaderEither[A]](l)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Left[A any](l error) ReaderEither[A] {
|
func Left[A any](l error) ReaderEither[A] {
|
||||||
return RE.Left[ReaderEither[A]](l)
|
return RE.Left[ReaderEither[A]](l)
|
||||||
}
|
}
|
||||||
@@ -48,10 +39,6 @@ func Right[A any](r A) ReaderEither[A] {
|
|||||||
return RE.Right[ReaderEither[A]](r)
|
return RE.Right[ReaderEither[A]](r)
|
||||||
}
|
}
|
||||||
|
|
||||||
func FromReader[A any](r R.Reader[A]) ReaderEither[A] {
|
|
||||||
return RE.FromReader[R.Reader[A], ReaderEither[A]](r)
|
|
||||||
}
|
|
||||||
|
|
||||||
func MonadMap[A, B any](fa ReaderEither[A], f func(A) B) ReaderEither[B] {
|
func MonadMap[A, B any](fa ReaderEither[A], f func(A) B) ReaderEither[B] {
|
||||||
return RE.MonadMap[ReaderEither[A], ReaderEither[B]](fa, f)
|
return RE.MonadMap[ReaderEither[A], ReaderEither[B]](fa, f)
|
||||||
}
|
}
|
||||||
@@ -84,30 +71,14 @@ func FromPredicate[A any](pred func(A) bool, onFalse func(A) error) func(A) Read
|
|||||||
return RE.FromPredicate[ReaderEither[A]](pred, onFalse)
|
return RE.FromPredicate[ReaderEither[A]](pred, onFalse)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Fold[A, B any](onLeft func(error) R.Reader[B], onRight func(A) R.Reader[B]) func(ReaderEither[A]) R.Reader[B] {
|
|
||||||
return RE.Fold[ReaderEither[A]](onLeft, onRight)
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetOrElse[A any](onLeft func(error) R.Reader[A]) func(ReaderEither[A]) R.Reader[A] {
|
|
||||||
return RE.GetOrElse[ReaderEither[A]](onLeft)
|
|
||||||
}
|
|
||||||
|
|
||||||
func OrElse[A any](onLeft func(error) ReaderEither[A]) func(ReaderEither[A]) ReaderEither[A] {
|
func OrElse[A any](onLeft func(error) ReaderEither[A]) func(ReaderEither[A]) ReaderEither[A] {
|
||||||
return RE.OrElse[ReaderEither[A]](onLeft)
|
return RE.OrElse[ReaderEither[A]](onLeft)
|
||||||
}
|
}
|
||||||
|
|
||||||
func OrLeft[A any](onLeft func(error) R.Reader[error]) func(ReaderEither[A]) ReaderEither[A] {
|
|
||||||
return RE.OrLeft[ReaderEither[A], ReaderEither[A]](onLeft)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Ask() ReaderEither[context.Context] {
|
func Ask() ReaderEither[context.Context] {
|
||||||
return RE.Ask[ReaderEither[context.Context]]()
|
return RE.Ask[ReaderEither[context.Context]]()
|
||||||
}
|
}
|
||||||
|
|
||||||
func Asks[A any](r R.Reader[A]) ReaderEither[A] {
|
|
||||||
return RE.Asks[R.Reader[A], ReaderEither[A]](r)
|
|
||||||
}
|
|
||||||
|
|
||||||
func MonadChainEitherK[A, B any](ma ReaderEither[A], f func(A) ET.Either[error, B]) ReaderEither[B] {
|
func MonadChainEitherK[A, B any](ma ReaderEither[A], f func(A) ET.Either[error, B]) ReaderEither[B] {
|
||||||
return RE.MonadChainEitherK[ReaderEither[A], ReaderEither[B]](ma, f)
|
return RE.MonadChainEitherK[ReaderEither[A], ReaderEither[B]](ma, f)
|
||||||
}
|
}
|
||||||
@@ -119,3 +90,11 @@ func ChainEitherK[A, B any](f func(A) ET.Either[error, B]) func(ma ReaderEither[
|
|||||||
func ChainOptionK[A, B any](onNone func() error) func(func(A) O.Option[B]) func(ReaderEither[A]) ReaderEither[B] {
|
func ChainOptionK[A, B any](onNone func() error) func(func(A) O.Option[B]) func(ReaderEither[A]) ReaderEither[B] {
|
||||||
return RE.ChainOptionK[ReaderEither[A], ReaderEither[B]](onNone)
|
return RE.ChainOptionK[ReaderEither[A], ReaderEither[B]](onNone)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func MonadFlap[B, A any](fab ReaderEither[func(A) B], a A) ReaderEither[B] {
|
||||||
|
return RE.MonadFlap[ReaderEither[func(A) B], ReaderEither[B]](fab, a)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Flap[B, A any](a A) func(ReaderEither[func(A) B]) ReaderEither[B] {
|
||||||
|
return RE.Flap[ReaderEither[func(A) B], ReaderEither[B]](a)
|
||||||
|
}
|
||||||
|
@@ -1,42 +0,0 @@
|
|||||||
// Copyright (c) 2023 IBM Corp.
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package readerio
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
|
|
||||||
IO "github.com/IBM/fp-go/io"
|
|
||||||
R "github.com/IBM/fp-go/readerio/generic"
|
|
||||||
)
|
|
||||||
|
|
||||||
// these functions curry a golang function with the context as the firsr parameter into a either reader with the context as the last parameter
|
|
||||||
// this goes back to the advice in https://pkg.go.dev/context to put the context as a first parameter as a convention
|
|
||||||
|
|
||||||
func From0[A any](f func(context.Context) IO.IO[A]) func() ReaderIO[A] {
|
|
||||||
return R.From0[ReaderIO[A]](f)
|
|
||||||
}
|
|
||||||
|
|
||||||
func From1[T1, A any](f func(context.Context, T1) IO.IO[A]) func(T1) ReaderIO[A] {
|
|
||||||
return R.From1[ReaderIO[A]](f)
|
|
||||||
}
|
|
||||||
|
|
||||||
func From2[T1, T2, A any](f func(context.Context, T1, T2) IO.IO[A]) func(T1, T2) ReaderIO[A] {
|
|
||||||
return R.From2[ReaderIO[A]](f)
|
|
||||||
}
|
|
||||||
|
|
||||||
func From3[T1, T2, T3, A any](f func(context.Context, T1, T2, T3) IO.IO[A]) func(T1, T2, T3) ReaderIO[A] {
|
|
||||||
return R.From3[ReaderIO[A]](f)
|
|
||||||
}
|
|
@@ -1,59 +0,0 @@
|
|||||||
// Copyright (c) 2023 IBM Corp.
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package readerio
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
|
|
||||||
R "github.com/IBM/fp-go/readerio/generic"
|
|
||||||
)
|
|
||||||
|
|
||||||
func MonadMap[A, B any](fa ReaderIO[A], f func(A) B) ReaderIO[B] {
|
|
||||||
return R.MonadMap[ReaderIO[A], ReaderIO[B]](fa, f)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Map[A, B any](f func(A) B) func(ReaderIO[A]) ReaderIO[B] {
|
|
||||||
return R.Map[ReaderIO[A], ReaderIO[B]](f)
|
|
||||||
}
|
|
||||||
|
|
||||||
func MonadChain[A, B any](ma ReaderIO[A], f func(A) ReaderIO[B]) ReaderIO[B] {
|
|
||||||
return R.MonadChain(ma, f)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Chain[A, B any](f func(A) ReaderIO[B]) func(ReaderIO[A]) ReaderIO[B] {
|
|
||||||
return R.Chain[ReaderIO[A]](f)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Of[A any](a A) ReaderIO[A] {
|
|
||||||
return R.Of[ReaderIO[A]](a)
|
|
||||||
}
|
|
||||||
|
|
||||||
func MonadAp[A, B any](fab ReaderIO[func(A) B], fa ReaderIO[A]) ReaderIO[B] {
|
|
||||||
return R.MonadAp[ReaderIO[A], ReaderIO[B]](fab, fa)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Ap[A, B any](fa ReaderIO[A]) func(ReaderIO[func(A) B]) ReaderIO[B] {
|
|
||||||
return R.Ap[ReaderIO[A], ReaderIO[B], ReaderIO[func(A) B]](fa)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Ask() ReaderIO[context.Context] {
|
|
||||||
return R.Ask[ReaderIO[context.Context]]()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Defer creates an IO by creating a brand new IO via a generator function, each time
|
|
||||||
func Defer[A any](gen func() ReaderIO[A]) ReaderIO[A] {
|
|
||||||
return R.Defer[ReaderIO[A]](gen)
|
|
||||||
}
|
|
@@ -1,80 +0,0 @@
|
|||||||
// Copyright (c) 2023 IBM Corp.
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package readerio
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
F "github.com/IBM/fp-go/function"
|
|
||||||
IO "github.com/IBM/fp-go/io"
|
|
||||||
T "github.com/IBM/fp-go/tuple"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func GoFunction(ctx context.Context, data string) IO.IO[string] {
|
|
||||||
return func() string {
|
|
||||||
return strings.ToUpper(data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func GoIntFunction(ctx context.Context, data string, number int) IO.IO[string] {
|
|
||||||
return func() string {
|
|
||||||
return fmt.Sprintf("%s: %d", data, number)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestReaderFrom(t *testing.T) {
|
|
||||||
ctx := context.Background()
|
|
||||||
f := From1(GoFunction)
|
|
||||||
|
|
||||||
result := f("input")(ctx)
|
|
||||||
|
|
||||||
assert.Equal(t, result(), "INPUT")
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func MyFinalResult(left, right string) string {
|
|
||||||
return fmt.Sprintf("%s-%s", left, right)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestReadersFrom(t *testing.T) {
|
|
||||||
ctx := context.Background()
|
|
||||||
|
|
||||||
f1 := From1(GoFunction)
|
|
||||||
f2 := From2(GoIntFunction)
|
|
||||||
|
|
||||||
result1 := f1("input")(ctx)
|
|
||||||
result2 := f2("input", 10)(ctx)
|
|
||||||
|
|
||||||
result3 := MyFinalResult(result1(), result2())
|
|
||||||
|
|
||||||
h := F.Pipe1(
|
|
||||||
SequenceT2(f1("input"), f2("input", 10)),
|
|
||||||
Map(T.Tupled2(MyFinalResult)),
|
|
||||||
)
|
|
||||||
|
|
||||||
composedResult := h(ctx)
|
|
||||||
|
|
||||||
assert.Equal(t, result1(), "INPUT")
|
|
||||||
assert.Equal(t, result2(), "input: 10")
|
|
||||||
assert.Equal(t, result3, "INPUT-input: 10")
|
|
||||||
|
|
||||||
assert.Equal(t, composedResult(), "INPUT-input: 10")
|
|
||||||
|
|
||||||
}
|
|
@@ -1,57 +0,0 @@
|
|||||||
// Copyright (c) 2023 IBM Corp.
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package readerio
|
|
||||||
|
|
||||||
import (
|
|
||||||
R "github.com/IBM/fp-go/readerio/generic"
|
|
||||||
T "github.com/IBM/fp-go/tuple"
|
|
||||||
)
|
|
||||||
|
|
||||||
// SequenceT converts n inputs of higher kinded types into a higher kinded types of n strongly typed values, represented as a tuple
|
|
||||||
|
|
||||||
func SequenceT1[A any](a ReaderIO[A]) ReaderIO[T.Tuple1[A]] {
|
|
||||||
return R.SequenceT1[
|
|
||||||
ReaderIO[A],
|
|
||||||
ReaderIO[T.Tuple1[A]],
|
|
||||||
](a)
|
|
||||||
}
|
|
||||||
|
|
||||||
func SequenceT2[A, B any](a ReaderIO[A], b ReaderIO[B]) ReaderIO[T.Tuple2[A, B]] {
|
|
||||||
return R.SequenceT2[
|
|
||||||
ReaderIO[A],
|
|
||||||
ReaderIO[B],
|
|
||||||
ReaderIO[T.Tuple2[A, B]],
|
|
||||||
](a, b)
|
|
||||||
}
|
|
||||||
|
|
||||||
func SequenceT3[A, B, C any](a ReaderIO[A], b ReaderIO[B], c ReaderIO[C]) ReaderIO[T.Tuple3[A, B, C]] {
|
|
||||||
return R.SequenceT3[
|
|
||||||
ReaderIO[A],
|
|
||||||
ReaderIO[B],
|
|
||||||
ReaderIO[C],
|
|
||||||
ReaderIO[T.Tuple3[A, B, C]],
|
|
||||||
](a, b, c)
|
|
||||||
}
|
|
||||||
|
|
||||||
func SequenceT4[A, B, C, D any](a ReaderIO[A], b ReaderIO[B], c ReaderIO[C], d ReaderIO[D]) ReaderIO[T.Tuple4[A, B, C, D]] {
|
|
||||||
return R.SequenceT4[
|
|
||||||
ReaderIO[A],
|
|
||||||
ReaderIO[B],
|
|
||||||
ReaderIO[C],
|
|
||||||
ReaderIO[D],
|
|
||||||
ReaderIO[T.Tuple4[A, B, C, D]],
|
|
||||||
](a, b, c, d)
|
|
||||||
}
|
|
67
context/readerioeither/bind.go
Normal file
67
context/readerioeither/bind.go
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package readerioeither
|
||||||
|
|
||||||
|
import (
|
||||||
|
G "github.com/IBM/fp-go/context/readerioeither/generic"
|
||||||
|
IOE "github.com/IBM/fp-go/ioeither"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Bind creates an empty context of type [S] to be used with the [Bind] operation
|
||||||
|
func Do[S any](
|
||||||
|
empty S,
|
||||||
|
) ReaderIOEither[S] {
|
||||||
|
return G.Do[ReaderIOEither[S], IOE.IOEither[error, S], S](empty)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bind attaches the result of a computation to a context [S1] to produce a context [S2]
|
||||||
|
func Bind[S1, S2, T any](
|
||||||
|
setter func(T) func(S1) S2,
|
||||||
|
f func(S1) ReaderIOEither[T],
|
||||||
|
) func(ReaderIOEither[S1]) ReaderIOEither[S2] {
|
||||||
|
return G.Bind[ReaderIOEither[S1], ReaderIOEither[S2], ReaderIOEither[T], IOE.IOEither[error, S1], IOE.IOEither[error, S2], IOE.IOEither[error, T], S1, S2, T](setter, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Let attaches the result of a computation to a context [S1] to produce a context [S2]
|
||||||
|
func Let[S1, S2, T any](
|
||||||
|
setter func(T) func(S1) S2,
|
||||||
|
f func(S1) T,
|
||||||
|
) func(ReaderIOEither[S1]) ReaderIOEither[S2] {
|
||||||
|
return G.Let[ReaderIOEither[S1], ReaderIOEither[S2], IOE.IOEither[error, S1], IOE.IOEither[error, S2], S1, S2, T](setter, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LetTo attaches the a value to a context [S1] to produce a context [S2]
|
||||||
|
func LetTo[S1, S2, T any](
|
||||||
|
setter func(T) func(S1) S2,
|
||||||
|
b T,
|
||||||
|
) func(ReaderIOEither[S1]) ReaderIOEither[S2] {
|
||||||
|
return G.LetTo[ReaderIOEither[S1], ReaderIOEither[S2], IOE.IOEither[error, S1], IOE.IOEither[error, S2], S1, S2, T](setter, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BindTo initializes a new state [S1] from a value [T]
|
||||||
|
func BindTo[S1, T any](
|
||||||
|
setter func(T) S1,
|
||||||
|
) func(ReaderIOEither[T]) ReaderIOEither[S1] {
|
||||||
|
return G.BindTo[ReaderIOEither[S1], ReaderIOEither[T], IOE.IOEither[error, S1], IOE.IOEither[error, T], S1, T](setter)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApS attaches a value to a context [S1] to produce a context [S2] by considering the context and the value concurrently
|
||||||
|
func ApS[S1, S2, T any](
|
||||||
|
setter func(T) func(S1) S2,
|
||||||
|
fa ReaderIOEither[T],
|
||||||
|
) func(ReaderIOEither[S1]) ReaderIOEither[S2] {
|
||||||
|
return G.ApS[ReaderIOEither[func(T) S2], ReaderIOEither[S1], ReaderIOEither[S2], ReaderIOEither[T], IOE.IOEither[error, func(T) S2], IOE.IOEither[error, S1], IOE.IOEither[error, S2], IOE.IOEither[error, T], S1, S2, T](setter, fa)
|
||||||
|
}
|
58
context/readerioeither/bind_test.go
Normal file
58
context/readerioeither/bind_test.go
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package readerioeither
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
E "github.com/IBM/fp-go/either"
|
||||||
|
F "github.com/IBM/fp-go/function"
|
||||||
|
"github.com/IBM/fp-go/internal/utils"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getLastName(s utils.Initial) ReaderIOEither[string] {
|
||||||
|
return Of("Doe")
|
||||||
|
}
|
||||||
|
|
||||||
|
func getGivenName(s utils.WithLastName) ReaderIOEither[string] {
|
||||||
|
return Of("John")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBind(t *testing.T) {
|
||||||
|
|
||||||
|
res := F.Pipe3(
|
||||||
|
Do(utils.Empty),
|
||||||
|
Bind(utils.SetLastName, getLastName),
|
||||||
|
Bind(utils.SetGivenName, getGivenName),
|
||||||
|
Map(utils.GetFullName),
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.Equal(t, res(context.Background())(), E.Of[error]("John Doe"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestApS(t *testing.T) {
|
||||||
|
|
||||||
|
res := F.Pipe3(
|
||||||
|
Do(utils.Empty),
|
||||||
|
ApS(utils.SetLastName, Of("Doe")),
|
||||||
|
ApS(utils.SetGivenName, Of("John")),
|
||||||
|
Map(utils.GetFullName),
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.Equal(t, res(context.Background())(), E.Of[error]("John Doe"))
|
||||||
|
}
|
37
context/readerioeither/bracket.go
Normal file
37
context/readerioeither/bracket.go
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package readerioeither
|
||||||
|
|
||||||
|
import (
|
||||||
|
G "github.com/IBM/fp-go/context/readerioeither/generic"
|
||||||
|
E "github.com/IBM/fp-go/either"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Bracket makes sure that a resource is cleaned up in the event of an error. The release action is called regardless of
|
||||||
|
// whether the body action returns and error or not.
|
||||||
|
func Bracket[
|
||||||
|
A, B, ANY any](
|
||||||
|
|
||||||
|
acquire ReaderIOEither[A],
|
||||||
|
use func(A) ReaderIOEither[B],
|
||||||
|
release func(A, E.Either[error, B]) ReaderIOEither[ANY],
|
||||||
|
) ReaderIOEither[B] {
|
||||||
|
return G.Bracket[ReaderIOEither[A], ReaderIOEither[B], ReaderIOEither[ANY]](
|
||||||
|
acquire,
|
||||||
|
use,
|
||||||
|
release,
|
||||||
|
)
|
||||||
|
}
|
@@ -25,25 +25,31 @@ import (
|
|||||||
F "github.com/IBM/fp-go/function"
|
F "github.com/IBM/fp-go/function"
|
||||||
"github.com/IBM/fp-go/internal/file"
|
"github.com/IBM/fp-go/internal/file"
|
||||||
IOE "github.com/IBM/fp-go/ioeither"
|
IOE "github.com/IBM/fp-go/ioeither"
|
||||||
|
IOEF "github.com/IBM/fp-go/ioeither/file"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
openIOE = IOE.Eitherize1(os.Open)
|
|
||||||
// Open opens a file for reading within the given context
|
// Open opens a file for reading within the given context
|
||||||
Open = F.Flow3(
|
Open = F.Flow3(
|
||||||
openIOE,
|
IOEF.Open,
|
||||||
RIOE.FromIOEither[*os.File],
|
RIOE.FromIOEither[*os.File],
|
||||||
RIOE.WithContext[*os.File],
|
RIOE.WithContext[*os.File],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Remove removes a file by name
|
||||||
|
Remove = F.Flow2(
|
||||||
|
IOEF.Remove,
|
||||||
|
RIOE.FromIOEither[string],
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
// Close closes an object
|
// Close closes an object
|
||||||
func Close[C io.Closer](c C) RIOE.ReaderIOEither[any] {
|
func Close[C io.Closer](c C) RIOE.ReaderIOEither[any] {
|
||||||
return RIOE.FromIOEither(func() ET.Either[error, any] {
|
return F.Pipe2(
|
||||||
return ET.TryCatchError(func() (any, error) {
|
c,
|
||||||
return c, c.Close()
|
IOEF.Close[C],
|
||||||
})
|
RIOE.FromIOEither[any],
|
||||||
})
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadFile reads a file in the scope of a context
|
// ReadFile reads a file in the scope of a context
|
||||||
|
@@ -19,9 +19,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
RIO "github.com/IBM/fp-go/context/readerio"
|
|
||||||
R "github.com/IBM/fp-go/context/readerioeither"
|
R "github.com/IBM/fp-go/context/readerioeither"
|
||||||
"github.com/IBM/fp-go/errors"
|
|
||||||
F "github.com/IBM/fp-go/function"
|
F "github.com/IBM/fp-go/function"
|
||||||
IO "github.com/IBM/fp-go/io"
|
IO "github.com/IBM/fp-go/io"
|
||||||
J "github.com/IBM/fp-go/json"
|
J "github.com/IBM/fp-go/json"
|
||||||
@@ -37,20 +35,17 @@ func getData(r RecordType) string {
|
|||||||
|
|
||||||
func ExampleReadFile() {
|
func ExampleReadFile() {
|
||||||
|
|
||||||
data := F.Pipe4(
|
data := F.Pipe3(
|
||||||
ReadFile("./data/file.json"),
|
ReadFile("./data/file.json"),
|
||||||
R.ChainEitherK(J.Unmarshal[RecordType]),
|
R.ChainEitherK(J.Unmarshal[RecordType]),
|
||||||
R.ChainFirstIOK(IO.Logf[RecordType]("Log: %v")),
|
R.ChainFirstIOK(IO.Logf[RecordType]("Log: %v")),
|
||||||
R.Map(getData),
|
R.Map(getData),
|
||||||
R.GetOrElse(F.Flow2(
|
|
||||||
errors.ToString,
|
|
||||||
RIO.Of[string],
|
|
||||||
)),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
result := data(context.Background())
|
result := data(context.Background())
|
||||||
|
|
||||||
fmt.Println(result())
|
fmt.Println(result())
|
||||||
|
|
||||||
// Output: Carsten
|
// Output:
|
||||||
|
// Right[string](Carsten)
|
||||||
}
|
}
|
||||||
|
52
context/readerioeither/file/tempfile.go
Normal file
52
context/readerioeither/file/tempfile.go
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package file
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
RIOE "github.com/IBM/fp-go/context/readerioeither"
|
||||||
|
F "github.com/IBM/fp-go/function"
|
||||||
|
IO "github.com/IBM/fp-go/io"
|
||||||
|
IOF "github.com/IBM/fp-go/io/file"
|
||||||
|
IOEF "github.com/IBM/fp-go/ioeither/file"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// onCreateTempFile creates a temp file with sensible defaults
|
||||||
|
onCreateTempFile = CreateTemp("", "*")
|
||||||
|
// destroy handler
|
||||||
|
onReleaseTempFile = F.Flow4(
|
||||||
|
IOF.Close[*os.File],
|
||||||
|
IO.Map((*os.File).Name),
|
||||||
|
RIOE.FromIO[string],
|
||||||
|
RIOE.Chain(Remove),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
// CreateTemp created a temp file with proper parametrization
|
||||||
|
func CreateTemp(dir, pattern string) RIOE.ReaderIOEither[*os.File] {
|
||||||
|
return F.Pipe2(
|
||||||
|
IOEF.CreateTemp(dir, pattern),
|
||||||
|
RIOE.FromIOEither[*os.File],
|
||||||
|
RIOE.WithContext[*os.File],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithTempFile creates a temporary file, then invokes a callback to create a resource based on the file, then close and remove the temp file
|
||||||
|
func WithTempFile[A any](f func(*os.File) RIOE.ReaderIOEither[A]) RIOE.ReaderIOEither[A] {
|
||||||
|
return RIOE.WithResource[A](onCreateTempFile, onReleaseTempFile)(f)
|
||||||
|
}
|
47
context/readerioeither/file/tempfile_test.go
Normal file
47
context/readerioeither/file/tempfile_test.go
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package file
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
RIOE "github.com/IBM/fp-go/context/readerioeither"
|
||||||
|
E "github.com/IBM/fp-go/either"
|
||||||
|
F "github.com/IBM/fp-go/function"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestWithTempFile(t *testing.T) {
|
||||||
|
|
||||||
|
res := WithTempFile(onWriteAll[*os.File]([]byte("Carsten")))
|
||||||
|
|
||||||
|
assert.Equal(t, E.Of[error]([]byte("Carsten")), res(context.Background())())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWithTempFileOnClosedFile(t *testing.T) {
|
||||||
|
|
||||||
|
res := WithTempFile(func(f *os.File) RIOE.ReaderIOEither[[]byte] {
|
||||||
|
return F.Pipe2(
|
||||||
|
f,
|
||||||
|
onWriteAll[*os.File]([]byte("Carsten")),
|
||||||
|
RIOE.ChainFirst(F.Constant1[[]byte](Close(f))),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.Equal(t, E.Of[error]([]byte("Carsten")), res(context.Background())())
|
||||||
|
}
|
57
context/readerioeither/file/write.go
Normal file
57
context/readerioeither/file/write.go
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package file
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
RIOE "github.com/IBM/fp-go/context/readerioeither"
|
||||||
|
F "github.com/IBM/fp-go/function"
|
||||||
|
)
|
||||||
|
|
||||||
|
func onWriteAll[W io.Writer](data []byte) func(w W) RIOE.ReaderIOEither[[]byte] {
|
||||||
|
return func(w W) RIOE.ReaderIOEither[[]byte] {
|
||||||
|
return F.Pipe1(
|
||||||
|
RIOE.TryCatch(func(_ context.Context) func() ([]byte, error) {
|
||||||
|
return func() ([]byte, error) {
|
||||||
|
_, err := w.Write(data)
|
||||||
|
return data, err
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
RIOE.WithContext[[]byte],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteAll uses a generator function to create a stream, writes data to it and closes it
|
||||||
|
func WriteAll[W io.WriteCloser](data []byte) func(acquire RIOE.ReaderIOEither[W]) RIOE.ReaderIOEither[[]byte] {
|
||||||
|
onWrite := onWriteAll[W](data)
|
||||||
|
return func(onCreate RIOE.ReaderIOEither[W]) RIOE.ReaderIOEither[[]byte] {
|
||||||
|
return RIOE.WithResource[[]byte](
|
||||||
|
onCreate,
|
||||||
|
Close[W])(
|
||||||
|
onWrite,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write uses a generator function to create a stream, writes data to it and closes it
|
||||||
|
func Write[R any, W io.WriteCloser](acquire RIOE.ReaderIOEither[W]) func(use func(W) RIOE.ReaderIOEither[R]) RIOE.ReaderIOEither[R] {
|
||||||
|
return RIOE.WithResource[R](
|
||||||
|
acquire,
|
||||||
|
Close[W])
|
||||||
|
}
|
@@ -2,7 +2,7 @@ package readerioeither
|
|||||||
|
|
||||||
// Code generated by go generate; DO NOT EDIT.
|
// Code generated by go generate; DO NOT EDIT.
|
||||||
// This file was generated by robots at
|
// This file was generated by robots at
|
||||||
// 2023-08-17 22:58:56.457404 +0200 CEST m=+0.024265101
|
// 2024-05-24 22:24:01.4250895 +0200 CEST m=+0.014515801
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -17,12 +17,24 @@ func Eitherize0[F ~func(context.Context) (R, error), R any](f F) func() ReaderIO
|
|||||||
return G.Eitherize0[ReaderIOEither[R]](f)
|
return G.Eitherize0[ReaderIOEither[R]](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Uneitherize0 converts a function with 1 parameters returning a [ReaderIOEither[R]] into a function with 0 parameters returning a tuple.
|
||||||
|
// The first parameter is considered to be the [context.Context].
|
||||||
|
func Uneitherize0[F ~func() ReaderIOEither[R], R any](f F) func(context.Context) (R, error) {
|
||||||
|
return G.Uneitherize0[ReaderIOEither[R], func(context.Context) (R, error)](f)
|
||||||
|
}
|
||||||
|
|
||||||
// Eitherize1 converts a function with 1 parameters returning a tuple into a function with 1 parameters returning a [ReaderIOEither[R]]
|
// Eitherize1 converts a function with 1 parameters returning a tuple into a function with 1 parameters returning a [ReaderIOEither[R]]
|
||||||
// The inverse function is [Uneitherize1]
|
// The inverse function is [Uneitherize1]
|
||||||
func Eitherize1[F ~func(context.Context, T0) (R, error), T0, R any](f F) func(T0) ReaderIOEither[R] {
|
func Eitherize1[F ~func(context.Context, T0) (R, error), T0, R any](f F) func(T0) ReaderIOEither[R] {
|
||||||
return G.Eitherize1[ReaderIOEither[R]](f)
|
return G.Eitherize1[ReaderIOEither[R]](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Uneitherize1 converts a function with 2 parameters returning a [ReaderIOEither[R]] into a function with 1 parameters returning a tuple.
|
||||||
|
// The first parameter is considered to be the [context.Context].
|
||||||
|
func Uneitherize1[F ~func(T0) ReaderIOEither[R], T0, R any](f F) func(context.Context, T0) (R, error) {
|
||||||
|
return G.Uneitherize1[ReaderIOEither[R], func(context.Context, T0) (R, error)](f)
|
||||||
|
}
|
||||||
|
|
||||||
// SequenceT1 converts 1 [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple1].
|
// SequenceT1 converts 1 [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple1].
|
||||||
func SequenceT1[T1 any](t1 ReaderIOEither[T1]) ReaderIOEither[T.Tuple1[T1]] {
|
func SequenceT1[T1 any](t1 ReaderIOEither[T1]) ReaderIOEither[T.Tuple1[T1]] {
|
||||||
return G.SequenceT1[ReaderIOEither[T.Tuple1[T1]]](t1)
|
return G.SequenceT1[ReaderIOEither[T.Tuple1[T1]]](t1)
|
||||||
@@ -74,6 +86,12 @@ func Eitherize2[F ~func(context.Context, T0, T1) (R, error), T0, T1, R any](f F)
|
|||||||
return G.Eitherize2[ReaderIOEither[R]](f)
|
return G.Eitherize2[ReaderIOEither[R]](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Uneitherize2 converts a function with 3 parameters returning a [ReaderIOEither[R]] into a function with 2 parameters returning a tuple.
|
||||||
|
// The first parameter is considered to be the [context.Context].
|
||||||
|
func Uneitherize2[F ~func(T0, T1) ReaderIOEither[R], T0, T1, R any](f F) func(context.Context, T0, T1) (R, error) {
|
||||||
|
return G.Uneitherize2[ReaderIOEither[R], func(context.Context, T0, T1) (R, error)](f)
|
||||||
|
}
|
||||||
|
|
||||||
// SequenceT2 converts 2 [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple2].
|
// SequenceT2 converts 2 [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple2].
|
||||||
func SequenceT2[T1, T2 any](t1 ReaderIOEither[T1], t2 ReaderIOEither[T2]) ReaderIOEither[T.Tuple2[T1, T2]] {
|
func SequenceT2[T1, T2 any](t1 ReaderIOEither[T1], t2 ReaderIOEither[T2]) ReaderIOEither[T.Tuple2[T1, T2]] {
|
||||||
return G.SequenceT2[ReaderIOEither[T.Tuple2[T1, T2]]](t1, t2)
|
return G.SequenceT2[ReaderIOEither[T.Tuple2[T1, T2]]](t1, t2)
|
||||||
@@ -125,6 +143,12 @@ func Eitherize3[F ~func(context.Context, T0, T1, T2) (R, error), T0, T1, T2, R a
|
|||||||
return G.Eitherize3[ReaderIOEither[R]](f)
|
return G.Eitherize3[ReaderIOEither[R]](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Uneitherize3 converts a function with 4 parameters returning a [ReaderIOEither[R]] into a function with 3 parameters returning a tuple.
|
||||||
|
// The first parameter is considered to be the [context.Context].
|
||||||
|
func Uneitherize3[F ~func(T0, T1, T2) ReaderIOEither[R], T0, T1, T2, R any](f F) func(context.Context, T0, T1, T2) (R, error) {
|
||||||
|
return G.Uneitherize3[ReaderIOEither[R], func(context.Context, T0, T1, T2) (R, error)](f)
|
||||||
|
}
|
||||||
|
|
||||||
// SequenceT3 converts 3 [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple3].
|
// SequenceT3 converts 3 [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple3].
|
||||||
func SequenceT3[T1, T2, T3 any](t1 ReaderIOEither[T1], t2 ReaderIOEither[T2], t3 ReaderIOEither[T3]) ReaderIOEither[T.Tuple3[T1, T2, T3]] {
|
func SequenceT3[T1, T2, T3 any](t1 ReaderIOEither[T1], t2 ReaderIOEither[T2], t3 ReaderIOEither[T3]) ReaderIOEither[T.Tuple3[T1, T2, T3]] {
|
||||||
return G.SequenceT3[ReaderIOEither[T.Tuple3[T1, T2, T3]]](t1, t2, t3)
|
return G.SequenceT3[ReaderIOEither[T.Tuple3[T1, T2, T3]]](t1, t2, t3)
|
||||||
@@ -176,6 +200,12 @@ func Eitherize4[F ~func(context.Context, T0, T1, T2, T3) (R, error), T0, T1, T2,
|
|||||||
return G.Eitherize4[ReaderIOEither[R]](f)
|
return G.Eitherize4[ReaderIOEither[R]](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Uneitherize4 converts a function with 5 parameters returning a [ReaderIOEither[R]] into a function with 4 parameters returning a tuple.
|
||||||
|
// The first parameter is considered to be the [context.Context].
|
||||||
|
func Uneitherize4[F ~func(T0, T1, T2, T3) ReaderIOEither[R], T0, T1, T2, T3, R any](f F) func(context.Context, T0, T1, T2, T3) (R, error) {
|
||||||
|
return G.Uneitherize4[ReaderIOEither[R], func(context.Context, T0, T1, T2, T3) (R, error)](f)
|
||||||
|
}
|
||||||
|
|
||||||
// SequenceT4 converts 4 [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple4].
|
// SequenceT4 converts 4 [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple4].
|
||||||
func SequenceT4[T1, T2, T3, T4 any](t1 ReaderIOEither[T1], t2 ReaderIOEither[T2], t3 ReaderIOEither[T3], t4 ReaderIOEither[T4]) ReaderIOEither[T.Tuple4[T1, T2, T3, T4]] {
|
func SequenceT4[T1, T2, T3, T4 any](t1 ReaderIOEither[T1], t2 ReaderIOEither[T2], t3 ReaderIOEither[T3], t4 ReaderIOEither[T4]) ReaderIOEither[T.Tuple4[T1, T2, T3, T4]] {
|
||||||
return G.SequenceT4[ReaderIOEither[T.Tuple4[T1, T2, T3, T4]]](t1, t2, t3, t4)
|
return G.SequenceT4[ReaderIOEither[T.Tuple4[T1, T2, T3, T4]]](t1, t2, t3, t4)
|
||||||
@@ -227,6 +257,12 @@ func Eitherize5[F ~func(context.Context, T0, T1, T2, T3, T4) (R, error), T0, T1,
|
|||||||
return G.Eitherize5[ReaderIOEither[R]](f)
|
return G.Eitherize5[ReaderIOEither[R]](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Uneitherize5 converts a function with 6 parameters returning a [ReaderIOEither[R]] into a function with 5 parameters returning a tuple.
|
||||||
|
// The first parameter is considered to be the [context.Context].
|
||||||
|
func Uneitherize5[F ~func(T0, T1, T2, T3, T4) ReaderIOEither[R], T0, T1, T2, T3, T4, R any](f F) func(context.Context, T0, T1, T2, T3, T4) (R, error) {
|
||||||
|
return G.Uneitherize5[ReaderIOEither[R], func(context.Context, T0, T1, T2, T3, T4) (R, error)](f)
|
||||||
|
}
|
||||||
|
|
||||||
// SequenceT5 converts 5 [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple5].
|
// SequenceT5 converts 5 [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple5].
|
||||||
func SequenceT5[T1, T2, T3, T4, T5 any](t1 ReaderIOEither[T1], t2 ReaderIOEither[T2], t3 ReaderIOEither[T3], t4 ReaderIOEither[T4], t5 ReaderIOEither[T5]) ReaderIOEither[T.Tuple5[T1, T2, T3, T4, T5]] {
|
func SequenceT5[T1, T2, T3, T4, T5 any](t1 ReaderIOEither[T1], t2 ReaderIOEither[T2], t3 ReaderIOEither[T3], t4 ReaderIOEither[T4], t5 ReaderIOEither[T5]) ReaderIOEither[T.Tuple5[T1, T2, T3, T4, T5]] {
|
||||||
return G.SequenceT5[ReaderIOEither[T.Tuple5[T1, T2, T3, T4, T5]]](t1, t2, t3, t4, t5)
|
return G.SequenceT5[ReaderIOEither[T.Tuple5[T1, T2, T3, T4, T5]]](t1, t2, t3, t4, t5)
|
||||||
@@ -278,6 +314,12 @@ func Eitherize6[F ~func(context.Context, T0, T1, T2, T3, T4, T5) (R, error), T0,
|
|||||||
return G.Eitherize6[ReaderIOEither[R]](f)
|
return G.Eitherize6[ReaderIOEither[R]](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Uneitherize6 converts a function with 7 parameters returning a [ReaderIOEither[R]] into a function with 6 parameters returning a tuple.
|
||||||
|
// The first parameter is considered to be the [context.Context].
|
||||||
|
func Uneitherize6[F ~func(T0, T1, T2, T3, T4, T5) ReaderIOEither[R], T0, T1, T2, T3, T4, T5, R any](f F) func(context.Context, T0, T1, T2, T3, T4, T5) (R, error) {
|
||||||
|
return G.Uneitherize6[ReaderIOEither[R], func(context.Context, T0, T1, T2, T3, T4, T5) (R, error)](f)
|
||||||
|
}
|
||||||
|
|
||||||
// SequenceT6 converts 6 [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple6].
|
// SequenceT6 converts 6 [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple6].
|
||||||
func SequenceT6[T1, T2, T3, T4, T5, T6 any](t1 ReaderIOEither[T1], t2 ReaderIOEither[T2], t3 ReaderIOEither[T3], t4 ReaderIOEither[T4], t5 ReaderIOEither[T5], t6 ReaderIOEither[T6]) ReaderIOEither[T.Tuple6[T1, T2, T3, T4, T5, T6]] {
|
func SequenceT6[T1, T2, T3, T4, T5, T6 any](t1 ReaderIOEither[T1], t2 ReaderIOEither[T2], t3 ReaderIOEither[T3], t4 ReaderIOEither[T4], t5 ReaderIOEither[T5], t6 ReaderIOEither[T6]) ReaderIOEither[T.Tuple6[T1, T2, T3, T4, T5, T6]] {
|
||||||
return G.SequenceT6[ReaderIOEither[T.Tuple6[T1, T2, T3, T4, T5, T6]]](t1, t2, t3, t4, t5, t6)
|
return G.SequenceT6[ReaderIOEither[T.Tuple6[T1, T2, T3, T4, T5, T6]]](t1, t2, t3, t4, t5, t6)
|
||||||
@@ -329,6 +371,12 @@ func Eitherize7[F ~func(context.Context, T0, T1, T2, T3, T4, T5, T6) (R, error),
|
|||||||
return G.Eitherize7[ReaderIOEither[R]](f)
|
return G.Eitherize7[ReaderIOEither[R]](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Uneitherize7 converts a function with 8 parameters returning a [ReaderIOEither[R]] into a function with 7 parameters returning a tuple.
|
||||||
|
// The first parameter is considered to be the [context.Context].
|
||||||
|
func Uneitherize7[F ~func(T0, T1, T2, T3, T4, T5, T6) ReaderIOEither[R], T0, T1, T2, T3, T4, T5, T6, R any](f F) func(context.Context, T0, T1, T2, T3, T4, T5, T6) (R, error) {
|
||||||
|
return G.Uneitherize7[ReaderIOEither[R], func(context.Context, T0, T1, T2, T3, T4, T5, T6) (R, error)](f)
|
||||||
|
}
|
||||||
|
|
||||||
// SequenceT7 converts 7 [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple7].
|
// SequenceT7 converts 7 [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple7].
|
||||||
func SequenceT7[T1, T2, T3, T4, T5, T6, T7 any](t1 ReaderIOEither[T1], t2 ReaderIOEither[T2], t3 ReaderIOEither[T3], t4 ReaderIOEither[T4], t5 ReaderIOEither[T5], t6 ReaderIOEither[T6], t7 ReaderIOEither[T7]) ReaderIOEither[T.Tuple7[T1, T2, T3, T4, T5, T6, T7]] {
|
func SequenceT7[T1, T2, T3, T4, T5, T6, T7 any](t1 ReaderIOEither[T1], t2 ReaderIOEither[T2], t3 ReaderIOEither[T3], t4 ReaderIOEither[T4], t5 ReaderIOEither[T5], t6 ReaderIOEither[T6], t7 ReaderIOEither[T7]) ReaderIOEither[T.Tuple7[T1, T2, T3, T4, T5, T6, T7]] {
|
||||||
return G.SequenceT7[ReaderIOEither[T.Tuple7[T1, T2, T3, T4, T5, T6, T7]]](t1, t2, t3, t4, t5, t6, t7)
|
return G.SequenceT7[ReaderIOEither[T.Tuple7[T1, T2, T3, T4, T5, T6, T7]]](t1, t2, t3, t4, t5, t6, t7)
|
||||||
@@ -380,6 +428,12 @@ func Eitherize8[F ~func(context.Context, T0, T1, T2, T3, T4, T5, T6, T7) (R, err
|
|||||||
return G.Eitherize8[ReaderIOEither[R]](f)
|
return G.Eitherize8[ReaderIOEither[R]](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Uneitherize8 converts a function with 9 parameters returning a [ReaderIOEither[R]] into a function with 8 parameters returning a tuple.
|
||||||
|
// The first parameter is considered to be the [context.Context].
|
||||||
|
func Uneitherize8[F ~func(T0, T1, T2, T3, T4, T5, T6, T7) ReaderIOEither[R], T0, T1, T2, T3, T4, T5, T6, T7, R any](f F) func(context.Context, T0, T1, T2, T3, T4, T5, T6, T7) (R, error) {
|
||||||
|
return G.Uneitherize8[ReaderIOEither[R], func(context.Context, T0, T1, T2, T3, T4, T5, T6, T7) (R, error)](f)
|
||||||
|
}
|
||||||
|
|
||||||
// SequenceT8 converts 8 [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple8].
|
// SequenceT8 converts 8 [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple8].
|
||||||
func SequenceT8[T1, T2, T3, T4, T5, T6, T7, T8 any](t1 ReaderIOEither[T1], t2 ReaderIOEither[T2], t3 ReaderIOEither[T3], t4 ReaderIOEither[T4], t5 ReaderIOEither[T5], t6 ReaderIOEither[T6], t7 ReaderIOEither[T7], t8 ReaderIOEither[T8]) ReaderIOEither[T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] {
|
func SequenceT8[T1, T2, T3, T4, T5, T6, T7, T8 any](t1 ReaderIOEither[T1], t2 ReaderIOEither[T2], t3 ReaderIOEither[T3], t4 ReaderIOEither[T4], t5 ReaderIOEither[T5], t6 ReaderIOEither[T6], t7 ReaderIOEither[T7], t8 ReaderIOEither[T8]) ReaderIOEither[T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] {
|
||||||
return G.SequenceT8[ReaderIOEither[T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]]](t1, t2, t3, t4, t5, t6, t7, t8)
|
return G.SequenceT8[ReaderIOEither[T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]]](t1, t2, t3, t4, t5, t6, t7, t8)
|
||||||
@@ -431,6 +485,12 @@ func Eitherize9[F ~func(context.Context, T0, T1, T2, T3, T4, T5, T6, T7, T8) (R,
|
|||||||
return G.Eitherize9[ReaderIOEither[R]](f)
|
return G.Eitherize9[ReaderIOEither[R]](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Uneitherize9 converts a function with 10 parameters returning a [ReaderIOEither[R]] into a function with 9 parameters returning a tuple.
|
||||||
|
// The first parameter is considered to be the [context.Context].
|
||||||
|
func Uneitherize9[F ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8) ReaderIOEither[R], T0, T1, T2, T3, T4, T5, T6, T7, T8, R any](f F) func(context.Context, T0, T1, T2, T3, T4, T5, T6, T7, T8) (R, error) {
|
||||||
|
return G.Uneitherize9[ReaderIOEither[R], func(context.Context, T0, T1, T2, T3, T4, T5, T6, T7, T8) (R, error)](f)
|
||||||
|
}
|
||||||
|
|
||||||
// SequenceT9 converts 9 [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple9].
|
// SequenceT9 converts 9 [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple9].
|
||||||
func SequenceT9[T1, T2, T3, T4, T5, T6, T7, T8, T9 any](t1 ReaderIOEither[T1], t2 ReaderIOEither[T2], t3 ReaderIOEither[T3], t4 ReaderIOEither[T4], t5 ReaderIOEither[T5], t6 ReaderIOEither[T6], t7 ReaderIOEither[T7], t8 ReaderIOEither[T8], t9 ReaderIOEither[T9]) ReaderIOEither[T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] {
|
func SequenceT9[T1, T2, T3, T4, T5, T6, T7, T8, T9 any](t1 ReaderIOEither[T1], t2 ReaderIOEither[T2], t3 ReaderIOEither[T3], t4 ReaderIOEither[T4], t5 ReaderIOEither[T5], t6 ReaderIOEither[T6], t7 ReaderIOEither[T7], t8 ReaderIOEither[T8], t9 ReaderIOEither[T9]) ReaderIOEither[T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] {
|
||||||
return G.SequenceT9[ReaderIOEither[T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]]](t1, t2, t3, t4, t5, t6, t7, t8, t9)
|
return G.SequenceT9[ReaderIOEither[T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]]](t1, t2, t3, t4, t5, t6, t7, t8, t9)
|
||||||
@@ -482,6 +542,12 @@ func Eitherize10[F ~func(context.Context, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9
|
|||||||
return G.Eitherize10[ReaderIOEither[R]](f)
|
return G.Eitherize10[ReaderIOEither[R]](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Uneitherize10 converts a function with 11 parameters returning a [ReaderIOEither[R]] into a function with 10 parameters returning a tuple.
|
||||||
|
// The first parameter is considered to be the [context.Context].
|
||||||
|
func Uneitherize10[F ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) ReaderIOEither[R], T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, R any](f F) func(context.Context, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) (R, error) {
|
||||||
|
return G.Uneitherize10[ReaderIOEither[R], func(context.Context, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) (R, error)](f)
|
||||||
|
}
|
||||||
|
|
||||||
// SequenceT10 converts 10 [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple10].
|
// SequenceT10 converts 10 [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple10].
|
||||||
func SequenceT10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any](t1 ReaderIOEither[T1], t2 ReaderIOEither[T2], t3 ReaderIOEither[T3], t4 ReaderIOEither[T4], t5 ReaderIOEither[T5], t6 ReaderIOEither[T6], t7 ReaderIOEither[T7], t8 ReaderIOEither[T8], t9 ReaderIOEither[T9], t10 ReaderIOEither[T10]) ReaderIOEither[T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] {
|
func SequenceT10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any](t1 ReaderIOEither[T1], t2 ReaderIOEither[T2], t3 ReaderIOEither[T3], t4 ReaderIOEither[T4], t5 ReaderIOEither[T5], t6 ReaderIOEither[T6], t7 ReaderIOEither[T7], t8 ReaderIOEither[T8], t9 ReaderIOEither[T9], t10 ReaderIOEither[T10]) ReaderIOEither[T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] {
|
||||||
return G.SequenceT10[ReaderIOEither[T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]]](t1, t2, t3, t4, t5, t6, t7, t8, t9, t10)
|
return G.SequenceT10[ReaderIOEither[T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]]](t1, t2, t3, t4, t5, t6, t7, t8, t9, t10)
|
||||||
|
92
context/readerioeither/generic/bind.go
Normal file
92
context/readerioeither/generic/bind.go
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package generic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
ET "github.com/IBM/fp-go/either"
|
||||||
|
A "github.com/IBM/fp-go/internal/apply"
|
||||||
|
C "github.com/IBM/fp-go/internal/chain"
|
||||||
|
F "github.com/IBM/fp-go/internal/functor"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Bind creates an empty context of type [S] to be used with the [Bind] operation
|
||||||
|
func Do[GRS ~func(context.Context) GS, GS ~func() ET.Either[error, S], S any](
|
||||||
|
empty S,
|
||||||
|
) GRS {
|
||||||
|
return Of[GRS, GS, S](empty)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bind attaches the result of a computation to a context [S1] to produce a context [S2]
|
||||||
|
func Bind[GRS1 ~func(context.Context) GS1, GRS2 ~func(context.Context) GS2, GRT ~func(context.Context) GT, GS1 ~func() ET.Either[error, S1], GS2 ~func() ET.Either[error, S2], GT ~func() ET.Either[error, T], S1, S2, T any](
|
||||||
|
setter func(T) func(S1) S2,
|
||||||
|
f func(S1) GRT,
|
||||||
|
) func(GRS1) GRS2 {
|
||||||
|
return C.Bind(
|
||||||
|
Chain[GRS1, GRS2, GS1, GS2, S1, S2],
|
||||||
|
Map[GRT, GRS2, GT, GS2, T, S2],
|
||||||
|
setter,
|
||||||
|
f,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Let attaches the result of a computation to a context [S1] to produce a context [S2]
|
||||||
|
func Let[GRS1 ~func(context.Context) GS1, GRS2 ~func(context.Context) GS2, GS1 ~func() ET.Either[error, S1], GS2 ~func() ET.Either[error, S2], S1, S2, T any](
|
||||||
|
key func(T) func(S1) S2,
|
||||||
|
f func(S1) T,
|
||||||
|
) func(GRS1) GRS2 {
|
||||||
|
return F.Let(
|
||||||
|
Map[GRS1, GRS2, GS1, GS2, S1, S2],
|
||||||
|
key,
|
||||||
|
f,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LetTo attaches the a value to a context [S1] to produce a context [S2]
|
||||||
|
func LetTo[GRS1 ~func(context.Context) GS1, GRS2 ~func(context.Context) GS2, GS1 ~func() ET.Either[error, S1], GS2 ~func() ET.Either[error, S2], S1, S2, B any](
|
||||||
|
key func(B) func(S1) S2,
|
||||||
|
b B,
|
||||||
|
) func(GRS1) GRS2 {
|
||||||
|
return F.LetTo(
|
||||||
|
Map[GRS1, GRS2, GS1, GS2, S1, S2],
|
||||||
|
key,
|
||||||
|
b,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BindTo initializes a new state [S1] from a value [T]
|
||||||
|
func BindTo[GRS1 ~func(context.Context) GS1, GRT ~func(context.Context) GT, GS1 ~func() ET.Either[error, S1], GT ~func() ET.Either[error, T], S1, T any](
|
||||||
|
setter func(T) S1,
|
||||||
|
) func(GRT) GRS1 {
|
||||||
|
return C.BindTo(
|
||||||
|
Map[GRT, GRS1, GT, GS1, T, S1],
|
||||||
|
setter,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApS attaches a value to a context [S1] to produce a context [S2] by considering the context and the value concurrently
|
||||||
|
func ApS[GRTS1 ~func(context.Context) GTS1, GRS1 ~func(context.Context) GS1, GRS2 ~func(context.Context) GS2, GRT ~func(context.Context) GT, GTS1 ~func() ET.Either[error, func(T) S2], GS1 ~func() ET.Either[error, S1], GS2 ~func() ET.Either[error, S2], GT ~func() ET.Either[error, T], S1, S2, T any](
|
||||||
|
setter func(T) func(S1) S2,
|
||||||
|
fa GRT,
|
||||||
|
) func(GRS1) GRS2 {
|
||||||
|
return A.ApS(
|
||||||
|
Ap[GRS2, GRTS1, GRT, GS2, GTS1, GT],
|
||||||
|
Map[GRS1, GRTS1, GS1, GTS1, S1, func(T) S2],
|
||||||
|
setter,
|
||||||
|
fa,
|
||||||
|
)
|
||||||
|
}
|
53
context/readerioeither/generic/bracket.go
Normal file
53
context/readerioeither/generic/bracket.go
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package generic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
E "github.com/IBM/fp-go/either"
|
||||||
|
G "github.com/IBM/fp-go/internal/bracket"
|
||||||
|
I "github.com/IBM/fp-go/readerio/generic"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Bracket makes sure that a resource is cleaned up in the event of an error. The release action is called regardless of
|
||||||
|
// whether the body action returns and error or not.
|
||||||
|
func Bracket[
|
||||||
|
GA ~func(context.Context) TA,
|
||||||
|
GB ~func(context.Context) TB,
|
||||||
|
GANY ~func(context.Context) TANY,
|
||||||
|
|
||||||
|
TA ~func() E.Either[error, A],
|
||||||
|
TB ~func() E.Either[error, B],
|
||||||
|
TANY ~func() E.Either[error, ANY],
|
||||||
|
|
||||||
|
A, B, ANY any](
|
||||||
|
|
||||||
|
acquire GA,
|
||||||
|
use func(A) GB,
|
||||||
|
release func(A, E.Either[error, B]) GANY,
|
||||||
|
) GB {
|
||||||
|
return G.Bracket[GA, GB, GANY, E.Either[error, B], A, B](
|
||||||
|
I.Of[GB, TB, context.Context, E.Either[error, B]],
|
||||||
|
MonadChain[GA, GB, TA, TB, A, B],
|
||||||
|
I.MonadChain[GB, GB, TB, TB, context.Context, E.Either[error, B], E.Either[error, B]],
|
||||||
|
MonadChain[GANY, GB, TANY, TB, ANY, B],
|
||||||
|
|
||||||
|
acquire,
|
||||||
|
use,
|
||||||
|
release,
|
||||||
|
)
|
||||||
|
}
|
@@ -2,7 +2,7 @@ package generic
|
|||||||
|
|
||||||
// Code generated by go generate; DO NOT EDIT.
|
// Code generated by go generate; DO NOT EDIT.
|
||||||
// This file was generated by robots at
|
// This file was generated by robots at
|
||||||
// 2023-08-17 22:58:56.457404 +0200 CEST m=+0.024265101
|
// 2024-05-24 22:24:01.4250895 +0200 CEST m=+0.014515801
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -19,12 +19,28 @@ func Eitherize0[GRA ~func(context.Context) GIOA, F ~func(context.Context) (R, er
|
|||||||
return RE.Eitherize0[GRA](f)
|
return RE.Eitherize0[GRA](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Uneitherize0 converts a function with 0 parameters returning a [GRA] into a function with 0 parameters returning a tuple.
|
||||||
|
// The first parameter is considered to be the [context.Context].
|
||||||
|
func Uneitherize0[GRA ~func(context.Context) GIOA, F ~func(context.Context) (R, error), GIOA ~func() E.Either[error, R], R any](f func() GRA) F {
|
||||||
|
return func(c context.Context) (R, error) {
|
||||||
|
return E.UnwrapError(f()(c)())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Eitherize1 converts a function with 1 parameters returning a tuple into a function with 1 parameters returning a [GRA]
|
// Eitherize1 converts a function with 1 parameters returning a tuple into a function with 1 parameters returning a [GRA]
|
||||||
// The inverse function is [Uneitherize1]
|
// The inverse function is [Uneitherize1]
|
||||||
func Eitherize1[GRA ~func(context.Context) GIOA, F ~func(context.Context, T0) (R, error), GIOA ~func() E.Either[error, R], T0, R any](f F) func(T0) GRA {
|
func Eitherize1[GRA ~func(context.Context) GIOA, F ~func(context.Context, T0) (R, error), GIOA ~func() E.Either[error, R], T0, R any](f F) func(T0) GRA {
|
||||||
return RE.Eitherize1[GRA](f)
|
return RE.Eitherize1[GRA](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Uneitherize1 converts a function with 1 parameters returning a [GRA] into a function with 1 parameters returning a tuple.
|
||||||
|
// The first parameter is considered to be the [context.Context].
|
||||||
|
func Uneitherize1[GRA ~func(context.Context) GIOA, F ~func(context.Context, T0) (R, error), GIOA ~func() E.Either[error, R], T0, R any](f func(T0) GRA) F {
|
||||||
|
return func(c context.Context, t0 T0) (R, error) {
|
||||||
|
return E.UnwrapError(f(t0)(c)())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// SequenceT1 converts 1 readers into a reader of a [T.Tuple1].
|
// SequenceT1 converts 1 readers into a reader of a [T.Tuple1].
|
||||||
func SequenceT1[
|
func SequenceT1[
|
||||||
GR_TUPLE1 ~func(context.Context) GIO_TUPLE1,
|
GR_TUPLE1 ~func(context.Context) GIO_TUPLE1,
|
||||||
@@ -169,6 +185,14 @@ func Eitherize2[GRA ~func(context.Context) GIOA, F ~func(context.Context, T0, T1
|
|||||||
return RE.Eitherize2[GRA](f)
|
return RE.Eitherize2[GRA](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Uneitherize2 converts a function with 2 parameters returning a [GRA] into a function with 2 parameters returning a tuple.
|
||||||
|
// The first parameter is considered to be the [context.Context].
|
||||||
|
func Uneitherize2[GRA ~func(context.Context) GIOA, F ~func(context.Context, T0, T1) (R, error), GIOA ~func() E.Either[error, R], T0, T1, R any](f func(T0, T1) GRA) F {
|
||||||
|
return func(c context.Context, t0 T0, t1 T1) (R, error) {
|
||||||
|
return E.UnwrapError(f(t0, t1)(c)())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// SequenceT2 converts 2 readers into a reader of a [T.Tuple2].
|
// SequenceT2 converts 2 readers into a reader of a [T.Tuple2].
|
||||||
func SequenceT2[
|
func SequenceT2[
|
||||||
GR_TUPLE2 ~func(context.Context) GIO_TUPLE2,
|
GR_TUPLE2 ~func(context.Context) GIO_TUPLE2,
|
||||||
@@ -364,6 +388,14 @@ func Eitherize3[GRA ~func(context.Context) GIOA, F ~func(context.Context, T0, T1
|
|||||||
return RE.Eitherize3[GRA](f)
|
return RE.Eitherize3[GRA](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Uneitherize3 converts a function with 3 parameters returning a [GRA] into a function with 3 parameters returning a tuple.
|
||||||
|
// The first parameter is considered to be the [context.Context].
|
||||||
|
func Uneitherize3[GRA ~func(context.Context) GIOA, F ~func(context.Context, T0, T1, T2) (R, error), GIOA ~func() E.Either[error, R], T0, T1, T2, R any](f func(T0, T1, T2) GRA) F {
|
||||||
|
return func(c context.Context, t0 T0, t1 T1, t2 T2) (R, error) {
|
||||||
|
return E.UnwrapError(f(t0, t1, t2)(c)())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// SequenceT3 converts 3 readers into a reader of a [T.Tuple3].
|
// SequenceT3 converts 3 readers into a reader of a [T.Tuple3].
|
||||||
func SequenceT3[
|
func SequenceT3[
|
||||||
GR_TUPLE3 ~func(context.Context) GIO_TUPLE3,
|
GR_TUPLE3 ~func(context.Context) GIO_TUPLE3,
|
||||||
@@ -610,6 +642,14 @@ func Eitherize4[GRA ~func(context.Context) GIOA, F ~func(context.Context, T0, T1
|
|||||||
return RE.Eitherize4[GRA](f)
|
return RE.Eitherize4[GRA](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Uneitherize4 converts a function with 4 parameters returning a [GRA] into a function with 4 parameters returning a tuple.
|
||||||
|
// The first parameter is considered to be the [context.Context].
|
||||||
|
func Uneitherize4[GRA ~func(context.Context) GIOA, F ~func(context.Context, T0, T1, T2, T3) (R, error), GIOA ~func() E.Either[error, R], T0, T1, T2, T3, R any](f func(T0, T1, T2, T3) GRA) F {
|
||||||
|
return func(c context.Context, t0 T0, t1 T1, t2 T2, t3 T3) (R, error) {
|
||||||
|
return E.UnwrapError(f(t0, t1, t2, t3)(c)())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// SequenceT4 converts 4 readers into a reader of a [T.Tuple4].
|
// SequenceT4 converts 4 readers into a reader of a [T.Tuple4].
|
||||||
func SequenceT4[
|
func SequenceT4[
|
||||||
GR_TUPLE4 ~func(context.Context) GIO_TUPLE4,
|
GR_TUPLE4 ~func(context.Context) GIO_TUPLE4,
|
||||||
@@ -907,6 +947,14 @@ func Eitherize5[GRA ~func(context.Context) GIOA, F ~func(context.Context, T0, T1
|
|||||||
return RE.Eitherize5[GRA](f)
|
return RE.Eitherize5[GRA](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Uneitherize5 converts a function with 5 parameters returning a [GRA] into a function with 5 parameters returning a tuple.
|
||||||
|
// The first parameter is considered to be the [context.Context].
|
||||||
|
func Uneitherize5[GRA ~func(context.Context) GIOA, F ~func(context.Context, T0, T1, T2, T3, T4) (R, error), GIOA ~func() E.Either[error, R], T0, T1, T2, T3, T4, R any](f func(T0, T1, T2, T3, T4) GRA) F {
|
||||||
|
return func(c context.Context, t0 T0, t1 T1, t2 T2, t3 T3, t4 T4) (R, error) {
|
||||||
|
return E.UnwrapError(f(t0, t1, t2, t3, t4)(c)())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// SequenceT5 converts 5 readers into a reader of a [T.Tuple5].
|
// SequenceT5 converts 5 readers into a reader of a [T.Tuple5].
|
||||||
func SequenceT5[
|
func SequenceT5[
|
||||||
GR_TUPLE5 ~func(context.Context) GIO_TUPLE5,
|
GR_TUPLE5 ~func(context.Context) GIO_TUPLE5,
|
||||||
@@ -1255,6 +1303,14 @@ func Eitherize6[GRA ~func(context.Context) GIOA, F ~func(context.Context, T0, T1
|
|||||||
return RE.Eitherize6[GRA](f)
|
return RE.Eitherize6[GRA](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Uneitherize6 converts a function with 6 parameters returning a [GRA] into a function with 6 parameters returning a tuple.
|
||||||
|
// The first parameter is considered to be the [context.Context].
|
||||||
|
func Uneitherize6[GRA ~func(context.Context) GIOA, F ~func(context.Context, T0, T1, T2, T3, T4, T5) (R, error), GIOA ~func() E.Either[error, R], T0, T1, T2, T3, T4, T5, R any](f func(T0, T1, T2, T3, T4, T5) GRA) F {
|
||||||
|
return func(c context.Context, t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5) (R, error) {
|
||||||
|
return E.UnwrapError(f(t0, t1, t2, t3, t4, t5)(c)())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// SequenceT6 converts 6 readers into a reader of a [T.Tuple6].
|
// SequenceT6 converts 6 readers into a reader of a [T.Tuple6].
|
||||||
func SequenceT6[
|
func SequenceT6[
|
||||||
GR_TUPLE6 ~func(context.Context) GIO_TUPLE6,
|
GR_TUPLE6 ~func(context.Context) GIO_TUPLE6,
|
||||||
@@ -1654,6 +1710,14 @@ func Eitherize7[GRA ~func(context.Context) GIOA, F ~func(context.Context, T0, T1
|
|||||||
return RE.Eitherize7[GRA](f)
|
return RE.Eitherize7[GRA](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Uneitherize7 converts a function with 7 parameters returning a [GRA] into a function with 7 parameters returning a tuple.
|
||||||
|
// The first parameter is considered to be the [context.Context].
|
||||||
|
func Uneitherize7[GRA ~func(context.Context) GIOA, F ~func(context.Context, T0, T1, T2, T3, T4, T5, T6) (R, error), GIOA ~func() E.Either[error, R], T0, T1, T2, T3, T4, T5, T6, R any](f func(T0, T1, T2, T3, T4, T5, T6) GRA) F {
|
||||||
|
return func(c context.Context, t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6) (R, error) {
|
||||||
|
return E.UnwrapError(f(t0, t1, t2, t3, t4, t5, t6)(c)())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// SequenceT7 converts 7 readers into a reader of a [T.Tuple7].
|
// SequenceT7 converts 7 readers into a reader of a [T.Tuple7].
|
||||||
func SequenceT7[
|
func SequenceT7[
|
||||||
GR_TUPLE7 ~func(context.Context) GIO_TUPLE7,
|
GR_TUPLE7 ~func(context.Context) GIO_TUPLE7,
|
||||||
@@ -2104,6 +2168,14 @@ func Eitherize8[GRA ~func(context.Context) GIOA, F ~func(context.Context, T0, T1
|
|||||||
return RE.Eitherize8[GRA](f)
|
return RE.Eitherize8[GRA](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Uneitherize8 converts a function with 8 parameters returning a [GRA] into a function with 8 parameters returning a tuple.
|
||||||
|
// The first parameter is considered to be the [context.Context].
|
||||||
|
func Uneitherize8[GRA ~func(context.Context) GIOA, F ~func(context.Context, T0, T1, T2, T3, T4, T5, T6, T7) (R, error), GIOA ~func() E.Either[error, R], T0, T1, T2, T3, T4, T5, T6, T7, R any](f func(T0, T1, T2, T3, T4, T5, T6, T7) GRA) F {
|
||||||
|
return func(c context.Context, t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7) (R, error) {
|
||||||
|
return E.UnwrapError(f(t0, t1, t2, t3, t4, t5, t6, t7)(c)())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// SequenceT8 converts 8 readers into a reader of a [T.Tuple8].
|
// SequenceT8 converts 8 readers into a reader of a [T.Tuple8].
|
||||||
func SequenceT8[
|
func SequenceT8[
|
||||||
GR_TUPLE8 ~func(context.Context) GIO_TUPLE8,
|
GR_TUPLE8 ~func(context.Context) GIO_TUPLE8,
|
||||||
@@ -2605,6 +2677,14 @@ func Eitherize9[GRA ~func(context.Context) GIOA, F ~func(context.Context, T0, T1
|
|||||||
return RE.Eitherize9[GRA](f)
|
return RE.Eitherize9[GRA](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Uneitherize9 converts a function with 9 parameters returning a [GRA] into a function with 9 parameters returning a tuple.
|
||||||
|
// The first parameter is considered to be the [context.Context].
|
||||||
|
func Uneitherize9[GRA ~func(context.Context) GIOA, F ~func(context.Context, T0, T1, T2, T3, T4, T5, T6, T7, T8) (R, error), GIOA ~func() E.Either[error, R], T0, T1, T2, T3, T4, T5, T6, T7, T8, R any](f func(T0, T1, T2, T3, T4, T5, T6, T7, T8) GRA) F {
|
||||||
|
return func(c context.Context, t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8) (R, error) {
|
||||||
|
return E.UnwrapError(f(t0, t1, t2, t3, t4, t5, t6, t7, t8)(c)())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// SequenceT9 converts 9 readers into a reader of a [T.Tuple9].
|
// SequenceT9 converts 9 readers into a reader of a [T.Tuple9].
|
||||||
func SequenceT9[
|
func SequenceT9[
|
||||||
GR_TUPLE9 ~func(context.Context) GIO_TUPLE9,
|
GR_TUPLE9 ~func(context.Context) GIO_TUPLE9,
|
||||||
@@ -3157,6 +3237,14 @@ func Eitherize10[GRA ~func(context.Context) GIOA, F ~func(context.Context, T0, T
|
|||||||
return RE.Eitherize10[GRA](f)
|
return RE.Eitherize10[GRA](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Uneitherize10 converts a function with 10 parameters returning a [GRA] into a function with 10 parameters returning a tuple.
|
||||||
|
// The first parameter is considered to be the [context.Context].
|
||||||
|
func Uneitherize10[GRA ~func(context.Context) GIOA, F ~func(context.Context, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) (R, error), GIOA ~func() E.Either[error, R], T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, R any](f func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) GRA) F {
|
||||||
|
return func(c context.Context, t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9) (R, error) {
|
||||||
|
return E.UnwrapError(f(t0, t1, t2, t3, t4, t5, t6, t7, t8, t9)(c)())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// SequenceT10 converts 10 readers into a reader of a [T.Tuple10].
|
// SequenceT10 converts 10 readers into a reader of a [T.Tuple10].
|
||||||
func SequenceT10[
|
func SequenceT10[
|
||||||
GR_TUPLE10 ~func(context.Context) GIO_TUPLE10,
|
GR_TUPLE10 ~func(context.Context) GIO_TUPLE10,
|
||||||
|
56
context/readerioeither/generic/monoid.go
Normal file
56
context/readerioeither/generic/monoid.go
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package generic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
ET "github.com/IBM/fp-go/either"
|
||||||
|
M "github.com/IBM/fp-go/monoid"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ApplicativeMonoid[GRA ~func(context.Context) GIOA, GRFA ~func(context.Context) GIOFA, GIOA ~func() ET.Either[error, A], GIOFA ~func() ET.Either[error, func(A) A], A any](
|
||||||
|
m M.Monoid[A],
|
||||||
|
) M.Monoid[GRA] {
|
||||||
|
return M.ApplicativeMonoid(
|
||||||
|
Of[GRA],
|
||||||
|
MonadMap[GRA, GRFA],
|
||||||
|
MonadAp[GRA, GRA, GRFA],
|
||||||
|
m,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ApplicativeMonoidSeq[GRA ~func(context.Context) GIOA, GRFA ~func(context.Context) GIOFA, GIOA ~func() ET.Either[error, A], GIOFA ~func() ET.Either[error, func(A) A], A any](
|
||||||
|
m M.Monoid[A],
|
||||||
|
) M.Monoid[GRA] {
|
||||||
|
return M.ApplicativeMonoid(
|
||||||
|
Of[GRA],
|
||||||
|
MonadMap[GRA, GRFA],
|
||||||
|
MonadApSeq[GRA, GRA, GRFA],
|
||||||
|
m,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ApplicativeMonoidPar[GRA ~func(context.Context) GIOA, GRFA ~func(context.Context) GIOFA, GIOA ~func() ET.Either[error, A], GIOFA ~func() ET.Either[error, func(A) A], A any](
|
||||||
|
m M.Monoid[A],
|
||||||
|
) M.Monoid[GRA] {
|
||||||
|
return M.ApplicativeMonoid(
|
||||||
|
Of[GRA],
|
||||||
|
MonadMap[GRA, GRFA],
|
||||||
|
MonadApPar[GRA, GRA, GRFA],
|
||||||
|
m,
|
||||||
|
)
|
||||||
|
}
|
@@ -100,6 +100,28 @@ func Map[
|
|||||||
return RIE.Map[GRA, GRB](f)
|
return RIE.Map[GRA, GRB](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func MonadMapTo[
|
||||||
|
GRA ~func(context.Context) GIOA,
|
||||||
|
GRB ~func(context.Context) GIOB,
|
||||||
|
|
||||||
|
GIOA ~func() E.Either[error, A],
|
||||||
|
GIOB ~func() E.Either[error, B],
|
||||||
|
|
||||||
|
A, B any](fa GRA, b B) GRB {
|
||||||
|
return RIE.MonadMapTo[GRA, GRB](fa, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func MapTo[
|
||||||
|
GRA ~func(context.Context) GIOA,
|
||||||
|
GRB ~func(context.Context) GIOB,
|
||||||
|
|
||||||
|
GIOA ~func() E.Either[error, A],
|
||||||
|
GIOB ~func() E.Either[error, B],
|
||||||
|
|
||||||
|
A, B any](b B) func(GRA) GRB {
|
||||||
|
return RIE.MapTo[GRA, GRB](b)
|
||||||
|
}
|
||||||
|
|
||||||
func MonadChain[
|
func MonadChain[
|
||||||
GRA ~func(context.Context) GIOA,
|
GRA ~func(context.Context) GIOA,
|
||||||
GRB ~func(context.Context) GIOB,
|
GRB ~func(context.Context) GIOB,
|
||||||
@@ -160,10 +182,7 @@ func withCancelCauseFunc[
|
|||||||
ma,
|
ma,
|
||||||
IOE.Swap[GIOA, func() E.Either[A, error]],
|
IOE.Swap[GIOA, func() E.Either[A, error]],
|
||||||
IOE.ChainFirstIOK[func() E.Either[A, error], func() any](func(err error) func() any {
|
IOE.ChainFirstIOK[func() E.Either[A, error], func() any](func(err error) func() any {
|
||||||
return IO.MakeIO[func() any](func() any {
|
return IO.FromImpure[func() any](func() { cancel(err) })
|
||||||
cancel(err)
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}),
|
}),
|
||||||
IOE.Swap[func() E.Either[A, error], GIOA],
|
IOE.Swap[func() E.Either[A, error], GIOA],
|
||||||
)
|
)
|
||||||
@@ -410,10 +429,10 @@ func FromIOEither[
|
|||||||
|
|
||||||
func FromIO[
|
func FromIO[
|
||||||
GRA ~func(context.Context) GIOA,
|
GRA ~func(context.Context) GIOA,
|
||||||
GIOA ~func() E.Either[error, A],
|
|
||||||
|
|
||||||
GIOB ~func() A,
|
GIOB ~func() A,
|
||||||
|
|
||||||
|
GIOA ~func() E.Either[error, A],
|
||||||
|
|
||||||
A any](t GIOB) GRA {
|
A any](t GIOB) GRA {
|
||||||
return RIE.FromIO[GRA](t)
|
return RIE.FromIO[GRA](t)
|
||||||
}
|
}
|
||||||
@@ -458,6 +477,34 @@ func ChainIOK[
|
|||||||
return RIE.ChainIOK[GRA, GRB](f)
|
return RIE.ChainIOK[GRA, GRB](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func MonadChainReaderIOK[
|
||||||
|
GRB ~func(context.Context) GIOB,
|
||||||
|
GRA ~func(context.Context) GIOA,
|
||||||
|
GRIO ~func(context.Context) GIO,
|
||||||
|
|
||||||
|
GIOA ~func() E.Either[error, A],
|
||||||
|
GIOB ~func() E.Either[error, B],
|
||||||
|
|
||||||
|
GIO ~func() B,
|
||||||
|
|
||||||
|
A, B any](ma GRA, f func(A) GRIO) GRB {
|
||||||
|
return RIE.MonadChainReaderIOK[GRA, GRB](ma, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ChainReaderIOK[
|
||||||
|
GRB ~func(context.Context) GIOB,
|
||||||
|
GRA ~func(context.Context) GIOA,
|
||||||
|
GRIO ~func(context.Context) GIO,
|
||||||
|
|
||||||
|
GIOA ~func() E.Either[error, A],
|
||||||
|
GIOB ~func() E.Either[error, B],
|
||||||
|
|
||||||
|
GIO ~func() B,
|
||||||
|
|
||||||
|
A, B any](f func(A) GRIO) func(ma GRA) GRB {
|
||||||
|
return RIE.ChainReaderIOK[GRA, GRB](f)
|
||||||
|
}
|
||||||
|
|
||||||
func MonadChainFirstIOK[
|
func MonadChainFirstIOK[
|
||||||
GRA ~func(context.Context) GIOA,
|
GRA ~func(context.Context) GIOA,
|
||||||
GIOA ~func() E.Either[error, A],
|
GIOA ~func() E.Either[error, A],
|
||||||
@@ -521,7 +568,7 @@ func Timer[
|
|||||||
](delay time.Duration) GRA {
|
](delay time.Duration) GRA {
|
||||||
return F.Pipe2(
|
return F.Pipe2(
|
||||||
IO.Now[func() time.Time](),
|
IO.Now[func() time.Time](),
|
||||||
FromIO[GRA, GIOA, func() time.Time],
|
FromIO[GRA, func() time.Time],
|
||||||
Delay[GRA](delay),
|
Delay[GRA](delay),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -543,3 +590,82 @@ func TryCatch[
|
|||||||
A any](f func(context.Context) func() (A, error)) GRA {
|
A any](f func(context.Context) func() (A, error)) GRA {
|
||||||
return RIE.TryCatch[GRA](f, ER.IdentityError)
|
return RIE.TryCatch[GRA](f, ER.IdentityError)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func MonadAlt[LAZY ~func() GEA, GEA ~func(context.Context) GIOA, GIOA ~func() E.Either[error, A], A any](first GEA, second LAZY) GEA {
|
||||||
|
return RIE.MonadAlt(first, second)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Alt[LAZY ~func() GEA, GEA ~func(context.Context) GIOA, GIOA ~func() E.Either[error, A], A any](second LAZY) func(GEA) GEA {
|
||||||
|
return RIE.Alt(second)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Memoize computes the value of the provided monad lazily but exactly once
|
||||||
|
// The context used to compute the value is the context of the first call, so do not use this
|
||||||
|
// method if the value has a functional dependency on the content of the context
|
||||||
|
func Memoize[
|
||||||
|
GRA ~func(context.Context) GIOA,
|
||||||
|
GIOA ~func() E.Either[error, A],
|
||||||
|
A any](rdr GRA) GRA {
|
||||||
|
return RIE.Memoize[GRA](rdr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Flatten[
|
||||||
|
GGRA ~func(context.Context) GGIOA,
|
||||||
|
GGIOA ~func() E.Either[error, GRA],
|
||||||
|
GRA ~func(context.Context) GIOA,
|
||||||
|
GIOA ~func() E.Either[error, A],
|
||||||
|
A any](rdr GGRA) GRA {
|
||||||
|
return RIE.Flatten[GRA](rdr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func MonadFromReaderIO[
|
||||||
|
GRIOEA ~func(context.Context) GIOEA,
|
||||||
|
GIOEA ~func() E.Either[error, A],
|
||||||
|
|
||||||
|
GRIOA ~func(context.Context) GIOA,
|
||||||
|
GIOA ~func() A,
|
||||||
|
|
||||||
|
A any](a A, f func(A) GRIOA) GRIOEA {
|
||||||
|
return RIE.MonadFromReaderIO[GRIOEA](a, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func FromReaderIO[
|
||||||
|
GRIOEA ~func(context.Context) GIOEA,
|
||||||
|
GIOEA ~func() E.Either[error, A],
|
||||||
|
|
||||||
|
GRIOA ~func(context.Context) GIOA,
|
||||||
|
GIOA ~func() A,
|
||||||
|
|
||||||
|
A any](f func(A) GRIOA) func(A) GRIOEA {
|
||||||
|
return RIE.FromReaderIO[GRIOEA](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func RightReaderIO[
|
||||||
|
GRIOEA ~func(context.Context) GIOEA,
|
||||||
|
GIOEA ~func() E.Either[error, A],
|
||||||
|
|
||||||
|
GRIOA ~func(context.Context) GIOA,
|
||||||
|
GIOA ~func() A,
|
||||||
|
|
||||||
|
A any](ma GRIOA) GRIOEA {
|
||||||
|
return RIE.RightReaderIO[GRIOEA](ma)
|
||||||
|
}
|
||||||
|
|
||||||
|
func LeftReaderIO[
|
||||||
|
GRIOEA ~func(context.Context) GIOEA,
|
||||||
|
GIOEA ~func() E.Either[error, A],
|
||||||
|
|
||||||
|
GRIOE ~func(context.Context) GIOE,
|
||||||
|
GIOE ~func() error,
|
||||||
|
|
||||||
|
A any](ma GRIOE) GRIOEA {
|
||||||
|
return RIE.LeftReaderIO[GRIOEA](ma)
|
||||||
|
}
|
||||||
|
|
||||||
|
func MonadFlap[GREAB ~func(context.Context) GEAB, GREB ~func(context.Context) GEB, GEAB ~func() E.Either[error, func(A) B], GEB ~func() E.Either[error, B], B, A any](fab GREAB, a A) GREB {
|
||||||
|
return RIE.MonadFlap[GREAB, GREB](fab, a)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Flap[GREAB ~func(context.Context) GEAB, GREB ~func(context.Context) GEB, GEAB ~func() E.Either[error, func(A) B], GEB ~func() E.Either[error, B], B, A any](a A) func(GREAB) GREB {
|
||||||
|
return RIE.Flap[GREAB, GREB](a)
|
||||||
|
}
|
||||||
|
@@ -13,18 +13,17 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package reader
|
package generic
|
||||||
|
|
||||||
import (
|
import (
|
||||||
R "github.com/IBM/fp-go/reader/generic"
|
"context"
|
||||||
|
|
||||||
|
ET "github.com/IBM/fp-go/either"
|
||||||
|
S "github.com/IBM/fp-go/semigroup"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TraverseArray transforms an array
|
func AltSemigroup[GRA ~func(context.Context) GIOA, GIOA ~func() ET.Either[error, A], A any]() S.Semigroup[GRA] {
|
||||||
func TraverseArray[A, B any](f func(A) Reader[B]) func([]A) Reader[[]B] {
|
return S.AltSemigroup(
|
||||||
return R.TraverseArray[Reader[B], Reader[[]B], []A](f)
|
MonadAlt[func() GRA],
|
||||||
}
|
)
|
||||||
|
|
||||||
// SequenceArray converts a homogeneous sequence of either into an either of sequence
|
|
||||||
func SequenceArray[A any](ma []Reader[A]) Reader[[]A] {
|
|
||||||
return R.SequenceArray[Reader[A], Reader[[]A]](ma)
|
|
||||||
}
|
}
|
44
context/readerioeither/generic/sync.go
Normal file
44
context/readerioeither/generic/sync.go
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package generic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
E "github.com/IBM/fp-go/either"
|
||||||
|
F "github.com/IBM/fp-go/function"
|
||||||
|
IO "github.com/IBM/fp-go/io/generic"
|
||||||
|
)
|
||||||
|
|
||||||
|
// WithLock executes the provided IO operation in the scope of a lock
|
||||||
|
func WithLock[
|
||||||
|
GRA ~func(context.Context) GIOA,
|
||||||
|
GIOA ~func() E.Either[error, A],
|
||||||
|
GRCANCEL ~func(context.Context) GIOCANCEL,
|
||||||
|
GIOCANCEL ~func() E.Either[error, context.CancelFunc],
|
||||||
|
A any](lock GRCANCEL) func(fa GRA) GRA {
|
||||||
|
|
||||||
|
type GRANY func(ctx context.Context) func() E.Either[error, any]
|
||||||
|
type IOANY func() any
|
||||||
|
|
||||||
|
return F.Flow2(
|
||||||
|
F.Constant1[context.CancelFunc, GRA],
|
||||||
|
WithResource[GRA, GRCANCEL, GRANY](lock, F.Flow2(
|
||||||
|
IO.FromImpure[IOANY, context.CancelFunc],
|
||||||
|
FromIO[GRANY, IOANY],
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
}
|
@@ -62,6 +62,25 @@ func TraverseArray[
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TraverseArrayWithIndex transforms an array
|
||||||
|
func TraverseArrayWithIndex[
|
||||||
|
AS ~[]A,
|
||||||
|
GRBS ~func(context.Context) GIOBS,
|
||||||
|
GRB ~func(context.Context) GIOB,
|
||||||
|
GIOBS ~func() E.Either[error, BS],
|
||||||
|
GIOB ~func() E.Either[error, B],
|
||||||
|
BS ~[]B,
|
||||||
|
A, B any](f func(int, A) GRB) func(AS) GRBS {
|
||||||
|
|
||||||
|
return RA.TraverseWithIndex[AS](
|
||||||
|
Of[GRBS, GIOBS, BS],
|
||||||
|
Map[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GIOBS, func() E.Either[error, func(B) BS], BS, func(B) BS],
|
||||||
|
Ap[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GRB],
|
||||||
|
|
||||||
|
f,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// SequenceArray converts a homogeneous sequence of either into an either of sequence
|
// SequenceArray converts a homogeneous sequence of either into an either of sequence
|
||||||
func SequenceArray[
|
func SequenceArray[
|
||||||
AS ~[]A,
|
AS ~[]A,
|
||||||
@@ -115,6 +134,26 @@ func TraverseRecord[K comparable,
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TraverseRecordWithIndex transforms a record
|
||||||
|
func TraverseRecordWithIndex[K comparable,
|
||||||
|
AS ~map[K]A,
|
||||||
|
GRBS ~func(context.Context) GIOBS,
|
||||||
|
GRB ~func(context.Context) GIOB,
|
||||||
|
GIOBS ~func() E.Either[error, BS],
|
||||||
|
GIOB ~func() E.Either[error, B],
|
||||||
|
BS ~map[K]B,
|
||||||
|
|
||||||
|
A, B any](f func(K, A) GRB) func(AS) GRBS {
|
||||||
|
|
||||||
|
return RR.TraverseWithIndex[AS](
|
||||||
|
Of[GRBS, GIOBS, BS],
|
||||||
|
Map[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GIOBS, func() E.Either[error, func(B) BS], BS, func(B) BS],
|
||||||
|
Ap[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GRB],
|
||||||
|
|
||||||
|
f,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// SequenceRecord converts a homogeneous sequence of either into an either of sequence
|
// SequenceRecord converts a homogeneous sequence of either into an either of sequence
|
||||||
func SequenceRecord[K comparable,
|
func SequenceRecord[K comparable,
|
||||||
AS ~map[K]A,
|
AS ~map[K]A,
|
||||||
@@ -127,3 +166,289 @@ func SequenceRecord[K comparable,
|
|||||||
|
|
||||||
return MonadTraverseRecord[K, GAS, GRAS](ma, F.Identity[GRA])
|
return MonadTraverseRecord[K, GAS, GRAS](ma, F.Identity[GRA])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MonadTraverseArraySeq transforms an array
|
||||||
|
func MonadTraverseArraySeq[
|
||||||
|
AS ~[]A,
|
||||||
|
GRBS ~func(context.Context) GIOBS,
|
||||||
|
GRB ~func(context.Context) GIOB,
|
||||||
|
GIOBS ~func() E.Either[error, BS],
|
||||||
|
GIOB ~func() E.Either[error, B],
|
||||||
|
BS ~[]B,
|
||||||
|
A, B any](as AS, f func(A) GRB) GRBS {
|
||||||
|
|
||||||
|
return RA.MonadTraverse[AS](
|
||||||
|
Of[GRBS, GIOBS, BS],
|
||||||
|
Map[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GIOBS, func() E.Either[error, func(B) BS], BS, func(B) BS],
|
||||||
|
ApSeq[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GRB],
|
||||||
|
|
||||||
|
as, f,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TraverseArraySeq transforms an array
|
||||||
|
func TraverseArraySeq[
|
||||||
|
AS ~[]A,
|
||||||
|
GRBS ~func(context.Context) GIOBS,
|
||||||
|
GRB ~func(context.Context) GIOB,
|
||||||
|
GIOBS ~func() E.Either[error, BS],
|
||||||
|
GIOB ~func() E.Either[error, B],
|
||||||
|
BS ~[]B,
|
||||||
|
A, B any](f func(A) GRB) func(AS) GRBS {
|
||||||
|
|
||||||
|
return RA.Traverse[AS](
|
||||||
|
Of[GRBS, GIOBS, BS],
|
||||||
|
Map[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GIOBS, func() E.Either[error, func(B) BS], BS, func(B) BS],
|
||||||
|
ApSeq[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GRB],
|
||||||
|
|
||||||
|
f,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TraverseArrayWithIndexSeq transforms an array
|
||||||
|
func TraverseArrayWithIndexSeq[
|
||||||
|
AS ~[]A,
|
||||||
|
GRBS ~func(context.Context) GIOBS,
|
||||||
|
GRB ~func(context.Context) GIOB,
|
||||||
|
GIOBS ~func() E.Either[error, BS],
|
||||||
|
GIOB ~func() E.Either[error, B],
|
||||||
|
BS ~[]B,
|
||||||
|
A, B any](f func(int, A) GRB) func(AS) GRBS {
|
||||||
|
|
||||||
|
return RA.TraverseWithIndex[AS](
|
||||||
|
Of[GRBS, GIOBS, BS],
|
||||||
|
Map[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GIOBS, func() E.Either[error, func(B) BS], BS, func(B) BS],
|
||||||
|
ApSeq[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GRB],
|
||||||
|
|
||||||
|
f,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SequenceArraySeq converts a homogeneous sequence of either into an either of sequence
|
||||||
|
func SequenceArraySeq[
|
||||||
|
AS ~[]A,
|
||||||
|
GAS ~[]GRA,
|
||||||
|
GRAS ~func(context.Context) GIOAS,
|
||||||
|
GRA ~func(context.Context) GIOA,
|
||||||
|
GIOAS ~func() E.Either[error, AS],
|
||||||
|
GIOA ~func() E.Either[error, A],
|
||||||
|
A any](ma GAS) GRAS {
|
||||||
|
|
||||||
|
return MonadTraverseArraySeq[GAS, GRAS](ma, F.Identity[GRA])
|
||||||
|
}
|
||||||
|
|
||||||
|
// MonadTraverseRecordSeq transforms a record
|
||||||
|
func MonadTraverseRecordSeq[K comparable,
|
||||||
|
AS ~map[K]A,
|
||||||
|
GRBS ~func(context.Context) GIOBS,
|
||||||
|
GRB ~func(context.Context) GIOB,
|
||||||
|
GIOBS ~func() E.Either[error, BS],
|
||||||
|
GIOB ~func() E.Either[error, B],
|
||||||
|
BS ~map[K]B,
|
||||||
|
|
||||||
|
A, B any](ma AS, f func(A) GRB) GRBS {
|
||||||
|
|
||||||
|
return RR.MonadTraverse[AS](
|
||||||
|
Of[GRBS, GIOBS, BS],
|
||||||
|
Map[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GIOBS, func() E.Either[error, func(B) BS], BS, func(B) BS],
|
||||||
|
ApSeq[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GRB],
|
||||||
|
|
||||||
|
ma, f,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TraverseRecordSeq transforms a record
|
||||||
|
func TraverseRecordSeq[K comparable,
|
||||||
|
AS ~map[K]A,
|
||||||
|
GRBS ~func(context.Context) GIOBS,
|
||||||
|
GRB ~func(context.Context) GIOB,
|
||||||
|
GIOBS ~func() E.Either[error, BS],
|
||||||
|
GIOB ~func() E.Either[error, B],
|
||||||
|
BS ~map[K]B,
|
||||||
|
|
||||||
|
A, B any](f func(A) GRB) func(AS) GRBS {
|
||||||
|
|
||||||
|
return RR.Traverse[AS](
|
||||||
|
Of[GRBS, GIOBS, BS],
|
||||||
|
Map[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GIOBS, func() E.Either[error, func(B) BS], BS, func(B) BS],
|
||||||
|
ApSeq[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GRB],
|
||||||
|
|
||||||
|
f,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TraverseRecordWithIndexSeq transforms a record
|
||||||
|
func TraverseRecordWithIndexSeq[K comparable,
|
||||||
|
AS ~map[K]A,
|
||||||
|
GRBS ~func(context.Context) GIOBS,
|
||||||
|
GRB ~func(context.Context) GIOB,
|
||||||
|
GIOBS ~func() E.Either[error, BS],
|
||||||
|
GIOB ~func() E.Either[error, B],
|
||||||
|
BS ~map[K]B,
|
||||||
|
|
||||||
|
A, B any](f func(K, A) GRB) func(AS) GRBS {
|
||||||
|
|
||||||
|
return RR.TraverseWithIndex[AS](
|
||||||
|
Of[GRBS, GIOBS, BS],
|
||||||
|
Map[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GIOBS, func() E.Either[error, func(B) BS], BS, func(B) BS],
|
||||||
|
ApSeq[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GRB],
|
||||||
|
|
||||||
|
f,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SequenceRecordSeq converts a homogeneous sequence of either into an either of sequence
|
||||||
|
func SequenceRecordSeq[K comparable,
|
||||||
|
AS ~map[K]A,
|
||||||
|
GAS ~map[K]GRA,
|
||||||
|
GRAS ~func(context.Context) GIOAS,
|
||||||
|
GRA ~func(context.Context) GIOA,
|
||||||
|
GIOAS ~func() E.Either[error, AS],
|
||||||
|
GIOA ~func() E.Either[error, A],
|
||||||
|
A any](ma GAS) GRAS {
|
||||||
|
|
||||||
|
return MonadTraverseRecordSeq[K, GAS, GRAS](ma, F.Identity[GRA])
|
||||||
|
}
|
||||||
|
|
||||||
|
// MonadTraverseArrayPar transforms an array
|
||||||
|
func MonadTraverseArrayPar[
|
||||||
|
AS ~[]A,
|
||||||
|
GRBS ~func(context.Context) GIOBS,
|
||||||
|
GRB ~func(context.Context) GIOB,
|
||||||
|
GIOBS ~func() E.Either[error, BS],
|
||||||
|
GIOB ~func() E.Either[error, B],
|
||||||
|
BS ~[]B,
|
||||||
|
A, B any](as AS, f func(A) GRB) GRBS {
|
||||||
|
|
||||||
|
return RA.MonadTraverse[AS](
|
||||||
|
Of[GRBS, GIOBS, BS],
|
||||||
|
Map[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GIOBS, func() E.Either[error, func(B) BS], BS, func(B) BS],
|
||||||
|
ApPar[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GRB],
|
||||||
|
|
||||||
|
as, f,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TraverseArrayPar transforms an array
|
||||||
|
func TraverseArrayPar[
|
||||||
|
AS ~[]A,
|
||||||
|
GRBS ~func(context.Context) GIOBS,
|
||||||
|
GRB ~func(context.Context) GIOB,
|
||||||
|
GIOBS ~func() E.Either[error, BS],
|
||||||
|
GIOB ~func() E.Either[error, B],
|
||||||
|
BS ~[]B,
|
||||||
|
A, B any](f func(A) GRB) func(AS) GRBS {
|
||||||
|
|
||||||
|
return RA.Traverse[AS](
|
||||||
|
Of[GRBS, GIOBS, BS],
|
||||||
|
Map[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GIOBS, func() E.Either[error, func(B) BS], BS, func(B) BS],
|
||||||
|
ApPar[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GRB],
|
||||||
|
|
||||||
|
f,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TraverseArrayWithIndexPar transforms an array
|
||||||
|
func TraverseArrayWithIndexPar[
|
||||||
|
AS ~[]A,
|
||||||
|
GRBS ~func(context.Context) GIOBS,
|
||||||
|
GRB ~func(context.Context) GIOB,
|
||||||
|
GIOBS ~func() E.Either[error, BS],
|
||||||
|
GIOB ~func() E.Either[error, B],
|
||||||
|
BS ~[]B,
|
||||||
|
A, B any](f func(int, A) GRB) func(AS) GRBS {
|
||||||
|
|
||||||
|
return RA.TraverseWithIndex[AS](
|
||||||
|
Of[GRBS, GIOBS, BS],
|
||||||
|
Map[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GIOBS, func() E.Either[error, func(B) BS], BS, func(B) BS],
|
||||||
|
ApPar[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GRB],
|
||||||
|
|
||||||
|
f,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SequenceArrayPar converts a homogeneous sequence of either into an either of sequence
|
||||||
|
func SequenceArrayPar[
|
||||||
|
AS ~[]A,
|
||||||
|
GAS ~[]GRA,
|
||||||
|
GRAS ~func(context.Context) GIOAS,
|
||||||
|
GRA ~func(context.Context) GIOA,
|
||||||
|
GIOAS ~func() E.Either[error, AS],
|
||||||
|
GIOA ~func() E.Either[error, A],
|
||||||
|
A any](ma GAS) GRAS {
|
||||||
|
|
||||||
|
return MonadTraverseArrayPar[GAS, GRAS](ma, F.Identity[GRA])
|
||||||
|
}
|
||||||
|
|
||||||
|
// MonadTraverseRecordPar transforms a record
|
||||||
|
func MonadTraverseRecordPar[K comparable,
|
||||||
|
AS ~map[K]A,
|
||||||
|
GRBS ~func(context.Context) GIOBS,
|
||||||
|
GRB ~func(context.Context) GIOB,
|
||||||
|
GIOBS ~func() E.Either[error, BS],
|
||||||
|
GIOB ~func() E.Either[error, B],
|
||||||
|
BS ~map[K]B,
|
||||||
|
|
||||||
|
A, B any](ma AS, f func(A) GRB) GRBS {
|
||||||
|
|
||||||
|
return RR.MonadTraverse[AS](
|
||||||
|
Of[GRBS, GIOBS, BS],
|
||||||
|
Map[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GIOBS, func() E.Either[error, func(B) BS], BS, func(B) BS],
|
||||||
|
ApPar[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GRB],
|
||||||
|
|
||||||
|
ma, f,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TraverseRecordPar transforms a record
|
||||||
|
func TraverseRecordPar[K comparable,
|
||||||
|
AS ~map[K]A,
|
||||||
|
GRBS ~func(context.Context) GIOBS,
|
||||||
|
GRB ~func(context.Context) GIOB,
|
||||||
|
GIOBS ~func() E.Either[error, BS],
|
||||||
|
GIOB ~func() E.Either[error, B],
|
||||||
|
BS ~map[K]B,
|
||||||
|
|
||||||
|
A, B any](f func(A) GRB) func(AS) GRBS {
|
||||||
|
|
||||||
|
return RR.Traverse[AS](
|
||||||
|
Of[GRBS, GIOBS, BS],
|
||||||
|
Map[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GIOBS, func() E.Either[error, func(B) BS], BS, func(B) BS],
|
||||||
|
ApPar[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GRB],
|
||||||
|
|
||||||
|
f,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TraverseRecordWithIndexPar transforms a record
|
||||||
|
func TraverseRecordWithIndexPar[K comparable,
|
||||||
|
AS ~map[K]A,
|
||||||
|
GRBS ~func(context.Context) GIOBS,
|
||||||
|
GRB ~func(context.Context) GIOB,
|
||||||
|
GIOBS ~func() E.Either[error, BS],
|
||||||
|
GIOB ~func() E.Either[error, B],
|
||||||
|
BS ~map[K]B,
|
||||||
|
|
||||||
|
A, B any](f func(K, A) GRB) func(AS) GRBS {
|
||||||
|
|
||||||
|
return RR.TraverseWithIndex[AS](
|
||||||
|
Of[GRBS, GIOBS, BS],
|
||||||
|
Map[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GIOBS, func() E.Either[error, func(B) BS], BS, func(B) BS],
|
||||||
|
ApPar[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GRB],
|
||||||
|
|
||||||
|
f,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SequenceRecordPar converts a homogeneous sequence of either into an either of sequence
|
||||||
|
func SequenceRecordPar[K comparable,
|
||||||
|
AS ~map[K]A,
|
||||||
|
GAS ~map[K]GRA,
|
||||||
|
GRAS ~func(context.Context) GIOAS,
|
||||||
|
GRA ~func(context.Context) GIOA,
|
||||||
|
GIOAS ~func() E.Either[error, AS],
|
||||||
|
GIOA ~func() E.Either[error, A],
|
||||||
|
A any](ma GAS) GRAS {
|
||||||
|
|
||||||
|
return MonadTraverseRecordPar[K, GAS, GRAS](ma, F.Identity[GRA])
|
||||||
|
}
|
||||||
|
72
context/readerioeither/http/builder/builder.go
Normal file
72
context/readerioeither/http/builder/builder.go
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package builder
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
RIOE "github.com/IBM/fp-go/context/readerioeither"
|
||||||
|
RIOEH "github.com/IBM/fp-go/context/readerioeither/http"
|
||||||
|
E "github.com/IBM/fp-go/either"
|
||||||
|
F "github.com/IBM/fp-go/function"
|
||||||
|
R "github.com/IBM/fp-go/http/builder"
|
||||||
|
H "github.com/IBM/fp-go/http/headers"
|
||||||
|
LZ "github.com/IBM/fp-go/lazy"
|
||||||
|
O "github.com/IBM/fp-go/option"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Requester(builder *R.Builder) RIOEH.Requester {
|
||||||
|
|
||||||
|
withBody := F.Curry3(func(data []byte, url string, method string) RIOE.ReaderIOEither[*http.Request] {
|
||||||
|
return RIOE.TryCatch(func(ctx context.Context) func() (*http.Request, error) {
|
||||||
|
return func() (*http.Request, error) {
|
||||||
|
req, err := http.NewRequestWithContext(ctx, method, url, bytes.NewReader(data))
|
||||||
|
if err == nil {
|
||||||
|
req.Header.Set(H.ContentLength, strconv.Itoa(len(data)))
|
||||||
|
H.Monoid.Concat(req.Header, builder.GetHeaders())
|
||||||
|
}
|
||||||
|
return req, err
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
withoutBody := F.Curry2(func(url string, method string) RIOE.ReaderIOEither[*http.Request] {
|
||||||
|
return RIOE.TryCatch(func(ctx context.Context) func() (*http.Request, error) {
|
||||||
|
return func() (*http.Request, error) {
|
||||||
|
req, err := http.NewRequestWithContext(ctx, method, url, nil)
|
||||||
|
if err == nil {
|
||||||
|
H.Monoid.Concat(req.Header, builder.GetHeaders())
|
||||||
|
}
|
||||||
|
return req, err
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
return F.Pipe5(
|
||||||
|
builder.GetBody(),
|
||||||
|
O.Fold(LZ.Of(E.Of[error](withoutBody)), E.Map[error](withBody)),
|
||||||
|
E.Ap[func(string) RIOE.ReaderIOEither[*http.Request]](builder.GetTargetURL()),
|
||||||
|
E.Flap[error, RIOE.ReaderIOEither[*http.Request]](builder.GetMethod()),
|
||||||
|
E.GetOrElse(RIOE.Left[*http.Request]),
|
||||||
|
RIOE.Map(func(req *http.Request) *http.Request {
|
||||||
|
req.Header = H.Monoid.Concat(req.Header, builder.GetHeaders())
|
||||||
|
return req
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
59
context/readerioeither/http/builder/builder_test.go
Normal file
59
context/readerioeither/http/builder/builder_test.go
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package builder
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
RIOE "github.com/IBM/fp-go/context/readerioeither"
|
||||||
|
E "github.com/IBM/fp-go/either"
|
||||||
|
F "github.com/IBM/fp-go/function"
|
||||||
|
R "github.com/IBM/fp-go/http/builder"
|
||||||
|
IO "github.com/IBM/fp-go/io"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestBuilderWithQuery(t *testing.T) {
|
||||||
|
// add some query
|
||||||
|
withLimit := R.WithQueryArg("limit")("10")
|
||||||
|
withURL := R.WithURL("http://www.example.org?a=b")
|
||||||
|
|
||||||
|
b := F.Pipe2(
|
||||||
|
R.Default,
|
||||||
|
withLimit,
|
||||||
|
withURL,
|
||||||
|
)
|
||||||
|
|
||||||
|
req := F.Pipe3(
|
||||||
|
b,
|
||||||
|
Requester,
|
||||||
|
RIOE.Map(func(r *http.Request) *url.URL {
|
||||||
|
return r.URL
|
||||||
|
}),
|
||||||
|
RIOE.ChainFirstIOK(func(u *url.URL) IO.IO[any] {
|
||||||
|
return IO.FromImpure(func() {
|
||||||
|
q := u.Query()
|
||||||
|
assert.Equal(t, "10", q.Get("limit"))
|
||||||
|
assert.Equal(t, "b", q.Get("a"))
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.True(t, E.IsRight(req(context.Background())()))
|
||||||
|
}
|
@@ -26,7 +26,7 @@ import (
|
|||||||
IOE "github.com/IBM/fp-go/ioeither"
|
IOE "github.com/IBM/fp-go/ioeither"
|
||||||
IOEF "github.com/IBM/fp-go/ioeither/file"
|
IOEF "github.com/IBM/fp-go/ioeither/file"
|
||||||
J "github.com/IBM/fp-go/json"
|
J "github.com/IBM/fp-go/json"
|
||||||
T "github.com/IBM/fp-go/tuple"
|
P "github.com/IBM/fp-go/pair"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
@@ -79,7 +79,7 @@ func ReadFullResponse(client Client) func(Requester) RIOE.ReaderIOEither[H.FullR
|
|||||||
IOE.Of[error, io.ReadCloser],
|
IOE.Of[error, io.ReadCloser],
|
||||||
IOEF.ReadAll[io.ReadCloser],
|
IOEF.ReadAll[io.ReadCloser],
|
||||||
),
|
),
|
||||||
IOE.Map[error](F.Bind1st(T.MakeTuple2[*http.Response, []byte], resp)),
|
IOE.Map[error](F.Bind1st(P.MakePair[*http.Response, []byte], resp)),
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
@@ -103,16 +103,27 @@ func ReadText(client Client) func(Requester) RIOE.ReaderIOEither[string] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ReadJson sends a request, reads the response and parses the response as JSON
|
// ReadJson sends a request, reads the response and parses the response as JSON
|
||||||
|
//
|
||||||
|
// Deprecated: use [ReadJSON] instead
|
||||||
func ReadJson[A any](client Client) func(Requester) RIOE.ReaderIOEither[A] {
|
func ReadJson[A any](client Client) func(Requester) RIOE.ReaderIOEither[A] {
|
||||||
|
return ReadJSON[A](client)
|
||||||
|
}
|
||||||
|
|
||||||
|
func readJSON(client Client) func(Requester) RIOE.ReaderIOEither[[]byte] {
|
||||||
return F.Flow3(
|
return F.Flow3(
|
||||||
ReadFullResponse(client),
|
ReadFullResponse(client),
|
||||||
RIOE.ChainFirstEitherK(F.Flow2(
|
RIOE.ChainFirstEitherK(F.Flow2(
|
||||||
H.Response,
|
H.Response,
|
||||||
H.ValidateJsonResponse,
|
H.ValidateJSONResponse,
|
||||||
)),
|
|
||||||
RIOE.ChainEitherK(F.Flow2(
|
|
||||||
H.Body,
|
|
||||||
J.Unmarshal[A],
|
|
||||||
)),
|
)),
|
||||||
|
RIOE.Map(H.Body),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadJSON sends a request, reads the response and parses the response as JSON
|
||||||
|
func ReadJSON[A any](client Client) func(Requester) RIOE.ReaderIOEither[A] {
|
||||||
|
return F.Flow2(
|
||||||
|
readJSON(client),
|
||||||
|
RIOE.ChainEitherK(J.Unmarshal[A]),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@@ -21,26 +21,137 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
H "net/http"
|
H "net/http"
|
||||||
|
|
||||||
|
R "github.com/IBM/fp-go/context/readerioeither"
|
||||||
|
E "github.com/IBM/fp-go/either"
|
||||||
|
"github.com/IBM/fp-go/errors"
|
||||||
|
F "github.com/IBM/fp-go/function"
|
||||||
|
IOE "github.com/IBM/fp-go/ioeither"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
type PostItem struct {
|
type PostItem struct {
|
||||||
UserId uint `json:"userId"`
|
UserID uint `json:"userId"`
|
||||||
Id uint `json:"id"`
|
Id uint `json:"id"`
|
||||||
Title string `json:"title"`
|
Title string `json:"title"`
|
||||||
Body string `json:"body"`
|
Body string `json:"body"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getTitle(item PostItem) string {
|
||||||
|
return item.Title
|
||||||
|
}
|
||||||
|
|
||||||
|
type simpleRequestBuilder struct {
|
||||||
|
method string
|
||||||
|
url string
|
||||||
|
headers H.Header
|
||||||
|
}
|
||||||
|
|
||||||
|
func requestBuilder() simpleRequestBuilder {
|
||||||
|
return simpleRequestBuilder{method: "GET"}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b simpleRequestBuilder) WithURL(url string) simpleRequestBuilder {
|
||||||
|
b.url = url
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b simpleRequestBuilder) WithHeader(key, value string) simpleRequestBuilder {
|
||||||
|
if b.headers == nil {
|
||||||
|
b.headers = make(H.Header)
|
||||||
|
} else {
|
||||||
|
b.headers = b.headers.Clone()
|
||||||
|
}
|
||||||
|
b.headers.Set(key, value)
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b simpleRequestBuilder) Build() R.ReaderIOEither[*H.Request] {
|
||||||
|
return func(ctx context.Context) IOE.IOEither[error, *H.Request] {
|
||||||
|
return IOE.TryCatchError(func() (*H.Request, error) {
|
||||||
|
req, err := H.NewRequestWithContext(ctx, b.method, b.url, nil)
|
||||||
|
if err == nil {
|
||||||
|
req.Header = b.headers
|
||||||
|
}
|
||||||
|
return req, err
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestSendSingleRequest(t *testing.T) {
|
func TestSendSingleRequest(t *testing.T) {
|
||||||
|
|
||||||
client := MakeClient(H.DefaultClient)
|
client := MakeClient(H.DefaultClient)
|
||||||
|
|
||||||
req1 := MakeGetRequest("https://jsonplaceholder.typicode.com/posts/1")
|
req1 := MakeGetRequest("https://jsonplaceholder.typicode.com/posts/1")
|
||||||
|
|
||||||
readItem := ReadJson[PostItem](client)
|
readItem := ReadJSON[PostItem](client)
|
||||||
|
|
||||||
resp1 := readItem(req1)
|
resp1 := readItem(req1)
|
||||||
|
|
||||||
resE := resp1(context.Background())()
|
resE := resp1(context.TODO())()
|
||||||
|
|
||||||
fmt.Println(resE)
|
fmt.Println(resE)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// setHeaderUnsafe updates a header value in a request object by mutating the request object
|
||||||
|
func setHeaderUnsafe(key, value string) func(*H.Request) *H.Request {
|
||||||
|
return func(req *H.Request) *H.Request {
|
||||||
|
req.Header.Set(key, value)
|
||||||
|
return req
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSendSingleRequestWithHeaderUnsafe(t *testing.T) {
|
||||||
|
|
||||||
|
client := MakeClient(H.DefaultClient)
|
||||||
|
|
||||||
|
// this is not safe from a puristic perspective, because the map call mutates the request object
|
||||||
|
req1 := F.Pipe2(
|
||||||
|
"https://jsonplaceholder.typicode.com/posts/1",
|
||||||
|
MakeGetRequest,
|
||||||
|
R.Map(setHeaderUnsafe("Content-Type", "text/html")),
|
||||||
|
)
|
||||||
|
|
||||||
|
readItem := ReadJSON[PostItem](client)
|
||||||
|
|
||||||
|
resp1 := F.Pipe2(
|
||||||
|
req1,
|
||||||
|
readItem,
|
||||||
|
R.Map(getTitle),
|
||||||
|
)
|
||||||
|
|
||||||
|
res := F.Pipe1(
|
||||||
|
resp1(context.TODO())(),
|
||||||
|
E.GetOrElse(errors.ToString),
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.Equal(t, "sunt aut facere repellat provident occaecati excepturi optio reprehenderit", res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSendSingleRequestWithHeaderSafe(t *testing.T) {
|
||||||
|
|
||||||
|
client := MakeClient(H.DefaultClient)
|
||||||
|
|
||||||
|
// the request builder assembles config values to construct
|
||||||
|
// the final http request. Each `With` step creates a copy of the settings
|
||||||
|
// so the flow is pure
|
||||||
|
request := requestBuilder().
|
||||||
|
WithURL("https://jsonplaceholder.typicode.com/posts/1").
|
||||||
|
WithHeader("Content-Type", "text/html").
|
||||||
|
Build()
|
||||||
|
|
||||||
|
readItem := ReadJSON[PostItem](client)
|
||||||
|
|
||||||
|
response := F.Pipe2(
|
||||||
|
request,
|
||||||
|
readItem,
|
||||||
|
R.Map(getTitle),
|
||||||
|
)
|
||||||
|
|
||||||
|
res := F.Pipe1(
|
||||||
|
response(context.TODO())(),
|
||||||
|
E.GetOrElse(errors.ToString),
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.Equal(t, "sunt aut facere repellat provident occaecati excepturi optio reprehenderit", res)
|
||||||
|
}
|
||||||
|
56
context/readerioeither/monoid.go
Normal file
56
context/readerioeither/monoid.go
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package readerioeither
|
||||||
|
|
||||||
|
import (
|
||||||
|
G "github.com/IBM/fp-go/context/readerioeither/generic"
|
||||||
|
L "github.com/IBM/fp-go/lazy"
|
||||||
|
M "github.com/IBM/fp-go/monoid"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ApplicativeMonoid returns a [Monoid] that concatenates [ReaderIOEither] instances via their applicative
|
||||||
|
func ApplicativeMonoid[A any](m M.Monoid[A]) M.Monoid[ReaderIOEither[A]] {
|
||||||
|
return G.ApplicativeMonoid[ReaderIOEither[A], ReaderIOEither[func(A) A]](m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApplicativeMonoidSeq returns a [Monoid] that concatenates [ReaderIOEither] instances via their applicative
|
||||||
|
func ApplicativeMonoidSeq[A any](m M.Monoid[A]) M.Monoid[ReaderIOEither[A]] {
|
||||||
|
return G.ApplicativeMonoidSeq[ReaderIOEither[A], ReaderIOEither[func(A) A]](m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApplicativeMonoidPar returns a [Monoid] that concatenates [ReaderIOEither] instances via their applicative
|
||||||
|
func ApplicativeMonoidPar[A any](m M.Monoid[A]) M.Monoid[ReaderIOEither[A]] {
|
||||||
|
return G.ApplicativeMonoidPar[ReaderIOEither[A], ReaderIOEither[func(A) A]](m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AlternativeMonoid is the alternative [Monoid] for [ReaderIOEither]
|
||||||
|
func AlternativeMonoid[A any](m M.Monoid[A]) M.Monoid[ReaderIOEither[A]] {
|
||||||
|
return M.AlternativeMonoid(
|
||||||
|
Of[A],
|
||||||
|
MonadMap[A, func(A) A],
|
||||||
|
MonadAp[A, A],
|
||||||
|
MonadAlt[A],
|
||||||
|
m,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AltMonoid is the alternative [Monoid] for an [ReaderIOEither]
|
||||||
|
func AltMonoid[A any](zero L.Lazy[ReaderIOEither[A]]) M.Monoid[ReaderIOEither[A]] {
|
||||||
|
return M.AltMonoid(
|
||||||
|
zero,
|
||||||
|
MonadAlt[A],
|
||||||
|
)
|
||||||
|
}
|
@@ -19,27 +19,19 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
R "github.com/IBM/fp-go/context/reader"
|
|
||||||
RIO "github.com/IBM/fp-go/context/readerio"
|
|
||||||
G "github.com/IBM/fp-go/context/readerioeither/generic"
|
G "github.com/IBM/fp-go/context/readerioeither/generic"
|
||||||
ET "github.com/IBM/fp-go/either"
|
ET "github.com/IBM/fp-go/either"
|
||||||
IO "github.com/IBM/fp-go/io"
|
IO "github.com/IBM/fp-go/io"
|
||||||
IOE "github.com/IBM/fp-go/ioeither"
|
IOE "github.com/IBM/fp-go/ioeither"
|
||||||
|
L "github.com/IBM/fp-go/lazy"
|
||||||
O "github.com/IBM/fp-go/option"
|
O "github.com/IBM/fp-go/option"
|
||||||
|
RIO "github.com/IBM/fp-go/readerio"
|
||||||
)
|
)
|
||||||
|
|
||||||
func FromEither[A any](e ET.Either[error, A]) ReaderIOEither[A] {
|
func FromEither[A any](e ET.Either[error, A]) ReaderIOEither[A] {
|
||||||
return G.FromEither[ReaderIOEither[A]](e)
|
return G.FromEither[ReaderIOEither[A]](e)
|
||||||
}
|
}
|
||||||
|
|
||||||
func RightReader[A any](r R.Reader[A]) ReaderIOEither[A] {
|
|
||||||
return G.RightReader[ReaderIOEither[A]](r)
|
|
||||||
}
|
|
||||||
|
|
||||||
func LeftReader[A any](l R.Reader[error]) ReaderIOEither[A] {
|
|
||||||
return G.LeftReader[ReaderIOEither[A]](l)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Left[A any](l error) ReaderIOEither[A] {
|
func Left[A any](l error) ReaderIOEither[A] {
|
||||||
return G.Left[ReaderIOEither[A]](l)
|
return G.Left[ReaderIOEither[A]](l)
|
||||||
}
|
}
|
||||||
@@ -48,10 +40,6 @@ func Right[A any](r A) ReaderIOEither[A] {
|
|||||||
return G.Right[ReaderIOEither[A]](r)
|
return G.Right[ReaderIOEither[A]](r)
|
||||||
}
|
}
|
||||||
|
|
||||||
func FromReader[A any](r R.Reader[A]) ReaderIOEither[A] {
|
|
||||||
return G.FromReader[ReaderIOEither[A]](r)
|
|
||||||
}
|
|
||||||
|
|
||||||
func MonadMap[A, B any](fa ReaderIOEither[A], f func(A) B) ReaderIOEither[B] {
|
func MonadMap[A, B any](fa ReaderIOEither[A], f func(A) B) ReaderIOEither[B] {
|
||||||
return G.MonadMap[ReaderIOEither[A], ReaderIOEither[B]](fa, f)
|
return G.MonadMap[ReaderIOEither[A], ReaderIOEither[B]](fa, f)
|
||||||
}
|
}
|
||||||
@@ -60,6 +48,14 @@ func Map[A, B any](f func(A) B) func(ReaderIOEither[A]) ReaderIOEither[B] {
|
|||||||
return G.Map[ReaderIOEither[A], ReaderIOEither[B]](f)
|
return G.Map[ReaderIOEither[A], ReaderIOEither[B]](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func MonadMapTo[A, B any](fa ReaderIOEither[A], b B) ReaderIOEither[B] {
|
||||||
|
return G.MonadMapTo[ReaderIOEither[A], ReaderIOEither[B]](fa, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func MapTo[A, B any](b B) func(ReaderIOEither[A]) ReaderIOEither[B] {
|
||||||
|
return G.MapTo[ReaderIOEither[A], ReaderIOEither[B]](b)
|
||||||
|
}
|
||||||
|
|
||||||
func MonadChain[A, B any](ma ReaderIOEither[A], f func(A) ReaderIOEither[B]) ReaderIOEither[B] {
|
func MonadChain[A, B any](ma ReaderIOEither[A], f func(A) ReaderIOEither[B]) ReaderIOEither[B] {
|
||||||
return G.MonadChain(ma, f)
|
return G.MonadChain(ma, f)
|
||||||
}
|
}
|
||||||
@@ -94,30 +90,14 @@ func FromPredicate[A any](pred func(A) bool, onFalse func(A) error) func(A) Read
|
|||||||
return G.FromPredicate[ReaderIOEither[A]](pred, onFalse)
|
return G.FromPredicate[ReaderIOEither[A]](pred, onFalse)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Fold[A, B any](onLeft func(error) RIO.ReaderIO[B], onRight func(A) RIO.ReaderIO[B]) func(ReaderIOEither[A]) RIO.ReaderIO[B] {
|
|
||||||
return G.Fold[RIO.ReaderIO[B], ReaderIOEither[A]](onLeft, onRight)
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetOrElse[A any](onLeft func(error) RIO.ReaderIO[A]) func(ReaderIOEither[A]) RIO.ReaderIO[A] {
|
|
||||||
return G.GetOrElse[RIO.ReaderIO[A], ReaderIOEither[A]](onLeft)
|
|
||||||
}
|
|
||||||
|
|
||||||
func OrElse[A any](onLeft func(error) ReaderIOEither[A]) func(ReaderIOEither[A]) ReaderIOEither[A] {
|
func OrElse[A any](onLeft func(error) ReaderIOEither[A]) func(ReaderIOEither[A]) ReaderIOEither[A] {
|
||||||
return G.OrElse[ReaderIOEither[A]](onLeft)
|
return G.OrElse[ReaderIOEither[A]](onLeft)
|
||||||
}
|
}
|
||||||
|
|
||||||
func OrLeft[A any](onLeft func(error) RIO.ReaderIO[error]) func(ReaderIOEither[A]) ReaderIOEither[A] {
|
|
||||||
return G.OrLeft[ReaderIOEither[A], RIO.ReaderIO[error]](onLeft)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Ask() ReaderIOEither[context.Context] {
|
func Ask() ReaderIOEither[context.Context] {
|
||||||
return G.Ask[ReaderIOEither[context.Context]]()
|
return G.Ask[ReaderIOEither[context.Context]]()
|
||||||
}
|
}
|
||||||
|
|
||||||
func Asks[A any](r R.Reader[A]) ReaderIOEither[A] {
|
|
||||||
return G.Asks[ReaderIOEither[A]](r)
|
|
||||||
}
|
|
||||||
|
|
||||||
func MonadChainEitherK[A, B any](ma ReaderIOEither[A], f func(A) ET.Either[error, B]) ReaderIOEither[B] {
|
func MonadChainEitherK[A, B any](ma ReaderIOEither[A], f func(A) ET.Either[error, B]) ReaderIOEither[B] {
|
||||||
return G.MonadChainEitherK[ReaderIOEither[A], ReaderIOEither[B]](ma, f)
|
return G.MonadChainEitherK[ReaderIOEither[A], ReaderIOEither[B]](ma, f)
|
||||||
}
|
}
|
||||||
@@ -146,6 +126,10 @@ func FromIO[A any](t IO.IO[A]) ReaderIOEither[A] {
|
|||||||
return G.FromIO[ReaderIOEither[A]](t)
|
return G.FromIO[ReaderIOEither[A]](t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func FromLazy[A any](t L.Lazy[A]) ReaderIOEither[A] {
|
||||||
|
return G.FromIO[ReaderIOEither[A]](t)
|
||||||
|
}
|
||||||
|
|
||||||
// Never returns a 'ReaderIOEither' that never returns, except if its context gets canceled
|
// Never returns a 'ReaderIOEither' that never returns, except if its context gets canceled
|
||||||
func Never[A any]() ReaderIOEither[A] {
|
func Never[A any]() ReaderIOEither[A] {
|
||||||
return G.Never[ReaderIOEither[A]]()
|
return G.Never[ReaderIOEither[A]]()
|
||||||
@@ -182,7 +166,7 @@ func Timer(delay time.Duration) ReaderIOEither[time.Time] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Defer creates an IO by creating a brand new IO via a generator function, each time
|
// Defer creates an IO by creating a brand new IO via a generator function, each time
|
||||||
func Defer[A any](gen func() ReaderIOEither[A]) ReaderIOEither[A] {
|
func Defer[A any](gen L.Lazy[ReaderIOEither[A]]) ReaderIOEither[A] {
|
||||||
return G.Defer[ReaderIOEither[A]](gen)
|
return G.Defer[ReaderIOEither[A]](gen)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -190,3 +174,45 @@ func Defer[A any](gen func() ReaderIOEither[A]) ReaderIOEither[A] {
|
|||||||
func TryCatch[A any](f func(context.Context) func() (A, error)) ReaderIOEither[A] {
|
func TryCatch[A any](f func(context.Context) func() (A, error)) ReaderIOEither[A] {
|
||||||
return G.TryCatch[ReaderIOEither[A]](f)
|
return G.TryCatch[ReaderIOEither[A]](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MonadAlt identifies an associative operation on a type constructor
|
||||||
|
func MonadAlt[A any](first ReaderIOEither[A], second L.Lazy[ReaderIOEither[A]]) ReaderIOEither[A] {
|
||||||
|
return G.MonadAlt(first, second)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alt identifies an associative operation on a type constructor
|
||||||
|
func Alt[A any](second L.Lazy[ReaderIOEither[A]]) func(ReaderIOEither[A]) ReaderIOEither[A] {
|
||||||
|
return G.Alt(second)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Memoize computes the value of the provided [ReaderIOEither] monad lazily but exactly once
|
||||||
|
// The context used to compute the value is the context of the first call, so do not use this
|
||||||
|
// method if the value has a functional dependency on the content of the context
|
||||||
|
func Memoize[A any](rdr ReaderIOEither[A]) ReaderIOEither[A] {
|
||||||
|
return G.Memoize[ReaderIOEither[A]](rdr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flatten converts a nested [ReaderIOEither] into a [ReaderIOEither]
|
||||||
|
func Flatten[A any](rdr ReaderIOEither[ReaderIOEither[A]]) ReaderIOEither[A] {
|
||||||
|
return G.Flatten[ReaderIOEither[ReaderIOEither[A]]](rdr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func MonadFlap[B, A any](fab ReaderIOEither[func(A) B], a A) ReaderIOEither[B] {
|
||||||
|
return G.MonadFlap[ReaderIOEither[func(A) B], ReaderIOEither[B]](fab, a)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Flap[B, A any](a A) func(ReaderIOEither[func(A) B]) ReaderIOEither[B] {
|
||||||
|
return G.Flap[ReaderIOEither[func(A) B], ReaderIOEither[B]](a)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Fold[A, B any](onLeft func(error) ReaderIOEither[B], onRight func(A) ReaderIOEither[B]) func(ReaderIOEither[A]) ReaderIOEither[B] {
|
||||||
|
return G.Fold[ReaderIOEither[B], ReaderIOEither[A]](onLeft, onRight)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetOrElse[A any](onLeft func(error) RIO.ReaderIO[context.Context, A]) func(ReaderIOEither[A]) RIO.ReaderIO[context.Context, A] {
|
||||||
|
return G.GetOrElse[RIO.ReaderIO[context.Context, A], ReaderIOEither[A]](onLeft)
|
||||||
|
}
|
||||||
|
|
||||||
|
func OrLeft[A any](onLeft func(error) RIO.ReaderIO[context.Context, error]) func(ReaderIOEither[A]) ReaderIOEither[A] {
|
||||||
|
return G.OrLeft[ReaderIOEither[A]](onLeft)
|
||||||
|
}
|
||||||
|
@@ -162,3 +162,125 @@ func TestRegularApply(t *testing.T) {
|
|||||||
res := applied(context.Background())()
|
res := applied(context.Background())()
|
||||||
assert.Equal(t, E.Of[error]("CARSTEN"), res)
|
assert.Equal(t, E.Of[error]("CARSTEN"), res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestWithResourceNoErrors(t *testing.T) {
|
||||||
|
var countAcquire, countBody, countRelease int
|
||||||
|
|
||||||
|
acquire := FromLazy(func() int {
|
||||||
|
countAcquire++
|
||||||
|
return countAcquire
|
||||||
|
})
|
||||||
|
|
||||||
|
release := func(int) ReaderIOEither[int] {
|
||||||
|
return FromLazy(func() int {
|
||||||
|
countRelease++
|
||||||
|
return countRelease
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
body := func(int) ReaderIOEither[int] {
|
||||||
|
return FromLazy(func() int {
|
||||||
|
countBody++
|
||||||
|
return countBody
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
resRIOE := WithResource[int](acquire, release)(body)
|
||||||
|
|
||||||
|
res := resRIOE(context.Background())()
|
||||||
|
|
||||||
|
assert.Equal(t, 1, countAcquire)
|
||||||
|
assert.Equal(t, 1, countBody)
|
||||||
|
assert.Equal(t, 1, countRelease)
|
||||||
|
assert.Equal(t, E.Of[error](1), res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWithResourceErrorInBody(t *testing.T) {
|
||||||
|
var countAcquire, countBody, countRelease int
|
||||||
|
|
||||||
|
acquire := FromLazy(func() int {
|
||||||
|
countAcquire++
|
||||||
|
return countAcquire
|
||||||
|
})
|
||||||
|
|
||||||
|
release := func(int) ReaderIOEither[int] {
|
||||||
|
return FromLazy(func() int {
|
||||||
|
countRelease++
|
||||||
|
return countRelease
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
err := fmt.Errorf("error in body")
|
||||||
|
body := func(int) ReaderIOEither[int] {
|
||||||
|
return Left[int](err)
|
||||||
|
}
|
||||||
|
|
||||||
|
resRIOE := WithResource[int](acquire, release)(body)
|
||||||
|
|
||||||
|
res := resRIOE(context.Background())()
|
||||||
|
|
||||||
|
assert.Equal(t, 1, countAcquire)
|
||||||
|
assert.Equal(t, 0, countBody)
|
||||||
|
assert.Equal(t, 1, countRelease)
|
||||||
|
assert.Equal(t, E.Left[int](err), res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWithResourceErrorInAcquire(t *testing.T) {
|
||||||
|
var countAcquire, countBody, countRelease int
|
||||||
|
|
||||||
|
err := fmt.Errorf("error in acquire")
|
||||||
|
acquire := Left[int](err)
|
||||||
|
|
||||||
|
release := func(int) ReaderIOEither[int] {
|
||||||
|
return FromLazy(func() int {
|
||||||
|
countRelease++
|
||||||
|
return countRelease
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
body := func(int) ReaderIOEither[int] {
|
||||||
|
return FromLazy(func() int {
|
||||||
|
countBody++
|
||||||
|
return countBody
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
resRIOE := WithResource[int](acquire, release)(body)
|
||||||
|
|
||||||
|
res := resRIOE(context.Background())()
|
||||||
|
|
||||||
|
assert.Equal(t, 0, countAcquire)
|
||||||
|
assert.Equal(t, 0, countBody)
|
||||||
|
assert.Equal(t, 0, countRelease)
|
||||||
|
assert.Equal(t, E.Left[int](err), res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWithResourceErrorInRelease(t *testing.T) {
|
||||||
|
var countAcquire, countBody, countRelease int
|
||||||
|
|
||||||
|
acquire := FromLazy(func() int {
|
||||||
|
countAcquire++
|
||||||
|
return countAcquire
|
||||||
|
})
|
||||||
|
|
||||||
|
err := fmt.Errorf("error in release")
|
||||||
|
release := func(int) ReaderIOEither[int] {
|
||||||
|
return Left[int](err)
|
||||||
|
}
|
||||||
|
|
||||||
|
body := func(int) ReaderIOEither[int] {
|
||||||
|
return FromLazy(func() int {
|
||||||
|
countBody++
|
||||||
|
return countBody
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
resRIOE := WithResource[int](acquire, release)(body)
|
||||||
|
|
||||||
|
res := resRIOE(context.Background())()
|
||||||
|
|
||||||
|
assert.Equal(t, 1, countAcquire)
|
||||||
|
assert.Equal(t, 1, countBody)
|
||||||
|
assert.Equal(t, 0, countRelease)
|
||||||
|
assert.Equal(t, E.Left[int](err), res)
|
||||||
|
}
|
||||||
|
26
context/readerioeither/semigroup.go
Normal file
26
context/readerioeither/semigroup.go
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package readerioeither
|
||||||
|
|
||||||
|
import (
|
||||||
|
G "github.com/IBM/fp-go/context/readerioeither/generic"
|
||||||
|
S "github.com/IBM/fp-go/semigroup"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AltSemigroup is a [Semigroup] that tries the first item and then the second one using an alternative
|
||||||
|
func AltSemigroup[A any]() S.Semigroup[ReaderIOEither[A]] {
|
||||||
|
return G.AltSemigroup[ReaderIOEither[A]]()
|
||||||
|
}
|
@@ -13,14 +13,15 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
// Package reader implements a specialization of the Reader monad assuming a golang context as the context of the monad
|
package readerioeither
|
||||||
package reader
|
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
R "github.com/IBM/fp-go/reader"
|
G "github.com/IBM/fp-go/context/readerioeither/generic"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Reader is a specialization of the Reader monad assuming a golang context as the context of the monad
|
// WithLock executes the provided IO operation in the scope of a lock
|
||||||
type Reader[A any] R.Reader[context.Context, A]
|
func WithLock[A any](lock ReaderIOEither[context.CancelFunc]) func(fa ReaderIOEither[A]) ReaderIOEither[A] {
|
||||||
|
return G.WithLock[ReaderIOEither[A]](lock)
|
||||||
|
}
|
@@ -24,6 +24,11 @@ func TraverseArray[A, B any](f func(A) ReaderIOEither[B]) func([]A) ReaderIOEith
|
|||||||
return G.TraverseArray[[]A, ReaderIOEither[[]B]](f)
|
return G.TraverseArray[[]A, ReaderIOEither[[]B]](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TraverseArrayWithIndex uses transforms an array [[]A] into [[]ReaderIOEither[B]] and then resolves that into a [ReaderIOEither[[]B]]
|
||||||
|
func TraverseArrayWithIndex[A, B any](f func(int, A) ReaderIOEither[B]) func([]A) ReaderIOEither[[]B] {
|
||||||
|
return G.TraverseArrayWithIndex[[]A, ReaderIOEither[[]B]](f)
|
||||||
|
}
|
||||||
|
|
||||||
// SequenceArray converts a homogeneous sequence of either into an either of sequence
|
// SequenceArray converts a homogeneous sequence of either into an either of sequence
|
||||||
func SequenceArray[A any](ma []ReaderIOEither[A]) ReaderIOEither[[]A] {
|
func SequenceArray[A any](ma []ReaderIOEither[A]) ReaderIOEither[[]A] {
|
||||||
return G.SequenceArray[[]A, []ReaderIOEither[A], ReaderIOEither[[]A]](ma)
|
return G.SequenceArray[[]A, []ReaderIOEither[A], ReaderIOEither[[]A]](ma)
|
||||||
@@ -34,7 +39,72 @@ func TraverseRecord[K comparable, A, B any](f func(A) ReaderIOEither[B]) func(ma
|
|||||||
return G.TraverseRecord[K, map[K]A, ReaderIOEither[map[K]B]](f)
|
return G.TraverseRecord[K, map[K]A, ReaderIOEither[map[K]B]](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TraverseRecordWithIndex uses transforms a record [map[K]A] into [map[K]ReaderIOEither[B]] and then resolves that into a [ReaderIOEither[map[K]B]]
|
||||||
|
func TraverseRecordWithIndex[K comparable, A, B any](f func(K, A) ReaderIOEither[B]) func(map[K]A) ReaderIOEither[map[K]B] {
|
||||||
|
return G.TraverseRecordWithIndex[K, map[K]A, ReaderIOEither[map[K]B]](f)
|
||||||
|
}
|
||||||
|
|
||||||
// SequenceRecord converts a homogeneous sequence of either into an either of sequence
|
// SequenceRecord converts a homogeneous sequence of either into an either of sequence
|
||||||
func SequenceRecord[K comparable, A any](ma map[K]ReaderIOEither[A]) ReaderIOEither[map[K]A] {
|
func SequenceRecord[K comparable, A any](ma map[K]ReaderIOEither[A]) ReaderIOEither[map[K]A] {
|
||||||
return G.SequenceRecord[K, map[K]A, map[K]ReaderIOEither[A], ReaderIOEither[map[K]A]](ma)
|
return G.SequenceRecord[K, map[K]A, map[K]ReaderIOEither[A], ReaderIOEither[map[K]A]](ma)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TraverseArraySeq uses transforms an array [[]A] into [[]ReaderIOEither[B]] and then resolves that into a [ReaderIOEither[[]B]]
|
||||||
|
func TraverseArraySeq[A, B any](f func(A) ReaderIOEither[B]) func([]A) ReaderIOEither[[]B] {
|
||||||
|
return G.TraverseArraySeq[[]A, ReaderIOEither[[]B]](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TraverseArrayWithIndexSeq uses transforms an array [[]A] into [[]ReaderIOEither[B]] and then resolves that into a [ReaderIOEither[[]B]]
|
||||||
|
func TraverseArrayWithIndexSeq[A, B any](f func(int, A) ReaderIOEither[B]) func([]A) ReaderIOEither[[]B] {
|
||||||
|
return G.TraverseArrayWithIndexSeq[[]A, ReaderIOEither[[]B]](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SequenceArraySeq converts a homogeneous sequence of either into an either of sequence
|
||||||
|
func SequenceArraySeq[A any](ma []ReaderIOEither[A]) ReaderIOEither[[]A] {
|
||||||
|
return G.SequenceArraySeq[[]A, []ReaderIOEither[A], ReaderIOEither[[]A]](ma)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TraverseRecordSeq uses transforms a record [map[K]A] into [map[K]ReaderIOEither[B]] and then resolves that into a [ReaderIOEither[map[K]B]]
|
||||||
|
func TraverseRecordSeq[K comparable, A, B any](f func(A) ReaderIOEither[B]) func(map[K]A) ReaderIOEither[map[K]B] {
|
||||||
|
return G.TraverseRecordSeq[K, map[K]A, ReaderIOEither[map[K]B]](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TraverseRecordWithIndexSeq uses transforms a record [map[K]A] into [map[K]ReaderIOEither[B]] and then resolves that into a [ReaderIOEither[map[K]B]]
|
||||||
|
func TraverseRecordWithIndexSeq[K comparable, A, B any](f func(K, A) ReaderIOEither[B]) func(map[K]A) ReaderIOEither[map[K]B] {
|
||||||
|
return G.TraverseRecordWithIndexSeq[K, map[K]A, ReaderIOEither[map[K]B]](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SequenceRecordSeq converts a homogeneous sequence of either into an either of sequence
|
||||||
|
func SequenceRecordSeq[K comparable, A any](ma map[K]ReaderIOEither[A]) ReaderIOEither[map[K]A] {
|
||||||
|
return G.SequenceRecordSeq[K, map[K]A, map[K]ReaderIOEither[A], ReaderIOEither[map[K]A]](ma)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TraverseArrayPar uses transforms an array [[]A] into [[]ReaderIOEither[B]] and then resolves that into a [ReaderIOEither[[]B]]
|
||||||
|
func TraverseArrayPar[A, B any](f func(A) ReaderIOEither[B]) func([]A) ReaderIOEither[[]B] {
|
||||||
|
return G.TraverseArrayPar[[]A, ReaderIOEither[[]B]](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TraverseArrayWithIndexPar uses transforms an array [[]A] into [[]ReaderIOEither[B]] and then resolves that into a [ReaderIOEither[[]B]]
|
||||||
|
func TraverseArrayWithIndexPar[A, B any](f func(int, A) ReaderIOEither[B]) func([]A) ReaderIOEither[[]B] {
|
||||||
|
return G.TraverseArrayWithIndexPar[[]A, ReaderIOEither[[]B]](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SequenceArrayPar converts a homogeneous sequence of either into an either of sequence
|
||||||
|
func SequenceArrayPar[A any](ma []ReaderIOEither[A]) ReaderIOEither[[]A] {
|
||||||
|
return G.SequenceArrayPar[[]A, []ReaderIOEither[A], ReaderIOEither[[]A]](ma)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TraverseRecordPar uses transforms a record [map[K]A] into [map[K]ReaderIOEither[B]] and then resolves that into a [ReaderIOEither[map[K]B]]
|
||||||
|
func TraverseRecordPar[K comparable, A, B any](f func(A) ReaderIOEither[B]) func(map[K]A) ReaderIOEither[map[K]B] {
|
||||||
|
return G.TraverseRecordPar[K, map[K]A, ReaderIOEither[map[K]B]](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TraverseRecordWithIndexPar uses transforms a record [map[K]A] into [map[K]ReaderIOEither[B]] and then resolves that into a [ReaderIOEither[map[K]B]]
|
||||||
|
func TraverseRecordWithIndexPar[K comparable, A, B any](f func(K, A) ReaderIOEither[B]) func(map[K]A) ReaderIOEither[map[K]B] {
|
||||||
|
return G.TraverseRecordWithIndexPar[K, map[K]A, ReaderIOEither[map[K]B]](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SequenceRecordPar converts a homogeneous sequence of either into an either of sequence
|
||||||
|
func SequenceRecordPar[K comparable, A any](ma map[K]ReaderIOEither[A]) ReaderIOEither[map[K]A] {
|
||||||
|
return G.SequenceRecordPar[K, map[K]A, map[K]ReaderIOEither[A], ReaderIOEither[map[K]A]](ma)
|
||||||
|
}
|
||||||
|
3
coverage.bat
Normal file
3
coverage.bat
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
@echo off
|
||||||
|
go tool cover -html=build/cover.out -o build/cover.html
|
||||||
|
cov-report -ex ".*/cli/.*.go|.*/gen.go|.*/binds.go" build\cover.out
|
38
di/app.go
Normal file
38
di/app.go
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package di
|
||||||
|
|
||||||
|
import (
|
||||||
|
DIE "github.com/IBM/fp-go/di/erasure"
|
||||||
|
F "github.com/IBM/fp-go/function"
|
||||||
|
IO "github.com/IBM/fp-go/io"
|
||||||
|
IOE "github.com/IBM/fp-go/ioeither"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// InjMain is the [InjectionToken] for the main application
|
||||||
|
InjMain = MakeToken[any]("APP")
|
||||||
|
|
||||||
|
// Main is the resolver for the main application
|
||||||
|
Main = Resolve(InjMain)
|
||||||
|
)
|
||||||
|
|
||||||
|
// RunMain runs the main application from a set of [DIE.Provider]s
|
||||||
|
var RunMain = F.Flow3(
|
||||||
|
DIE.MakeInjector,
|
||||||
|
Main,
|
||||||
|
IOE.Fold(IO.Of[error], F.Constant1[any](IO.Of[error](nil))),
|
||||||
|
)
|
43
di/doc.go
Normal file
43
di/doc.go
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// Package di implements functions and data types supporting dependency injection patterns
|
||||||
|
//
|
||||||
|
// The fundamental building block is the concept of a [Dependency]. This describes the abstract concept of a function, service or value together with its type.
|
||||||
|
// Examples for dependencies can be as simple as configuration values such as the API URL for a service, the current username, a [Dependency] could be the map
|
||||||
|
// of the configuration environment, an http client or as complex as a service interface. Important is that a [Dependency] only defines the concept but
|
||||||
|
// not the implementation.
|
||||||
|
//
|
||||||
|
// The implementation of a [Dependency] is called a [Provider], the dependency of an `API URL` could e.g. be realized by a provider that consults the environment to read the information
|
||||||
|
// or a config file or simply hardcode it.
|
||||||
|
// In many cases the implementation of a [Provider] depends in turn on other [Dependency] (but never directly on other [Provider]s), a provider for an `API URL` that reads
|
||||||
|
// the information from the environment would e.g. depend on a [Dependency] that represents this environment.
|
||||||
|
//
|
||||||
|
// It is the resposibility of the [InjectableFactory] to create an instance of a [Dependency]. All instances are considered singletons. Create an [InjectableFactory] via the [MakeInjector] method. Use [Resolve] to create a strongly typed
|
||||||
|
// factory for a particular [InjectionToken].
|
||||||
|
//
|
||||||
|
// In most cases it is not required to use [InjectableFactory] directly, instead you would create a [Provider] via the [MakeProvider2] method (suffix indicates the number of dependencies). In this call
|
||||||
|
// you give a number of (strongly typed) [Dependency] identifiers and a (strongly typed) factory function for the implementation. The dependency injection framework makes
|
||||||
|
// sure to resolve the dependencies before calling the factory method.
|
||||||
|
//
|
||||||
|
// For convenience purposes it can be helpful to attach a default implementation of a [Dependency]. In this case use the [MakeTokenWithDefault2] method (suffix indicates the number of dependencies)
|
||||||
|
// to define the [InjectionToken].
|
||||||
|
//
|
||||||
|
// [Provider]: [github.com/IBM/fp-go/di/erasure.Provider]
|
||||||
|
// [InjectableFactory]: [github.com/IBM/fp-go/di/erasure.InjectableFactory]
|
||||||
|
// [MakeInjector]: [github.com/IBM/fp-go/di/erasure.MakeInjector]
|
||||||
|
package di
|
||||||
|
|
||||||
|
//go:generate go run .. di --count 15 --filename gen.go
|
170
di/erasure/injector.go
Normal file
170
di/erasure/injector.go
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package erasure
|
||||||
|
|
||||||
|
import (
|
||||||
|
A "github.com/IBM/fp-go/array"
|
||||||
|
"github.com/IBM/fp-go/errors"
|
||||||
|
F "github.com/IBM/fp-go/function"
|
||||||
|
I "github.com/IBM/fp-go/identity"
|
||||||
|
IG "github.com/IBM/fp-go/identity/generic"
|
||||||
|
IOE "github.com/IBM/fp-go/ioeither"
|
||||||
|
L "github.com/IBM/fp-go/lazy"
|
||||||
|
O "github.com/IBM/fp-go/option"
|
||||||
|
R "github.com/IBM/fp-go/record"
|
||||||
|
T "github.com/IBM/fp-go/tuple"
|
||||||
|
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
func providerToEntry(p Provider) T.Tuple2[string, ProviderFactory] {
|
||||||
|
return T.MakeTuple2(p.Provides().Id(), p.Factory())
|
||||||
|
}
|
||||||
|
|
||||||
|
func itemProviderToMap(p Provider) map[string][]ProviderFactory {
|
||||||
|
return R.Singleton(p.Provides().Id(), A.Of(p.Factory()))
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
// missingProviderError returns a [ProviderFactory] that fails due to a missing dependency
|
||||||
|
missingProviderError = F.Flow4(
|
||||||
|
Dependency.String,
|
||||||
|
errors.OnSome[string]("no provider for dependency [%s]"),
|
||||||
|
IOE.Left[any, error],
|
||||||
|
F.Constant1[InjectableFactory, IOE.IOEither[error, any]],
|
||||||
|
)
|
||||||
|
|
||||||
|
// missingProviderErrorOrDefault returns the default [ProviderFactory] or an error
|
||||||
|
missingProviderErrorOrDefault = F.Flow3(
|
||||||
|
T.Replicate2[Dependency],
|
||||||
|
T.Map2(Dependency.ProviderFactory, F.Flow2(missingProviderError, F.Constant[ProviderFactory])),
|
||||||
|
T.Tupled2(O.MonadGetOrElse[ProviderFactory]),
|
||||||
|
)
|
||||||
|
|
||||||
|
emptyMulti any = A.Empty[any]()
|
||||||
|
|
||||||
|
// emptyMultiDependency returns a [ProviderFactory] for an empty, multi dependency
|
||||||
|
emptyMultiDependency = F.Constant1[Dependency](F.Constant1[InjectableFactory](IOE.Of[error](emptyMulti)))
|
||||||
|
|
||||||
|
// handleMissingProvider covers the case of a missing provider. It either
|
||||||
|
// returns an error or an empty multi value provider
|
||||||
|
handleMissingProvider = F.Flow2(
|
||||||
|
F.Ternary(isMultiDependency, emptyMultiDependency, missingProviderErrorOrDefault),
|
||||||
|
F.Constant[ProviderFactory],
|
||||||
|
)
|
||||||
|
|
||||||
|
// mergeItemProviders is a monoid for item provider factories
|
||||||
|
mergeItemProviders = R.UnionMonoid[string](A.Semigroup[ProviderFactory]())
|
||||||
|
|
||||||
|
// mergeProviders is a monoid for provider factories
|
||||||
|
mergeProviders = R.UnionLastMonoid[string, ProviderFactory]()
|
||||||
|
|
||||||
|
// collectItemProviders create a provider map for item providers
|
||||||
|
collectItemProviders = F.Flow2(
|
||||||
|
A.FoldMap[Provider](mergeItemProviders)(itemProviderToMap),
|
||||||
|
R.Map[string](itemProviderFactory),
|
||||||
|
)
|
||||||
|
|
||||||
|
// collectProviders collects non-item providers
|
||||||
|
collectProviders = F.Flow2(
|
||||||
|
A.Map(providerToEntry),
|
||||||
|
R.FromEntries[string, ProviderFactory],
|
||||||
|
)
|
||||||
|
|
||||||
|
// assembleProviders constructs the provider map for item and non-item providers
|
||||||
|
assembleProviders = F.Flow3(
|
||||||
|
A.Partition(isItemProvider),
|
||||||
|
T.Map2(collectProviders, collectItemProviders),
|
||||||
|
T.Tupled2(mergeProviders.Concat),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
// isMultiDependency tests if a dependency is a container dependency
|
||||||
|
func isMultiDependency(dep Dependency) bool {
|
||||||
|
return dep.Flag()&Multi == Multi
|
||||||
|
}
|
||||||
|
|
||||||
|
// isItemProvider tests if a provivder provides a single item
|
||||||
|
func isItemProvider(provider Provider) bool {
|
||||||
|
return provider.Provides().Flag()&Item == Item
|
||||||
|
}
|
||||||
|
|
||||||
|
// itemProviderFactory combines multiple factories into one, returning an array
|
||||||
|
func itemProviderFactory(fcts []ProviderFactory) ProviderFactory {
|
||||||
|
return func(inj InjectableFactory) IOE.IOEither[error, any] {
|
||||||
|
return F.Pipe2(
|
||||||
|
fcts,
|
||||||
|
IOE.TraverseArray(I.Flap[IOE.IOEither[error, any]](inj)),
|
||||||
|
IOE.Map[error](F.ToAny[[]any]),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeInjector creates an [InjectableFactory] based on a set of [Provider]s
|
||||||
|
//
|
||||||
|
// The resulting [InjectableFactory] can then be used to retrieve service instances given their [Dependency]. The implementation
|
||||||
|
// makes sure to transitively resolve the required dependencies.
|
||||||
|
func MakeInjector(providers []Provider) InjectableFactory {
|
||||||
|
|
||||||
|
type Result = IOE.IOEither[error, any]
|
||||||
|
type LazyResult = L.Lazy[Result]
|
||||||
|
|
||||||
|
// resolved stores the values resolved so far, key is the string ID
|
||||||
|
// of the token, value is a lazy result
|
||||||
|
var resolved sync.Map
|
||||||
|
|
||||||
|
// provide a mapping for all providers
|
||||||
|
factoryByID := assembleProviders(providers)
|
||||||
|
|
||||||
|
// the actual factory, we need lazy initialization
|
||||||
|
var injFct InjectableFactory
|
||||||
|
|
||||||
|
// lazy initialization, so we can cross reference it
|
||||||
|
injFct = func(token Dependency) Result {
|
||||||
|
|
||||||
|
key := token.Id()
|
||||||
|
|
||||||
|
// according to https://github.com/golang/go/issues/44159 this
|
||||||
|
// is the best way to use the sync map
|
||||||
|
actual, loaded := resolved.Load(key)
|
||||||
|
if !loaded {
|
||||||
|
|
||||||
|
computeResult := L.MakeLazy(func() Result {
|
||||||
|
return F.Pipe5(
|
||||||
|
token,
|
||||||
|
T.Replicate2[Dependency],
|
||||||
|
T.Map2(F.Flow3(
|
||||||
|
Dependency.Id,
|
||||||
|
R.Lookup[ProviderFactory, string],
|
||||||
|
I.Ap[O.Option[ProviderFactory]](factoryByID),
|
||||||
|
), handleMissingProvider),
|
||||||
|
T.Tupled2(O.MonadGetOrElse[ProviderFactory]),
|
||||||
|
IG.Ap[ProviderFactory](injFct),
|
||||||
|
IOE.Memoize[error, any],
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
actual, _ = resolved.LoadOrStore(key, F.Pipe1(
|
||||||
|
computeResult,
|
||||||
|
L.Memoize[Result],
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
return actual.(LazyResult)()
|
||||||
|
}
|
||||||
|
|
||||||
|
return injFct
|
||||||
|
}
|
181
di/erasure/provider.go
Normal file
181
di/erasure/provider.go
Normal file
@@ -0,0 +1,181 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package erasure
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
A "github.com/IBM/fp-go/array"
|
||||||
|
E "github.com/IBM/fp-go/either"
|
||||||
|
F "github.com/IBM/fp-go/function"
|
||||||
|
I "github.com/IBM/fp-go/identity"
|
||||||
|
IO "github.com/IBM/fp-go/io"
|
||||||
|
IOG "github.com/IBM/fp-go/io/generic"
|
||||||
|
IOE "github.com/IBM/fp-go/ioeither"
|
||||||
|
IOO "github.com/IBM/fp-go/iooption"
|
||||||
|
Int "github.com/IBM/fp-go/number/integer"
|
||||||
|
O "github.com/IBM/fp-go/option"
|
||||||
|
R "github.com/IBM/fp-go/record"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
// InjectableFactory is a factory function that can create an untyped instance of a service based on its [Dependency] identifier
|
||||||
|
InjectableFactory = func(Dependency) IOE.IOEither[error, any]
|
||||||
|
ProviderFactory = func(InjectableFactory) IOE.IOEither[error, any]
|
||||||
|
|
||||||
|
paramIndex = map[int]int
|
||||||
|
paramValue = map[int]any
|
||||||
|
handler = func(paramIndex) func([]IOE.IOEither[error, any]) IOE.IOEither[error, paramValue]
|
||||||
|
mapping = map[int]paramIndex
|
||||||
|
|
||||||
|
Provider interface {
|
||||||
|
fmt.Stringer
|
||||||
|
// Provides returns the [Dependency] implemented by this provider
|
||||||
|
Provides() Dependency
|
||||||
|
// Factory returns s function that can create an instance of the dependency based on an [InjectableFactory]
|
||||||
|
Factory() ProviderFactory
|
||||||
|
}
|
||||||
|
|
||||||
|
provider struct {
|
||||||
|
provides Dependency
|
||||||
|
factory ProviderFactory
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func (p *provider) Provides() Dependency {
|
||||||
|
return p.provides
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *provider) Factory() ProviderFactory {
|
||||||
|
return p.factory
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *provider) String() string {
|
||||||
|
return fmt.Sprintf("Provider for [%s]", p.provides)
|
||||||
|
}
|
||||||
|
|
||||||
|
func MakeProvider(token Dependency, fct ProviderFactory) Provider {
|
||||||
|
return &provider{token, fct}
|
||||||
|
}
|
||||||
|
|
||||||
|
func mapFromToken(idx int, token Dependency) map[int]paramIndex {
|
||||||
|
return R.Singleton(token.Flag()&BehaviourMask, R.Singleton(idx, idx))
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Empty is the empty array of providers
|
||||||
|
Empty = A.Empty[Provider]()
|
||||||
|
|
||||||
|
mergeTokenMaps = R.UnionMonoid[int](R.UnionLastSemigroup[int, int]())
|
||||||
|
foldDeps = A.FoldMapWithIndex[Dependency](mergeTokenMaps)(mapFromToken)
|
||||||
|
mergeMaps = R.UnionLastMonoid[int, any]()
|
||||||
|
collectParams = R.CollectOrd[any, any](Int.Ord)(F.SK[int, any])
|
||||||
|
|
||||||
|
mapDeps = F.Curry2(A.MonadMap[Dependency, IOE.IOEither[error, any]])
|
||||||
|
|
||||||
|
handlers = map[int]handler{
|
||||||
|
Identity: func(mp paramIndex) func([]IOE.IOEither[error, any]) IOE.IOEither[error, paramValue] {
|
||||||
|
return func(res []IOE.IOEither[error, any]) IOE.IOEither[error, paramValue] {
|
||||||
|
return F.Pipe1(
|
||||||
|
mp,
|
||||||
|
IOE.TraverseRecord[int](getAt(res)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Option: func(mp paramIndex) func([]IOE.IOEither[error, any]) IOE.IOEither[error, paramValue] {
|
||||||
|
return func(res []IOE.IOEither[error, any]) IOE.IOEither[error, paramValue] {
|
||||||
|
return F.Pipe3(
|
||||||
|
mp,
|
||||||
|
IOG.TraverseRecord[IO.IO[map[int]E.Either[error, any]], paramIndex](getAt(res)),
|
||||||
|
IO.Map(R.Map[int](F.Flow2(
|
||||||
|
E.ToOption[error, any],
|
||||||
|
F.ToAny[O.Option[any]],
|
||||||
|
))),
|
||||||
|
IOE.FromIO[error, paramValue],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
IOEither: func(mp paramIndex) func([]IOE.IOEither[error, any]) IOE.IOEither[error, paramValue] {
|
||||||
|
return func(res []IOE.IOEither[error, any]) IOE.IOEither[error, paramValue] {
|
||||||
|
return F.Pipe2(
|
||||||
|
mp,
|
||||||
|
R.Map[int](F.Flow2(
|
||||||
|
getAt(res),
|
||||||
|
F.ToAny[IOE.IOEither[error, any]],
|
||||||
|
)),
|
||||||
|
IOE.Of[error, paramValue],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
IOOption: func(mp paramIndex) func([]IOE.IOEither[error, any]) IOE.IOEither[error, paramValue] {
|
||||||
|
return func(res []IOE.IOEither[error, any]) IOE.IOEither[error, paramValue] {
|
||||||
|
return F.Pipe2(
|
||||||
|
mp,
|
||||||
|
R.Map[int](F.Flow3(
|
||||||
|
getAt(res),
|
||||||
|
IOE.ToIOOption[error, any],
|
||||||
|
F.ToAny[IOO.IOOption[any]],
|
||||||
|
)),
|
||||||
|
IOE.Of[error, paramValue],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func getAt[T any](ar []T) func(idx int) T {
|
||||||
|
return func(idx int) T {
|
||||||
|
return ar[idx]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleMapping(mp mapping) func(res []IOE.IOEither[error, any]) IOE.IOEither[error, []any] {
|
||||||
|
preFct := F.Pipe1(
|
||||||
|
mp,
|
||||||
|
R.Collect(func(idx int, p paramIndex) func([]IOE.IOEither[error, any]) IOE.IOEither[error, paramValue] {
|
||||||
|
return handlers[idx](p)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
doFct := F.Flow2(
|
||||||
|
I.Flap[IOE.IOEither[error, paramValue], []IOE.IOEither[error, any]],
|
||||||
|
IOE.TraverseArray[error, func([]IOE.IOEither[error, any]) IOE.IOEither[error, paramValue], paramValue],
|
||||||
|
)
|
||||||
|
postFct := IOE.Map[error](F.Flow2(
|
||||||
|
A.Fold(mergeMaps),
|
||||||
|
collectParams,
|
||||||
|
))
|
||||||
|
|
||||||
|
return func(res []IOE.IOEither[error, any]) IOE.IOEither[error, []any] {
|
||||||
|
return F.Pipe2(
|
||||||
|
preFct,
|
||||||
|
doFct(res),
|
||||||
|
postFct,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeProviderFactory constructs a [ProviderFactory] based on a set of [Dependency]s and
|
||||||
|
// a function that accepts the resolved dependencies to return a result
|
||||||
|
func MakeProviderFactory(
|
||||||
|
deps []Dependency,
|
||||||
|
fct func(param ...any) IOE.IOEither[error, any]) ProviderFactory {
|
||||||
|
|
||||||
|
return F.Flow3(
|
||||||
|
mapDeps(deps),
|
||||||
|
handleMapping(foldDeps(deps)),
|
||||||
|
IOE.Chain(F.Unvariadic0(fct)),
|
||||||
|
)
|
||||||
|
}
|
45
di/erasure/token.go
Normal file
45
di/erasure/token.go
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package erasure
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
O "github.com/IBM/fp-go/option"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
BehaviourMask = 0x0f
|
||||||
|
Identity = 0 // required dependency
|
||||||
|
Option = 1 // optional dependency
|
||||||
|
IOEither = 2 // lazy and required
|
||||||
|
IOOption = 3 // lazy and optional
|
||||||
|
|
||||||
|
TypeMask = 0xf0
|
||||||
|
Multi = 1 << 4 // array of implementations
|
||||||
|
Item = 2 << 4 // item of a multi token
|
||||||
|
)
|
||||||
|
|
||||||
|
// Dependency describes the relationship to a service
|
||||||
|
type Dependency interface {
|
||||||
|
fmt.Stringer
|
||||||
|
// Id returns a unique identifier for a token that can be used as a cache key
|
||||||
|
Id() string
|
||||||
|
// Flag returns a tag that identifies the behaviour of the dependency
|
||||||
|
Flag() int
|
||||||
|
// ProviderFactory optionally returns an attached [ProviderFactory] that represents the default for this dependency
|
||||||
|
ProviderFactory() O.Option[ProviderFactory]
|
||||||
|
}
|
32
di/injector.go
Normal file
32
di/injector.go
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package di
|
||||||
|
|
||||||
|
import (
|
||||||
|
DIE "github.com/IBM/fp-go/di/erasure"
|
||||||
|
F "github.com/IBM/fp-go/function"
|
||||||
|
IG "github.com/IBM/fp-go/identity/generic"
|
||||||
|
IOE "github.com/IBM/fp-go/ioeither"
|
||||||
|
RIOE "github.com/IBM/fp-go/readerioeither"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Resolve performs a type safe resolution of a dependency
|
||||||
|
func Resolve[T any](token InjectionToken[T]) RIOE.ReaderIOEither[DIE.InjectableFactory, error, T] {
|
||||||
|
return F.Flow2(
|
||||||
|
IG.Ap[DIE.InjectableFactory](asDependency(token)),
|
||||||
|
IOE.ChainEitherK(token.Unerase),
|
||||||
|
)
|
||||||
|
}
|
79
di/provider.go
Normal file
79
di/provider.go
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package di
|
||||||
|
|
||||||
|
import (
|
||||||
|
A "github.com/IBM/fp-go/array"
|
||||||
|
DIE "github.com/IBM/fp-go/di/erasure"
|
||||||
|
E "github.com/IBM/fp-go/either"
|
||||||
|
"github.com/IBM/fp-go/errors"
|
||||||
|
F "github.com/IBM/fp-go/function"
|
||||||
|
IOE "github.com/IBM/fp-go/ioeither"
|
||||||
|
)
|
||||||
|
|
||||||
|
func lookupAt[T any](idx int, token Dependency[T]) func(params []any) E.Either[error, T] {
|
||||||
|
return F.Flow3(
|
||||||
|
A.Lookup[any](idx),
|
||||||
|
E.FromOption[any](errors.OnNone("No parameter at position %d", idx)),
|
||||||
|
E.Chain(token.Unerase),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func eraseTuple[A, R any](f func(A) IOE.IOEither[error, R]) func(E.Either[error, A]) IOE.IOEither[error, any] {
|
||||||
|
return F.Flow3(
|
||||||
|
IOE.FromEither[error, A],
|
||||||
|
IOE.Chain(f),
|
||||||
|
IOE.Map[error](F.ToAny[R]),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func eraseProviderFactory0[R any](f IOE.IOEither[error, R]) func(params ...any) IOE.IOEither[error, any] {
|
||||||
|
return func(_ ...any) IOE.IOEither[error, any] {
|
||||||
|
return F.Pipe1(
|
||||||
|
f,
|
||||||
|
IOE.Map[error](F.ToAny[R]),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func MakeProviderFactory0[R any](
|
||||||
|
fct IOE.IOEither[error, R],
|
||||||
|
) DIE.ProviderFactory {
|
||||||
|
return DIE.MakeProviderFactory(
|
||||||
|
A.Empty[DIE.Dependency](),
|
||||||
|
eraseProviderFactory0(fct),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeTokenWithDefault0 creates a unique [InjectionToken] for a specific type with an attached default [DIE.Provider]
|
||||||
|
func MakeTokenWithDefault0[R any](name string, fct IOE.IOEither[error, R]) InjectionToken[R] {
|
||||||
|
return MakeTokenWithDefault[R](name, MakeProviderFactory0(fct))
|
||||||
|
}
|
||||||
|
|
||||||
|
func MakeProvider0[R any](
|
||||||
|
token InjectionToken[R],
|
||||||
|
fct IOE.IOEither[error, R],
|
||||||
|
) DIE.Provider {
|
||||||
|
return DIE.MakeProvider(
|
||||||
|
token,
|
||||||
|
MakeProviderFactory0(fct),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConstProvider simple implementation for a provider with a constant value
|
||||||
|
func ConstProvider[R any](token InjectionToken[R], value R) DIE.Provider {
|
||||||
|
return MakeProvider0[R](token, IOE.Of[error](value))
|
||||||
|
}
|
346
di/provider_test.go
Normal file
346
di/provider_test.go
Normal file
@@ -0,0 +1,346 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package di
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
A "github.com/IBM/fp-go/array"
|
||||||
|
DIE "github.com/IBM/fp-go/di/erasure"
|
||||||
|
E "github.com/IBM/fp-go/either"
|
||||||
|
F "github.com/IBM/fp-go/function"
|
||||||
|
IOE "github.com/IBM/fp-go/ioeither"
|
||||||
|
O "github.com/IBM/fp-go/option"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
INJ_KEY2 = MakeToken[string]("INJ_KEY2")
|
||||||
|
INJ_KEY1 = MakeToken[string]("INJ_KEY1")
|
||||||
|
INJ_KEY3 = MakeToken[string]("INJ_KEY3")
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSimpleProvider(t *testing.T) {
|
||||||
|
|
||||||
|
var staticCount int
|
||||||
|
|
||||||
|
staticValue := func(value string) IOE.IOEither[error, string] {
|
||||||
|
return func() E.Either[error, string] {
|
||||||
|
staticCount++
|
||||||
|
return E.Of[error](fmt.Sprintf("Static based on [%s], at [%s]", value, time.Now()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var dynamicCount int
|
||||||
|
|
||||||
|
dynamicValue := func(value string) IOE.IOEither[error, string] {
|
||||||
|
return func() E.Either[error, string] {
|
||||||
|
dynamicCount++
|
||||||
|
return E.Of[error](fmt.Sprintf("Dynamic based on [%s] at [%s]", value, time.Now()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p1 := MakeProvider0(INJ_KEY1, staticValue("Carsten"))
|
||||||
|
p2 := MakeProvider1(INJ_KEY2, INJ_KEY1.Identity(), dynamicValue)
|
||||||
|
|
||||||
|
inj := DIE.MakeInjector(A.From(p1, p2))
|
||||||
|
|
||||||
|
i1 := Resolve(INJ_KEY1)
|
||||||
|
i2 := Resolve(INJ_KEY2)
|
||||||
|
|
||||||
|
res := IOE.SequenceT4(
|
||||||
|
i2(inj),
|
||||||
|
i1(inj),
|
||||||
|
i2(inj),
|
||||||
|
i1(inj),
|
||||||
|
)
|
||||||
|
|
||||||
|
r := res()
|
||||||
|
|
||||||
|
assert.True(t, E.IsRight(r))
|
||||||
|
assert.Equal(t, 1, staticCount)
|
||||||
|
assert.Equal(t, 1, dynamicCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOptionalProvider(t *testing.T) {
|
||||||
|
|
||||||
|
var staticCount int
|
||||||
|
|
||||||
|
staticValue := func(value string) IOE.IOEither[error, string] {
|
||||||
|
return func() E.Either[error, string] {
|
||||||
|
staticCount++
|
||||||
|
return E.Of[error](fmt.Sprintf("Static based on [%s], at [%s]", value, time.Now()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var dynamicCount int
|
||||||
|
|
||||||
|
dynamicValue := func(value O.Option[string]) IOE.IOEither[error, string] {
|
||||||
|
return func() E.Either[error, string] {
|
||||||
|
dynamicCount++
|
||||||
|
return E.Of[error](fmt.Sprintf("Dynamic based on [%s] at [%s]", value, time.Now()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p1 := MakeProvider0(INJ_KEY1, staticValue("Carsten"))
|
||||||
|
p2 := MakeProvider1(INJ_KEY2, INJ_KEY1.Option(), dynamicValue)
|
||||||
|
|
||||||
|
inj := DIE.MakeInjector(A.From(p1, p2))
|
||||||
|
|
||||||
|
i1 := Resolve(INJ_KEY1)
|
||||||
|
i2 := Resolve(INJ_KEY2)
|
||||||
|
|
||||||
|
res := IOE.SequenceT4(
|
||||||
|
i2(inj),
|
||||||
|
i1(inj),
|
||||||
|
i2(inj),
|
||||||
|
i1(inj),
|
||||||
|
)
|
||||||
|
|
||||||
|
r := res()
|
||||||
|
|
||||||
|
assert.True(t, E.IsRight(r))
|
||||||
|
assert.Equal(t, 1, staticCount)
|
||||||
|
assert.Equal(t, 1, dynamicCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOptionalProviderMissingDependency(t *testing.T) {
|
||||||
|
|
||||||
|
var dynamicCount int
|
||||||
|
|
||||||
|
dynamicValue := func(value O.Option[string]) IOE.IOEither[error, string] {
|
||||||
|
return func() E.Either[error, string] {
|
||||||
|
dynamicCount++
|
||||||
|
return E.Of[error](fmt.Sprintf("Dynamic based on [%s] at [%s]", value, time.Now()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p2 := MakeProvider1(INJ_KEY2, INJ_KEY1.Option(), dynamicValue)
|
||||||
|
|
||||||
|
inj := DIE.MakeInjector(A.From(p2))
|
||||||
|
|
||||||
|
i2 := Resolve(INJ_KEY2)
|
||||||
|
|
||||||
|
res := IOE.SequenceT2(
|
||||||
|
i2(inj),
|
||||||
|
i2(inj),
|
||||||
|
)
|
||||||
|
|
||||||
|
r := res()
|
||||||
|
|
||||||
|
assert.True(t, E.IsRight(r))
|
||||||
|
assert.Equal(t, 1, dynamicCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestProviderMissingDependency(t *testing.T) {
|
||||||
|
|
||||||
|
var dynamicCount int
|
||||||
|
|
||||||
|
dynamicValue := func(value string) IOE.IOEither[error, string] {
|
||||||
|
return func() E.Either[error, string] {
|
||||||
|
dynamicCount++
|
||||||
|
return E.Of[error](fmt.Sprintf("Dynamic based on [%s] at [%s]", value, time.Now()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p2 := MakeProvider1(INJ_KEY2, INJ_KEY1.Identity(), dynamicValue)
|
||||||
|
|
||||||
|
inj := DIE.MakeInjector(A.From(p2))
|
||||||
|
|
||||||
|
i2 := Resolve(INJ_KEY2)
|
||||||
|
|
||||||
|
res := IOE.SequenceT2(
|
||||||
|
i2(inj),
|
||||||
|
i2(inj),
|
||||||
|
)
|
||||||
|
|
||||||
|
r := res()
|
||||||
|
|
||||||
|
assert.True(t, E.IsLeft(r))
|
||||||
|
assert.Equal(t, 0, dynamicCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEagerAndLazyProvider(t *testing.T) {
|
||||||
|
|
||||||
|
var staticCount int
|
||||||
|
|
||||||
|
staticValue := func(value string) IOE.IOEither[error, string] {
|
||||||
|
return func() E.Either[error, string] {
|
||||||
|
staticCount++
|
||||||
|
return E.Of[error](fmt.Sprintf("Static based on [%s], at [%s]", value, time.Now()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var dynamicCount int
|
||||||
|
|
||||||
|
dynamicValue := func(value string) IOE.IOEither[error, string] {
|
||||||
|
return func() E.Either[error, string] {
|
||||||
|
dynamicCount++
|
||||||
|
return E.Of[error](fmt.Sprintf("Dynamic based on [%s] at [%s]", value, time.Now()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var lazyEagerCount int
|
||||||
|
|
||||||
|
lazyEager := func(laz IOE.IOEither[error, string], eager string) IOE.IOEither[error, string] {
|
||||||
|
return F.Pipe1(
|
||||||
|
laz,
|
||||||
|
IOE.Chain(func(lazValue string) IOE.IOEither[error, string] {
|
||||||
|
return func() E.Either[error, string] {
|
||||||
|
lazyEagerCount++
|
||||||
|
return E.Of[error](fmt.Sprintf("Dynamic based on [%s], [%s] at [%s]", lazValue, eager, time.Now()))
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
p1 := MakeProvider0(INJ_KEY1, staticValue("Carsten"))
|
||||||
|
p2 := MakeProvider1(INJ_KEY2, INJ_KEY1.Identity(), dynamicValue)
|
||||||
|
p3 := MakeProvider2(INJ_KEY3, INJ_KEY2.IOEither(), INJ_KEY1.Identity(), lazyEager)
|
||||||
|
|
||||||
|
inj := DIE.MakeInjector(A.From(p1, p2, p3))
|
||||||
|
|
||||||
|
i3 := Resolve(INJ_KEY3)
|
||||||
|
|
||||||
|
r := i3(inj)()
|
||||||
|
|
||||||
|
fmt.Println(r)
|
||||||
|
|
||||||
|
assert.True(t, E.IsRight(r))
|
||||||
|
assert.Equal(t, 1, staticCount)
|
||||||
|
assert.Equal(t, 1, dynamicCount)
|
||||||
|
assert.Equal(t, 1, lazyEagerCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestItemProvider(t *testing.T) {
|
||||||
|
// define a multi token
|
||||||
|
injMulti := MakeMultiToken[string]("configs")
|
||||||
|
|
||||||
|
// provide some values
|
||||||
|
v1 := ConstProvider(injMulti.Item(), "Value1")
|
||||||
|
v2 := ConstProvider(injMulti.Item(), "Value2")
|
||||||
|
// mix in non-multi values
|
||||||
|
p1 := ConstProvider(INJ_KEY1, "Value3")
|
||||||
|
p2 := ConstProvider(INJ_KEY2, "Value4")
|
||||||
|
|
||||||
|
// populate the injector
|
||||||
|
inj := DIE.MakeInjector(A.From(p1, v1, p2, v2))
|
||||||
|
|
||||||
|
// access the multi value
|
||||||
|
multi := Resolve(injMulti.Container())
|
||||||
|
|
||||||
|
multiInj := multi(inj)
|
||||||
|
|
||||||
|
value := multiInj()
|
||||||
|
|
||||||
|
assert.Equal(t, E.Of[error](A.From("Value1", "Value2")), value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEmptyItemProvider(t *testing.T) {
|
||||||
|
// define a multi token
|
||||||
|
injMulti := MakeMultiToken[string]("configs")
|
||||||
|
|
||||||
|
// mix in non-multi values
|
||||||
|
p1 := ConstProvider(INJ_KEY1, "Value3")
|
||||||
|
p2 := ConstProvider(INJ_KEY2, "Value4")
|
||||||
|
|
||||||
|
// populate the injector
|
||||||
|
inj := DIE.MakeInjector(A.From(p1, p2))
|
||||||
|
|
||||||
|
// access the multi value
|
||||||
|
multi := Resolve(injMulti.Container())
|
||||||
|
|
||||||
|
multiInj := multi(inj)
|
||||||
|
|
||||||
|
value := multiInj()
|
||||||
|
|
||||||
|
assert.Equal(t, E.Of[error](A.Empty[string]()), value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDependencyOnMultiProvider(t *testing.T) {
|
||||||
|
// define a multi token
|
||||||
|
injMulti := MakeMultiToken[string]("configs")
|
||||||
|
|
||||||
|
// provide some values
|
||||||
|
v1 := ConstProvider(injMulti.Item(), "Value1")
|
||||||
|
v2 := ConstProvider(injMulti.Item(), "Value2")
|
||||||
|
// mix in non-multi values
|
||||||
|
p1 := ConstProvider(INJ_KEY1, "Value3")
|
||||||
|
p2 := ConstProvider(INJ_KEY2, "Value4")
|
||||||
|
|
||||||
|
fromMulti := func(val string, multi []string) IOE.IOEither[error, string] {
|
||||||
|
return IOE.Of[error](fmt.Sprintf("Val: %s, Multi: %s", val, multi))
|
||||||
|
}
|
||||||
|
p3 := MakeProvider2(INJ_KEY3, INJ_KEY1.Identity(), injMulti.Container().Identity(), fromMulti)
|
||||||
|
|
||||||
|
// populate the injector
|
||||||
|
inj := DIE.MakeInjector(A.From(p1, p2, v1, v2, p3))
|
||||||
|
|
||||||
|
r3 := Resolve(INJ_KEY3)
|
||||||
|
|
||||||
|
v := r3(inj)()
|
||||||
|
|
||||||
|
assert.Equal(t, E.Of[error]("Val: Value3, Multi: [Value1 Value2]"), v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTokenWithDefaultProvider(t *testing.T) {
|
||||||
|
// token without a default
|
||||||
|
injToken1 := MakeToken[string]("Token1")
|
||||||
|
// token with a default
|
||||||
|
injToken2 := MakeTokenWithDefault0("Token2", IOE.Of[error]("Carsten"))
|
||||||
|
// dependency
|
||||||
|
injToken3 := MakeToken[string]("Token3")
|
||||||
|
|
||||||
|
p3 := MakeProvider1(injToken3, injToken2.Identity(), func(data string) IOE.IOEither[error, string] {
|
||||||
|
return IOE.Of[error](fmt.Sprintf("Token: %s", data))
|
||||||
|
})
|
||||||
|
|
||||||
|
// populate the injector
|
||||||
|
inj := DIE.MakeInjector(A.From(p3))
|
||||||
|
|
||||||
|
// resolving injToken3 should work and use the default provider for injToken2
|
||||||
|
r1 := Resolve(injToken1)
|
||||||
|
r3 := Resolve(injToken3)
|
||||||
|
|
||||||
|
// inj1 should not be available
|
||||||
|
assert.True(t, E.IsLeft(r1(inj)()))
|
||||||
|
// r3 should work
|
||||||
|
assert.Equal(t, E.Of[error]("Token: Carsten"), r3(inj)())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTokenWithDefaultProviderAndOverride(t *testing.T) {
|
||||||
|
// token with a default
|
||||||
|
injToken2 := MakeTokenWithDefault0("Token2", IOE.Of[error]("Carsten"))
|
||||||
|
// dependency
|
||||||
|
injToken3 := MakeToken[string]("Token3")
|
||||||
|
|
||||||
|
p2 := ConstProvider(injToken2, "Override")
|
||||||
|
|
||||||
|
p3 := MakeProvider1(injToken3, injToken2.Identity(), func(data string) IOE.IOEither[error, string] {
|
||||||
|
return IOE.Of[error](fmt.Sprintf("Token: %s", data))
|
||||||
|
})
|
||||||
|
|
||||||
|
// populate the injector
|
||||||
|
inj := DIE.MakeInjector(A.From(p2, p3))
|
||||||
|
|
||||||
|
// resolving injToken3 should work and use the default provider for injToken2
|
||||||
|
r3 := Resolve(injToken3)
|
||||||
|
|
||||||
|
// r3 should work
|
||||||
|
assert.Equal(t, E.Of[error]("Token: Override"), r3(inj)())
|
||||||
|
}
|
204
di/token.go
Normal file
204
di/token.go
Normal file
@@ -0,0 +1,204 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package di
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"sync/atomic"
|
||||||
|
|
||||||
|
DIE "github.com/IBM/fp-go/di/erasure"
|
||||||
|
E "github.com/IBM/fp-go/either"
|
||||||
|
IO "github.com/IBM/fp-go/io"
|
||||||
|
IOE "github.com/IBM/fp-go/ioeither"
|
||||||
|
IOO "github.com/IBM/fp-go/iooption"
|
||||||
|
O "github.com/IBM/fp-go/option"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Dependency describes the relationship to a service, that has a type and
|
||||||
|
// a behaviour such as required, option or lazy
|
||||||
|
type Dependency[T any] interface {
|
||||||
|
DIE.Dependency
|
||||||
|
// Unerase converts a value with erased type signature into a strongly typed value
|
||||||
|
Unerase(val any) E.Either[error, T]
|
||||||
|
}
|
||||||
|
|
||||||
|
// InjectionToken uniquely identifies a dependency by giving it an Id, Type and name
|
||||||
|
type InjectionToken[T any] interface {
|
||||||
|
Dependency[T]
|
||||||
|
// Identity idenifies this dependency as a mandatory, required dependency, it will be resolved eagerly and injected as `T`.
|
||||||
|
// If the dependency cannot be resolved, the resolution process fails
|
||||||
|
Identity() Dependency[T]
|
||||||
|
// Option identifies this dependency as optional, it will be resolved eagerly and injected as [O.Option[T]].
|
||||||
|
// If the dependency cannot be resolved, the resolution process continues and the dependency is represented as [O.None[T]]
|
||||||
|
Option() Dependency[O.Option[T]]
|
||||||
|
// IOEither identifies this dependency as mandatory but it will be resolved lazily as a [IOE.IOEither[error, T]]. This
|
||||||
|
// value is memoized to make sure the dependency is a singleton.
|
||||||
|
// If the dependency cannot be resolved, the resolution process fails
|
||||||
|
IOEither() Dependency[IOE.IOEither[error, T]]
|
||||||
|
// IOOption identifies this dependency as optional but it will be resolved lazily as a [IOO.IOOption[T]]. This
|
||||||
|
// value is memoized to make sure the dependency is a singleton.
|
||||||
|
// If the dependency cannot be resolved, the resolution process continues and the dependency is represented as the none value.
|
||||||
|
IOOption() Dependency[IOO.IOOption[T]]
|
||||||
|
}
|
||||||
|
|
||||||
|
// MultiInjectionToken uniquely identifies a dependency by giving it an Id, Type and name that can have multiple implementations.
|
||||||
|
// Implementations are provided via the [MultiInjectionToken.Item] injection token.
|
||||||
|
type MultiInjectionToken[T any] interface {
|
||||||
|
// Container returns the injection token used to request an array of all provided items
|
||||||
|
Container() InjectionToken[[]T]
|
||||||
|
// Item returns the injection token used to provide an item
|
||||||
|
Item() InjectionToken[T]
|
||||||
|
}
|
||||||
|
|
||||||
|
// makeID creates a generator of unique string IDs
|
||||||
|
func makeID() IO.IO[string] {
|
||||||
|
var count atomic.Int64
|
||||||
|
return IO.MakeIO(func() string {
|
||||||
|
return strconv.FormatInt(count.Add(1), 16)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// genID is the common generator of unique string IDs
|
||||||
|
var genID = makeID()
|
||||||
|
|
||||||
|
type tokenBase struct {
|
||||||
|
name string
|
||||||
|
id string
|
||||||
|
flag int
|
||||||
|
providerFactory O.Option[DIE.ProviderFactory]
|
||||||
|
}
|
||||||
|
|
||||||
|
type token[T any] struct {
|
||||||
|
base *tokenBase
|
||||||
|
toType func(val any) E.Either[error, T]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *token[T]) Id() string {
|
||||||
|
return t.base.id
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *token[T]) Flag() int {
|
||||||
|
return t.base.flag
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *token[T]) String() string {
|
||||||
|
return t.base.name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *token[T]) Unerase(val any) E.Either[error, T] {
|
||||||
|
return t.toType(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *token[T]) ProviderFactory() O.Option[DIE.ProviderFactory] {
|
||||||
|
return t.base.providerFactory
|
||||||
|
}
|
||||||
|
func makeTokenBase(name string, id string, typ int, providerFactory O.Option[DIE.ProviderFactory]) *tokenBase {
|
||||||
|
return &tokenBase{name, id, typ, providerFactory}
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeToken[T any](name string, id string, typ int, unerase func(val any) E.Either[error, T], providerFactory O.Option[DIE.ProviderFactory]) Dependency[T] {
|
||||||
|
return &token[T]{makeTokenBase(name, id, typ, providerFactory), unerase}
|
||||||
|
}
|
||||||
|
|
||||||
|
type injectionToken[T any] struct {
|
||||||
|
token[T]
|
||||||
|
option Dependency[O.Option[T]]
|
||||||
|
ioeither Dependency[IOE.IOEither[error, T]]
|
||||||
|
iooption Dependency[IOO.IOOption[T]]
|
||||||
|
}
|
||||||
|
|
||||||
|
type multiInjectionToken[T any] struct {
|
||||||
|
container *injectionToken[[]T]
|
||||||
|
item *injectionToken[T]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *injectionToken[T]) Identity() Dependency[T] {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *injectionToken[T]) Option() Dependency[O.Option[T]] {
|
||||||
|
return i.option
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *injectionToken[T]) IOEither() Dependency[IOE.IOEither[error, T]] {
|
||||||
|
return i.ioeither
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *injectionToken[T]) IOOption() Dependency[IOO.IOOption[T]] {
|
||||||
|
return i.iooption
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *injectionToken[T]) ProviderFactory() O.Option[DIE.ProviderFactory] {
|
||||||
|
return i.base.providerFactory
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *multiInjectionToken[T]) Container() InjectionToken[[]T] {
|
||||||
|
return m.container
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *multiInjectionToken[T]) Item() InjectionToken[T] {
|
||||||
|
return m.item
|
||||||
|
}
|
||||||
|
|
||||||
|
// makeToken create a unique [InjectionToken] for a specific type
|
||||||
|
func makeInjectionToken[T any](name string, providerFactory O.Option[DIE.ProviderFactory]) InjectionToken[T] {
|
||||||
|
id := genID()
|
||||||
|
toIdentity := toType[T]()
|
||||||
|
return &injectionToken[T]{
|
||||||
|
token[T]{makeTokenBase(name, id, DIE.Identity, providerFactory), toIdentity},
|
||||||
|
makeToken[O.Option[T]](fmt.Sprintf("Option[%s]", name), id, DIE.Option, toOptionType(toIdentity), providerFactory),
|
||||||
|
makeToken[IOE.IOEither[error, T]](fmt.Sprintf("IOEither[%s]", name), id, DIE.IOEither, toIOEitherType(toIdentity), providerFactory),
|
||||||
|
makeToken[IOO.IOOption[T]](fmt.Sprintf("IOOption[%s]", name), id, DIE.IOOption, toIOOptionType(toIdentity), providerFactory),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeToken create a unique [InjectionToken] for a specific type
|
||||||
|
func MakeToken[T any](name string) InjectionToken[T] {
|
||||||
|
return makeInjectionToken[T](name, O.None[DIE.ProviderFactory]())
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeToken create a unique [InjectionToken] for a specific type
|
||||||
|
func MakeTokenWithDefault[T any](name string, providerFactory DIE.ProviderFactory) InjectionToken[T] {
|
||||||
|
return makeInjectionToken[T](name, O.Of(providerFactory))
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeMultiToken creates a [MultiInjectionToken]
|
||||||
|
func MakeMultiToken[T any](name string) MultiInjectionToken[T] {
|
||||||
|
id := genID()
|
||||||
|
toItem := toType[T]()
|
||||||
|
toContainer := toArrayType(toItem)
|
||||||
|
containerName := fmt.Sprintf("Container[%s]", name)
|
||||||
|
itemName := fmt.Sprintf("Item[%s]", name)
|
||||||
|
// empty factory
|
||||||
|
providerFactory := O.None[DIE.ProviderFactory]()
|
||||||
|
// container
|
||||||
|
container := &injectionToken[[]T]{
|
||||||
|
token[[]T]{makeTokenBase(containerName, id, DIE.Multi|DIE.Identity, providerFactory), toContainer},
|
||||||
|
makeToken[O.Option[[]T]](fmt.Sprintf("Option[%s]", containerName), id, DIE.Multi|DIE.Option, toOptionType(toContainer), providerFactory),
|
||||||
|
makeToken[IOE.IOEither[error, []T]](fmt.Sprintf("IOEither[%s]", containerName), id, DIE.Multi|DIE.IOEither, toIOEitherType(toContainer), providerFactory),
|
||||||
|
makeToken[IOO.IOOption[[]T]](fmt.Sprintf("IOOption[%s]", containerName), id, DIE.Multi|DIE.IOOption, toIOOptionType(toContainer), providerFactory),
|
||||||
|
}
|
||||||
|
// item
|
||||||
|
item := &injectionToken[T]{
|
||||||
|
token[T]{makeTokenBase(itemName, id, DIE.Item|DIE.Identity, providerFactory), toItem},
|
||||||
|
makeToken[O.Option[T]](fmt.Sprintf("Option[%s]", itemName), id, DIE.Item|DIE.Option, toOptionType(toItem), providerFactory),
|
||||||
|
makeToken[IOE.IOEither[error, T]](fmt.Sprintf("IOEither[%s]", itemName), id, DIE.Item|DIE.IOEither, toIOEitherType(toItem), providerFactory),
|
||||||
|
makeToken[IOO.IOOption[T]](fmt.Sprintf("IOOption[%s]", itemName), id, DIE.Item|DIE.IOOption, toIOOptionType(toItem), providerFactory),
|
||||||
|
}
|
||||||
|
// returns the token
|
||||||
|
return &multiInjectionToken[T]{container, item}
|
||||||
|
}
|
84
di/utils.go
Normal file
84
di/utils.go
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package di
|
||||||
|
|
||||||
|
import (
|
||||||
|
DIE "github.com/IBM/fp-go/di/erasure"
|
||||||
|
E "github.com/IBM/fp-go/either"
|
||||||
|
"github.com/IBM/fp-go/errors"
|
||||||
|
F "github.com/IBM/fp-go/function"
|
||||||
|
IOE "github.com/IBM/fp-go/ioeither"
|
||||||
|
IOO "github.com/IBM/fp-go/iooption"
|
||||||
|
O "github.com/IBM/fp-go/option"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
toOptionAny = toType[O.Option[any]]()
|
||||||
|
toIOEitherAny = toType[IOE.IOEither[error, any]]()
|
||||||
|
toIOOptionAny = toType[IOO.IOOption[any]]()
|
||||||
|
toArrayAny = toType[[]any]()
|
||||||
|
)
|
||||||
|
|
||||||
|
// asDependency converts a generic type to a [DIE.Dependency]
|
||||||
|
func asDependency[T DIE.Dependency](t T) DIE.Dependency {
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
// toType converts an any to a T
|
||||||
|
func toType[T any]() func(t any) E.Either[error, T] {
|
||||||
|
return E.ToType[T](errors.OnSome[any]("Value of type [%T] cannot be converted."))
|
||||||
|
}
|
||||||
|
|
||||||
|
// toOptionType converts an any to an Option[any] and then to an Option[T]
|
||||||
|
func toOptionType[T any](item func(any) E.Either[error, T]) func(t any) E.Either[error, O.Option[T]] {
|
||||||
|
return F.Flow2(
|
||||||
|
toOptionAny,
|
||||||
|
E.Chain(O.Fold(
|
||||||
|
F.Nullary2(O.None[T], E.Of[error, O.Option[T]]),
|
||||||
|
F.Flow2(
|
||||||
|
item,
|
||||||
|
E.Map[error](O.Of[T]),
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// toIOEitherType converts an any to an IOEither[error, any] and then to an IOEither[error, T]
|
||||||
|
func toIOEitherType[T any](item func(any) E.Either[error, T]) func(t any) E.Either[error, IOE.IOEither[error, T]] {
|
||||||
|
return F.Flow2(
|
||||||
|
toIOEitherAny,
|
||||||
|
E.Map[error](IOE.ChainEitherK(item)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// toIOOptionType converts an any to an IOOption[any] and then to an IOOption[T]
|
||||||
|
func toIOOptionType[T any](item func(any) E.Either[error, T]) func(t any) E.Either[error, IOO.IOOption[T]] {
|
||||||
|
return F.Flow2(
|
||||||
|
toIOOptionAny,
|
||||||
|
E.Map[error](IOO.ChainOptionK(F.Flow2(
|
||||||
|
item,
|
||||||
|
E.ToOption[error, T],
|
||||||
|
))),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// toArrayType converts an any to a []T
|
||||||
|
func toArrayType[T any](item func(any) E.Either[error, T]) func(t any) E.Either[error, []T] {
|
||||||
|
return F.Flow2(
|
||||||
|
toArrayAny,
|
||||||
|
E.Chain(E.TraverseArray(item)),
|
||||||
|
)
|
||||||
|
}
|
84
di/utils_test.go
Normal file
84
di/utils_test.go
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package di
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
A "github.com/IBM/fp-go/array"
|
||||||
|
E "github.com/IBM/fp-go/either"
|
||||||
|
F "github.com/IBM/fp-go/function"
|
||||||
|
IOE "github.com/IBM/fp-go/ioeither"
|
||||||
|
O "github.com/IBM/fp-go/option"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
toInt = toType[int]()
|
||||||
|
toString = toType[string]()
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestToType(t *testing.T) {
|
||||||
|
// good cases
|
||||||
|
assert.Equal(t, E.Of[error](10), toInt(any(10)))
|
||||||
|
assert.Equal(t, E.Of[error]("Carsten"), toString(any("Carsten")))
|
||||||
|
assert.Equal(t, E.Of[error](O.Of("Carsten")), toType[O.Option[string]]()(any(O.Of("Carsten"))))
|
||||||
|
assert.Equal(t, E.Of[error](O.Of(any("Carsten"))), toType[O.Option[any]]()(any(O.Of(any("Carsten")))))
|
||||||
|
// failure
|
||||||
|
assert.False(t, E.IsRight(toInt(any("Carsten"))))
|
||||||
|
assert.False(t, E.IsRight(toType[O.Option[string]]()(O.Of(any("Carsten")))))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestToOptionType(t *testing.T) {
|
||||||
|
// shortcuts
|
||||||
|
toOptInt := toOptionType(toInt)
|
||||||
|
toOptString := toOptionType(toString)
|
||||||
|
// good cases
|
||||||
|
assert.Equal(t, E.Of[error](O.Of(10)), toOptInt(any(O.Of(any(10)))))
|
||||||
|
assert.Equal(t, E.Of[error](O.Of("Carsten")), toOptString(any(O.Of(any("Carsten")))))
|
||||||
|
// bad cases
|
||||||
|
assert.False(t, E.IsRight(toOptInt(any(10))))
|
||||||
|
assert.False(t, E.IsRight(toOptInt(any(O.Of(10)))))
|
||||||
|
}
|
||||||
|
|
||||||
|
func invokeIOEither[T any](e E.Either[error, IOE.IOEither[error, T]]) E.Either[error, T] {
|
||||||
|
return F.Pipe1(
|
||||||
|
e,
|
||||||
|
E.Chain(func(ioe IOE.IOEither[error, T]) E.Either[error, T] {
|
||||||
|
return ioe()
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestToIOEitherType(t *testing.T) {
|
||||||
|
// shortcuts
|
||||||
|
toIOEitherInt := toIOEitherType(toInt)
|
||||||
|
toIOEitherString := toIOEitherType(toString)
|
||||||
|
// good cases
|
||||||
|
assert.Equal(t, E.Of[error](10), invokeIOEither(toIOEitherInt(any(IOE.Of[error](any(10))))))
|
||||||
|
assert.Equal(t, E.Of[error]("Carsten"), invokeIOEither(toIOEitherString(any(IOE.Of[error](any("Carsten"))))))
|
||||||
|
// bad cases
|
||||||
|
assert.False(t, E.IsRight(invokeIOEither(toIOEitherString(any(IOE.Of[error](any(10)))))))
|
||||||
|
assert.False(t, E.IsRight(invokeIOEither(toIOEitherString(any(IOE.Of[error]("Carsten"))))))
|
||||||
|
assert.False(t, E.IsRight(invokeIOEither(toIOEitherString(any("Carsten")))))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestToArrayType(t *testing.T) {
|
||||||
|
// shortcuts
|
||||||
|
toArrayString := toArrayType(toString)
|
||||||
|
// good cases
|
||||||
|
assert.Equal(t, E.Of[error](A.From("a", "b")), toArrayString(any(A.From(any("a"), any("b")))))
|
||||||
|
}
|
@@ -24,6 +24,7 @@ func ApplySemigroup[E, A any](s S.Semigroup[A]) S.Semigroup[Either[E, A]] {
|
|||||||
return S.ApplySemigroup(MonadMap[E, A, func(A) A], MonadAp[A, E, A], s)
|
return S.ApplySemigroup(MonadMap[E, A, func(A) A], MonadAp[A, E, A], s)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ApplicativeMonoid returns a [Monoid] that concatenates [Either] instances via their applicative
|
||||||
func ApplicativeMonoid[E, A any](m M.Monoid[A]) M.Monoid[Either[E, A]] {
|
func ApplicativeMonoid[E, A any](m M.Monoid[A]) M.Monoid[Either[E, A]] {
|
||||||
return M.ApplicativeMonoid(Of[E, A], MonadMap[E, A, func(A) A], MonadAp[A, E, A], m)
|
return M.ApplicativeMonoid(Of[E, A], MonadMap[E, A, func(A) A], MonadAp[A, E, A], m)
|
||||||
}
|
}
|
||||||
|
@@ -20,7 +20,7 @@ import (
|
|||||||
RA "github.com/IBM/fp-go/internal/array"
|
RA "github.com/IBM/fp-go/internal/array"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TraverseArray transforms an array
|
// TraverseArrayG transforms an array
|
||||||
func TraverseArrayG[GA ~[]A, GB ~[]B, E, A, B any](f func(A) Either[E, B]) func(GA) Either[E, GB] {
|
func TraverseArrayG[GA ~[]A, GB ~[]B, E, A, B any](f func(A) Either[E, B]) func(GA) Either[E, GB] {
|
||||||
return RA.Traverse[GA](
|
return RA.Traverse[GA](
|
||||||
Of[E, GB],
|
Of[E, GB],
|
||||||
@@ -36,6 +36,22 @@ func TraverseArray[E, A, B any](f func(A) Either[E, B]) func([]A) Either[E, []B]
|
|||||||
return TraverseArrayG[[]A, []B](f)
|
return TraverseArrayG[[]A, []B](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TraverseArrayWithIndexG transforms an array
|
||||||
|
func TraverseArrayWithIndexG[GA ~[]A, GB ~[]B, E, A, B any](f func(int, A) Either[E, B]) func(GA) Either[E, GB] {
|
||||||
|
return RA.TraverseWithIndex[GA](
|
||||||
|
Of[E, GB],
|
||||||
|
Map[E, GB, func(B) GB],
|
||||||
|
Ap[GB, E, B],
|
||||||
|
|
||||||
|
f,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TraverseArrayWithIndex transforms an array
|
||||||
|
func TraverseArrayWithIndex[E, A, B any](f func(int, A) Either[E, B]) func([]A) Either[E, []B] {
|
||||||
|
return TraverseArrayWithIndexG[[]A, []B](f)
|
||||||
|
}
|
||||||
|
|
||||||
func SequenceArrayG[GA ~[]A, GOA ~[]Either[E, A], E, A any](ma GOA) Either[E, GA] {
|
func SequenceArrayG[GA ~[]A, GOA ~[]Either[E, A], E, A any](ma GOA) Either[E, GA] {
|
||||||
return TraverseArrayG[GOA, GA](F.Identity[Either[E, A]])(ma)
|
return TraverseArrayG[GOA, GA](F.Identity[Either[E, A]])(ma)
|
||||||
}
|
}
|
||||||
@@ -44,3 +60,15 @@ func SequenceArrayG[GA ~[]A, GOA ~[]Either[E, A], E, A any](ma GOA) Either[E, GA
|
|||||||
func SequenceArray[E, A any](ma []Either[E, A]) Either[E, []A] {
|
func SequenceArray[E, A any](ma []Either[E, A]) Either[E, []A] {
|
||||||
return SequenceArrayG[[]A](ma)
|
return SequenceArrayG[[]A](ma)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CompactArrayG discards the none values and keeps the right values
|
||||||
|
func CompactArrayG[A1 ~[]Either[E, A], A2 ~[]A, E, A any](fa A1) A2 {
|
||||||
|
return RA.Reduce(fa, func(out A2, value Either[E, A]) A2 {
|
||||||
|
return MonadFold(value, F.Constant1[E](out), F.Bind1st(RA.Append[A2, A], out))
|
||||||
|
}, make(A2, 0, len(fa)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// CompactArray discards the none values and keeps the right values
|
||||||
|
func CompactArray[E, A any](fa []Either[E, A]) []A {
|
||||||
|
return CompactArrayG[[]Either[E, A], []A](fa)
|
||||||
|
}
|
||||||
|
50
either/array_test.go
Normal file
50
either/array_test.go
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
package either
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
TST "github.com/IBM/fp-go/internal/testing"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCompactArray(t *testing.T) {
|
||||||
|
ar := []Either[string, string]{
|
||||||
|
Of[string]("ok"),
|
||||||
|
Left[string]("err"),
|
||||||
|
Of[string]("ok"),
|
||||||
|
}
|
||||||
|
|
||||||
|
res := CompactArray(ar)
|
||||||
|
assert.Equal(t, 2, len(res))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSequenceArray(t *testing.T) {
|
||||||
|
|
||||||
|
s := TST.SequenceArrayTest(
|
||||||
|
FromStrictEquals[error, bool](),
|
||||||
|
Pointed[error, string](),
|
||||||
|
Pointed[error, bool](),
|
||||||
|
Functor[error, []string, bool](),
|
||||||
|
SequenceArray[error, string],
|
||||||
|
)
|
||||||
|
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
t.Run(fmt.Sprintf("TestSequenceArray %d", i), s(i))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSequenceArrayError(t *testing.T) {
|
||||||
|
|
||||||
|
s := TST.SequenceArrayErrorTest(
|
||||||
|
FromStrictEquals[error, bool](),
|
||||||
|
Left[string, error],
|
||||||
|
Left[bool, error],
|
||||||
|
Pointed[error, string](),
|
||||||
|
Pointed[error, bool](),
|
||||||
|
Functor[error, []string, bool](),
|
||||||
|
SequenceArray[error, string],
|
||||||
|
)
|
||||||
|
// run across four bits
|
||||||
|
s(4)(t)
|
||||||
|
}
|
89
either/bind.go
Normal file
89
either/bind.go
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package either
|
||||||
|
|
||||||
|
import (
|
||||||
|
A "github.com/IBM/fp-go/internal/apply"
|
||||||
|
C "github.com/IBM/fp-go/internal/chain"
|
||||||
|
F "github.com/IBM/fp-go/internal/functor"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Bind creates an empty context of type [S] to be used with the [Bind] operation
|
||||||
|
func Do[E, S any](
|
||||||
|
empty S,
|
||||||
|
) Either[E, S] {
|
||||||
|
return Of[E](empty)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bind attaches the result of a computation to a context [S1] to produce a context [S2]
|
||||||
|
func Bind[E, S1, S2, T any](
|
||||||
|
setter func(T) func(S1) S2,
|
||||||
|
f func(S1) Either[E, T],
|
||||||
|
) func(Either[E, S1]) Either[E, S2] {
|
||||||
|
return C.Bind(
|
||||||
|
Chain[E, S1, S2],
|
||||||
|
Map[E, T, S2],
|
||||||
|
setter,
|
||||||
|
f,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Let attaches the result of a computation to a context [S1] to produce a context [S2]
|
||||||
|
func Let[E, S1, S2, T any](
|
||||||
|
key func(T) func(S1) S2,
|
||||||
|
f func(S1) T,
|
||||||
|
) func(Either[E, S1]) Either[E, S2] {
|
||||||
|
return F.Let(
|
||||||
|
Map[E, S1, S2],
|
||||||
|
key,
|
||||||
|
f,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LetTo attaches the a value to a context [S1] to produce a context [S2]
|
||||||
|
func LetTo[E, S1, S2, T any](
|
||||||
|
key func(T) func(S1) S2,
|
||||||
|
b T,
|
||||||
|
) func(Either[E, S1]) Either[E, S2] {
|
||||||
|
return F.LetTo(
|
||||||
|
Map[E, S1, S2],
|
||||||
|
key,
|
||||||
|
b,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BindTo initializes a new state [S1] from a value [T]
|
||||||
|
func BindTo[E, S1, T any](
|
||||||
|
setter func(T) S1,
|
||||||
|
) func(Either[E, T]) Either[E, S1] {
|
||||||
|
return C.BindTo(
|
||||||
|
Map[E, T, S1],
|
||||||
|
setter,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApS attaches a value to a context [S1] to produce a context [S2] by considering the context and the value concurrently
|
||||||
|
func ApS[E, S1, S2, T any](
|
||||||
|
setter func(T) func(S1) S2,
|
||||||
|
fa Either[E, T],
|
||||||
|
) func(Either[E, S1]) Either[E, S2] {
|
||||||
|
return A.ApS(
|
||||||
|
Ap[S2, E, T],
|
||||||
|
Map[E, S1, func(T) S2],
|
||||||
|
setter,
|
||||||
|
fa,
|
||||||
|
)
|
||||||
|
}
|
56
either/bind_test.go
Normal file
56
either/bind_test.go
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package either
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
F "github.com/IBM/fp-go/function"
|
||||||
|
"github.com/IBM/fp-go/internal/utils"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getLastName(s utils.Initial) Either[error, string] {
|
||||||
|
return Of[error]("Doe")
|
||||||
|
}
|
||||||
|
|
||||||
|
func getGivenName(s utils.WithLastName) Either[error, string] {
|
||||||
|
return Of[error]("John")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBind(t *testing.T) {
|
||||||
|
|
||||||
|
res := F.Pipe3(
|
||||||
|
Do[error](utils.Empty),
|
||||||
|
Bind(utils.SetLastName, getLastName),
|
||||||
|
Bind(utils.SetGivenName, getGivenName),
|
||||||
|
Map[error](utils.GetFullName),
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.Equal(t, res, Of[error]("John Doe"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestApS(t *testing.T) {
|
||||||
|
|
||||||
|
res := F.Pipe3(
|
||||||
|
Do[error](utils.Empty),
|
||||||
|
ApS(utils.SetLastName, Of[error]("Doe")),
|
||||||
|
ApS(utils.SetGivenName, Of[error]("John")),
|
||||||
|
Map[error](utils.GetFullName),
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.Equal(t, res, Of[error]("John Doe"))
|
||||||
|
}
|
@@ -20,61 +20,82 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
// Either defines a data structure that logically holds either an E or an A. The flag discriminates the cases
|
either struct {
|
||||||
Either[E, A any] struct {
|
|
||||||
isLeft bool
|
isLeft bool
|
||||||
left E
|
value any
|
||||||
right A
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Either defines a data structure that logically holds either an E or an A. The flag discriminates the cases
|
||||||
|
Either[E, A any] either
|
||||||
)
|
)
|
||||||
|
|
||||||
// String prints some debug info for the object
|
// String prints some debug info for the object
|
||||||
func (s Either[E, A]) String() string {
|
//
|
||||||
|
// go:noinline
|
||||||
|
func eitherString(s *either) string {
|
||||||
if s.isLeft {
|
if s.isLeft {
|
||||||
return fmt.Sprintf("Left[%T, %T](%v)", s.left, s.right, s.left)
|
return fmt.Sprintf("Left[%T](%v)", s.value, s.value)
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("Right[%T, %T](%v)", s.left, s.right, s.right)
|
return fmt.Sprintf("Right[%T](%v)", s.value, s.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format prints some debug info for the object
|
||||||
|
//
|
||||||
|
// go:noinline
|
||||||
|
func eitherFormat(e *either, f fmt.State, c rune) {
|
||||||
|
switch c {
|
||||||
|
case 's':
|
||||||
|
fmt.Fprint(f, eitherString(e))
|
||||||
|
default:
|
||||||
|
fmt.Fprint(f, eitherString(e))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// String prints some debug info for the object
|
||||||
|
func (s Either[E, A]) String() string {
|
||||||
|
return eitherString((*either)(&s))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Format prints some debug info for the object
|
// Format prints some debug info for the object
|
||||||
func (s Either[E, A]) Format(f fmt.State, c rune) {
|
func (s Either[E, A]) Format(f fmt.State, c rune) {
|
||||||
switch c {
|
eitherFormat((*either)(&s), f, c)
|
||||||
case 's':
|
|
||||||
fmt.Fprint(f, s.String())
|
|
||||||
default:
|
|
||||||
fmt.Fprint(f, s.String())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsLeft tests if the either is a left value. Rather use [Fold] if you need to access the values. Inverse is [IsRight].
|
// IsLeft tests if the [Either] is a left value. Rather use [Fold] if you need to access the values. Inverse is [IsRight].
|
||||||
func IsLeft[E, A any](val Either[E, A]) bool {
|
func IsLeft[E, A any](val Either[E, A]) bool {
|
||||||
return val.isLeft
|
return val.isLeft
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsLeft tests if the either is a right value. Rather use [Fold] if you need to access the values. Inverse is [IsLeft].
|
// IsLeft tests if the [Either] is a right value. Rather use [Fold] if you need to access the values. Inverse is [IsLeft].
|
||||||
func IsRight[E, A any](val Either[E, A]) bool {
|
func IsRight[E, A any](val Either[E, A]) bool {
|
||||||
return !val.isLeft
|
return !val.isLeft
|
||||||
}
|
}
|
||||||
|
|
||||||
// Left creates a new instance of an [Either] representing the left value.
|
// Left creates a new instance of an [Either] representing the left value.
|
||||||
func Left[A, E any](value E) Either[E, A] {
|
func Left[A, E any](value E) Either[E, A] {
|
||||||
return Either[E, A]{isLeft: true, left: value}
|
return Either[E, A]{true, value}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Right creates a new instance of an [Either] representing the right value.
|
// Right creates a new instance of an [Either] representing the right value.
|
||||||
func Right[E, A any](value A) Either[E, A] {
|
func Right[E, A any](value A) Either[E, A] {
|
||||||
return Either[E, A]{isLeft: false, right: value}
|
return Either[E, A]{false, value}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MonadFold extracts the values from an [Either] by invoking the [onLeft] callback or the [onRight] callback depending on the case
|
// MonadFold extracts the values from an [Either] by invoking the [onLeft] callback or the [onRight] callback depending on the case
|
||||||
func MonadFold[E, A, B any](ma Either[E, A], onLeft func(e E) B, onRight func(a A) B) B {
|
func MonadFold[E, A, B any](ma Either[E, A], onLeft func(e E) B, onRight func(a A) B) B {
|
||||||
if ma.isLeft {
|
if ma.isLeft {
|
||||||
return onLeft(ma.left)
|
return onLeft(ma.value.(E))
|
||||||
}
|
}
|
||||||
return onRight(ma.right)
|
return onRight(ma.value.(A))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unwrap converts an [Either] into the idiomatic tuple
|
// Unwrap converts an [Either] into the idiomatic tuple
|
||||||
func Unwrap[E, A any](ma Either[E, A]) (A, E) {
|
func Unwrap[E, A any](ma Either[E, A]) (A, E) {
|
||||||
return ma.right, ma.left
|
if ma.isLeft {
|
||||||
|
var a A
|
||||||
|
return a, ma.value.(E)
|
||||||
|
} else {
|
||||||
|
var e E
|
||||||
|
return ma.value.(A), e
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -16,4 +16,4 @@
|
|||||||
// Package option defines the [Either] datastructure and its monadic operations
|
// Package option defines the [Either] datastructure and its monadic operations
|
||||||
package either
|
package either
|
||||||
|
|
||||||
//go:generate go run .. either --count 10 --filename gen.go
|
//go:generate go run .. either --count 15 --filename gen.go
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user