1
0
mirror of https://github.com/IBM/fp-go.git synced 2025-08-24 19:29:11 +02:00

Compare commits

...

85 Commits

Author SHA1 Message Date
Dr. Carsten Leue
973138c822 fix: add prepend method
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-12-21 16:46:47 +01:00
Dr. Carsten Leue
12ef79184b fix: typing for Y combinator
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-12-21 16:25:35 +01:00
Dr. Carsten Leue
5ac47440a1 fix: add a single element cache
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-12-21 14:10:46 +01:00
Dr. Carsten Leue
3aa55c74d4 fix: add WithBearer
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-12-19 14:33:52 +01:00
Dr. Carsten Leue
a6f55a199c fix: experiment with doc links
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-12-19 12:18:07 +01:00
Dr. Carsten Leue
2b500d15da Merge branch 'main' of github.com:IBM/fp-go 2023-12-18 21:40:39 +01:00
Dr. Carsten Leue
599b8256b6 fix: generate DI variations
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-12-18 21:40:30 +01:00
renovate[bot]
cf70b47984 chore(deps): update actions/setup-node action to v4.0.1 2023-12-18 15:59:48 +00:00
Dr. Carsten Leue
7bceb856f8 fix: move DI to separate package
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-12-18 10:06:47 +01:00
Dr. Carsten Leue
49e89de783 fix: make Curry operations a bit more generic
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-12-18 09:46:46 +01:00
Dr. Carsten Leue
a87de2f644 fix: use endomorphism in optics
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-12-18 09:28:36 +01:00
Dr. Carsten Leue
6d043d2752 fix: common functions for endomorphism
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-12-17 22:59:25 +01:00
Dr. Carsten Leue
1d02f21ff5 fix: rename FormEndomorphism and FormBuilder
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-12-17 22:38:55 +01:00
Dr. Carsten Leue
e82575fe08 fix: consistent endomorphism
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-12-17 13:24:05 +01:00
Dr. Carsten Leue
5fcd0b1595 fix: use endomorphism
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-12-17 12:34:15 +01:00
Dr. Carsten Leue
5caabf478c fix: add Lens for FormData
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-12-16 23:38:03 +01:00
Dr. Carsten Leue
b7ec18c83e fix: Content-Length header in Requester
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-12-16 22:06:40 +01:00
Dr. Carsten Leue
96686425fb fix: add WithFormData and WithJson
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-12-16 19:29:20 +01:00
Dr. Carsten Leue
1f675e08fa fix: add support for request builder
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-12-16 16:49:57 +01:00
Dr. Carsten Leue
4d2f410c98 fix: add MakeBodyRequest
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-12-15 16:00:58 +01:00
Dr. Carsten Leue
8f49c1328c fix: add provider factories with more dependencies
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-12-15 15:31:01 +01:00
Dr. Carsten Leue
2a1d5821db fix: expose http client as injection token
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-12-15 15:21:07 +01:00
renovate[bot]
6abbdc5ee1 chore(deps): update actions/setup-go action to v5 2023-12-06 19:24:14 +00:00
renovate[bot]
5fea9858a9 fix(deps): update module github.com/urfave/cli/v2 to v2.26.0 2023-12-03 00:19:17 +00:00
Carsten Leue
e6426c90c0 fix: add WithResource to IO and IOOption (#90)
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-12-01 09:38:30 +01:00
Carsten Leue
54ce59105e fix: add ChainFirstIOK (#89)
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-11-30 14:50:29 +01:00
Carsten Leue
8bb006c741 fix: add a uniq method to arrays (#88)
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-11-30 09:03:16 +01:00
Carsten Leue
b6efa35b03 fix: bug in compact array (#87)
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-11-29 22:13:46 +01:00
Carsten Leue
35848900c0 fix: generic parameter order for ChainTo (#86)
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-11-28 09:26:19 +01:00
Dr. Carsten Leue
3d54f99739 fix: add missing TraverseArraySeq
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-11-27 20:52:02 +01:00
Carsten Leue
ffd9418cac fix: support Alt for IOOption (#84)
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-11-27 17:00:59 +01:00
Carsten Leue
acfcea59f4 fix: break cyclic dependencies between IOOption and IOEither (#83)
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-11-27 14:55:10 +01:00
Carsten Leue
b4bf511f03 fix: http interface of IOEither (#82)
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-11-27 12:12:44 +01:00
Carsten Leue
211340952b Dependency injection (#81)
* fix: checkin

Signed-off-by: Carsten Leue <carsten.leue@de.ibm.com>

* fix: add initial DI implementation

Signed-off-by: Carsten Leue <carsten.leue@de.ibm.com>

* fix: add multi provider

Signed-off-by: Carsten Leue <carsten.leue@de.ibm.com>

* fix: simplify DI implementation

Signed-off-by: Carsten Leue <carsten.leue@de.ibm.com>

* fix: simplify provider

Signed-off-by: Carsten Leue <carsten.leue@de.ibm.com>

* fix: add Switch to function package

Signed-off-by: Carsten Leue <carsten.leue@de.ibm.com>

* fix: add DI

Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>

---------

Signed-off-by: Carsten Leue <carsten.leue@de.ibm.com>
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-11-25 16:56:39 +01:00
Carsten Leue
56860425c4 fix: add Flip function (#80)
Signed-off-by: Carsten Leue <carsten.leue@de.ibm.com>
2023-11-12 13:34:27 +01:00
Carsten Leue
c0b16c675b fix: add reduce and filter (#79)
Signed-off-by: Carsten Leue <carsten.leue@de.ibm.com>
2023-11-11 16:50:18 +01:00
Carsten Leue
4b68e66528 fix: add samples for Any and Next to iterator package (#78)
Signed-off-by: Carsten Leue <carsten.leue@de.ibm.com>
2023-11-10 13:46:10 +01:00
renovate[bot]
5b7e5b153b chore(deps): update actions/checkout action to v4.1.1 2023-11-08 10:44:39 +00:00
Carsten Leue
d43fbeb375 Add presentation to sample section (#76)
* doc: add presentation

Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>

* fix: add some more examples

Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>

* doc: update presentation

Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>

* fix: update presentation

Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>

* fix: add presentation and samples

Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>

* fix: benchmarks

Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>

* fix: upload presentation

Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>

* doc: add presentation

Signed-off-by: Carsten Leue <carsten.leue@de.ibm.com>

* doc: add link to video

Signed-off-by: Carsten Leue <carsten.leue@de.ibm.com>

---------

Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
Signed-off-by: Carsten Leue <carsten.leue@de.ibm.com>
2023-11-08 09:58:23 +01:00
Carsten Leue
57d507c1ba Merge pull request #75 from pinguo-lixin/fix-magma-reverse
fix: magma.Reverse
2023-11-05 20:13:42 +01:00
lixin
aa19857127 fix: magma.Reverse 2023-11-04 14:49:30 +08:00
Dr. Carsten Leue
7766787cc1 fix: update
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-10-27 13:24:40 +02:00
Carsten Leue
6a9f38f990 Merge pull request #74 from IBM/cleue-add-asserts
fix: add assertions
2023-10-24 12:27:36 +02:00
Dr. Carsten Leue
e9584bc247 fix: add assertions
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-10-24 12:27:10 +02:00
renovate[bot]
55252c5680 chore(deps): update actions/setup-node action to v4 2023-10-23 19:08:50 +00:00
Carsten Leue
88bf35c4af Merge pull request #72 from IBM/cleue-improve-with-lock
Cleanup Either, context.Reader and context.ReaderIO
2023-10-23 09:06:22 +02:00
Dr. Carsten Leue
da3c9683eb fix: remove Reader and ReaderEither for context since they do not make sense
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-10-23 09:05:10 +02:00
Dr. Carsten Leue
08d9fed9af fix: remove unnecesary indirection in E.TryCatch
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-10-23 08:36:32 +02:00
Carsten Leue
83a0c6cdef Merge pull request #71 from IBM/cleue-add-mutex-to-io
fix: add WithLock to limit concurrency
2023-10-22 21:08:06 +02:00
Dr. Carsten Leue
9da484b79e fix: add WithLock to limit concurrency
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-10-22 21:07:40 +02:00
Carsten Leue
e46cfd89d4 Merge pull request #70 from IBM/cleue-add-bool-package
fix: add monoid for bool
2023-10-12 21:39:03 +02:00
Dr. Carsten Leue
6a4cdfa891 fix: add monoid for bool
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-10-12 21:38:11 +02:00
Carsten Leue
93a7e9964b Merge pull request #69 from IBM/cleue-add-missing-FromIO
fix: add missing FromIO to ReaderIO
2023-10-12 11:18:07 +02:00
Dr. Carsten Leue
9ef98e1ec4 fix: add missing FromIO to ReaderIO
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-10-12 11:17:31 +02:00
Carsten Leue
f1a730998d Merge pull request #68 from IBM/cleue-add-flap-to-readerio
fix: add Flap to more monads
2023-10-12 09:49:18 +02:00
Dr. Carsten Leue
f129297045 fix: add Flap to more monads
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-10-12 09:48:42 +02:00
Carsten Leue
b434f1fbf4 Merge pull request #67 from IBM/cleue-add-flap-to-reader
fix: add flap to Reader
2023-10-11 22:24:00 +02:00
Dr. Carsten Leue
756e1336dc fix: add flap to Reader
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-10-11 22:23:19 +02:00
Carsten Leue
c629d18bb3 Merge pull request #66 from IBM/cleue-add-try-catch-for-single-value-return
fix: remove File.GetName and add Join for convenience
2023-10-11 21:25:17 +02:00
Dr. Carsten Leue
1eefc28ba6 fix: remove File.GetName and add Join for convenience
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-10-11 17:36:11 +02:00
Carsten Leue
af2915d98a Merge pull request #65 from IBM/cleue-fix-flap-order
fix: change order of generics for flap
2023-10-10 22:41:43 +02:00
Dr. Carsten Leue
e9f9c2777f fix: change order of generics for flap
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-10-10 22:40:59 +02:00
Carsten Leue
895862a67e Merge pull request #64 from IBM/cleue-add-FromReaderIO
fix: add missing RightReaderIO and FromReaderIO to context
2023-10-10 15:05:34 +02:00
Dr. Carsten Leue
caf0574742 fix: add missing RightReaderIO and FromReaderIO to context
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-10-10 15:04:59 +02:00
Carsten Leue
bace6f01eb Merge pull request #62 from IBM/cleue-add-missing-chain-readeriok
fix: add missing ChainReaderIOK
2023-10-06 23:03:16 +02:00
Dr. Carsten Leue
254c63a16f fix: add missing ChainReaderIOK
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-10-06 23:02:45 +02:00
Carsten Leue
655c8a9b1d Merge pull request #61 from IBM/cleue-add-chain-first-to-readerio
fix: add missing ChainXXIOK to Reader
2023-10-06 22:36:38 +02:00
Dr. Carsten Leue
c6d6be66e0 fix: add missing ChainXXIOK to Reader
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-10-06 22:36:04 +02:00
Carsten Leue
e313f95865 Merge pull request #60 from IBM/cleue-alternative-monoid-for-option
fix: provide AltMonoid
2023-10-06 21:50:57 +02:00
Dr. Carsten Leue
5f25317f97 fix: provide AltMonoid
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-10-06 21:50:22 +02:00
Carsten Leue
f5aa2d6c15 Merge pull request #58 from IBM/renovate/major-go-dependencies
chore(deps): update actions/checkout action to v4
2023-10-05 15:20:29 +02:00
renovate[bot]
7262820624 chore(deps): update actions/checkout action to v4 2023-10-05 12:37:09 +00:00
Carsten Leue
c8fc10358b Merge pull request #56 from IBM/cleue-add-flap
fix: add flap and non empty array
2023-09-26 22:33:30 +02:00
Dr. Carsten Leue
1cd167541d fix: add flap and non empty array
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-09-26 22:32:53 +02:00
Carsten Leue
ebe94d71dc Merge pull request #55 from IBM/sample-match
fix: add match example
2023-09-24 22:17:05 +02:00
Dr. Carsten Leue
7ef6eb524b fix: add match example
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-09-24 22:15:01 +02:00
Carsten Leue
7b82e87bf3 Merge pull request #53 from IBM/cleue-add-missing-flatten-to-reader
fix: add missing Flatten to Reader
2023-09-20 17:54:07 +02:00
Dr. Carsten Leue
e8fdbe9f87 fix: add missing Flatten to Reader
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-09-20 17:53:45 +02:00
Carsten Leue
226c7039de Merge pull request #52 from IBM/cleue-add-memoize-to-reader
fix: add missing Memoize to readers
2023-09-20 16:04:51 +02:00
Dr. Carsten Leue
943ae8e009 fix: add missing Memoize to readers
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-09-20 15:56:02 +02:00
Carsten Leue
44c8441b07 Merge pull request #51 from IBM/cleue-rioe-tests
fix: add RIOE testcases
2023-09-19 22:33:56 +02:00
Dr. Carsten Leue
600aeae770 fix: add RIOE testcases
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-09-19 22:31:55 +02:00
Carsten Leue
f74a407294 Merge pull request #50 from IBM/cleue-add-some-tweaks
fix: add WithTempFile to ReaderIOEither
2023-09-19 18:07:06 +02:00
Dr. Carsten Leue
b15ab38861 fix: add WithTempFile to ReaderIOEither
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-09-19 18:06:32 +02:00
Carsten Leue
6532a83e82 Merge pull request #49 from IBM/cleue-add-ioeither-sample-with-return-tuple
fix: add missing IOO.FromIOEither
2023-09-19 12:29:23 +02:00
235 changed files with 8714 additions and 1149 deletions

View File

@@ -29,11 +29,11 @@ jobs:
go-version: [ '1.20.x', '1.21.x' ]
steps:
# full checkout for semantic-release
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
fetch-depth: 0
- name: Set up go ${{ matrix.go-version }}
uses: actions/setup-go@v4
uses: actions/setup-go@v5
with:
go-version: ${{ matrix.go-version }}
-
@@ -55,17 +55,17 @@ jobs:
steps:
# full checkout for semantic-release
- name: Full checkout
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
fetch-depth: 0
- name: Set up Node.js ${{ env.NODE_VERSION }}
uses: actions/setup-node@e33196f7422957bea03ed53f6fbb155025ffc7b8 # v3.7.0
uses: actions/setup-node@b39b52d1213e96004bfcb1c61a8a6fa8ab84f3e8 # v4.0.1
with:
node-version: ${{ env.NODE_VERSION }}
- name: Set up go ${{env.GO_VERSION}}
uses: actions/setup-go@v4
uses: actions/setup-go@v5
with:
go-version: ${{env.GO_VERSION}}

View File

@@ -14,6 +14,8 @@ go get github.com/IBM/fp-go
Refer to the [samples](./samples/).
Find API documentation [here](https://pkg.go.dev/github.com/IBM/fp-go)
## 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:
@@ -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.
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
| Opertator | 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
View 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
View 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)))
}

View File

@@ -17,6 +17,7 @@ package array
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"
M "github.com/IBM/fp-go/monoid"
@@ -52,6 +53,10 @@ func MonadMapRef[A, B any](as []A, f func(a *A) B) []B {
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 {
return F.Bind2nd(MonadMap[A, B], f)
}
@@ -60,18 +65,6 @@ func MapRef[A, B any](f func(a *A) B) func([]A) []B {
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 {
var result []A
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
}
func Filter[A any](pred func(A) bool) func([]A) []A {
return F.Bind2nd(filter[A], pred)
// Filter returns a new array with all elements from the original array that match a predicate
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)
}
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)
}
// FilterChain 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 A) O.Option[B]) func([]A) []B {
func MonadFilterMapWithIndex[A, B any](fa []A, f func(int, A) O.Option[B]) []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)
}
// 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.
func FilterChain[A, B any](f func(A) O.Option[[]B]) func([]A) []B {
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 {
return func(as []A) B {
return array.Reduce(as, f, initial)
}
return G.Reduce[[]A](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 {
@@ -210,7 +228,7 @@ func Last[A any](as []A) O.Option[A] {
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 {
count := len(as)
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)
return func(as []A) []A {
if IsEmpty(as) {
@@ -254,7 +272,7 @@ func Lookup[A any](idx int) func([]A) O.Option[A] {
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)
}
@@ -287,24 +305,47 @@ func ConstNil[A any]() []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)
}
// Copy creates a shallow copy of the array
func Copy[A any](b []A) []A {
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.
func FoldMap[A, B any](m M.Monoid[B]) func(func(A) B) func([]A) B {
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.
func Fold[A any](m M.Monoid[A]) func([]A) A {
return G.Fold[[]A](m)
}
func Push[A any](a A) func([]A) []A {
return G.Push[[]A](a)
func Push[A any](a A) EM.Endomorphism[[]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)
}

View File

@@ -59,6 +59,17 @@ func TestMap(t *testing.T) {
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) {
values := MakeBy(101, F.Identity[int])

77
array/example_any_test.go Normal file
View 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
}

View 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
View 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
View 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))
}

View File

@@ -18,6 +18,7 @@ package generic
import (
F "github.com/IBM/fp-go/function"
"github.com/IBM/fp-go/internal/array"
FC "github.com/IBM/fp-go/internal/functor"
M "github.com/IBM/fp-go/monoid"
O "github.com/IBM/fp-go/option"
"github.com/IBM/fp-go/tuple"
@@ -28,14 +29,46 @@ func Of[GA ~[]A, A any](value A) GA {
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)
}
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)
}
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
func From[GA ~[]A, A any](data ...A) GA {
return data
@@ -117,20 +150,56 @@ 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)
}
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 {
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 O.MonadFold(f(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 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)
}
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 {
return F.Flow2(
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])
}
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)
}
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] {
left := Empty[GA]()
right := Empty[GA]()
@@ -231,6 +304,11 @@ func Copy[AS ~[]A, A any](b AS) AS {
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 {
return func(f func(A) B) func(AS) B {
return func(as AS) B {
@@ -241,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 {
return func(as AS) A {
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)
}
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 F.Bind2nd(MonadFlap[FAB, GFAB, GB, A, B], a)
}
func Prepend[ENDO ~func(AS) AS, AS []A, A any](head A) ENDO {
return array.Prepend[ENDO](head)
}

97
array/generic/find.go Normal file
View 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))
}

32
array/generic/uniq.go Normal file
View 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))
}
}

View File

@@ -18,6 +18,7 @@ package array
import (
"github.com/IBM/fp-go/internal/array"
M "github.com/IBM/fp-go/monoid"
S "github.com/IBM/fp-go/semigroup"
)
func concat[T any](left, right []T) []T {
@@ -40,6 +41,10 @@ func Monoid[T any]() M.Monoid[[]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 {
return count + len(data)
}

130
array/nonempty/array.go Normal file
View File

@@ -0,0 +1,130 @@
// 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](as NonEmptyArray[A]) bool {
return false
}
func IsNonEmpty[A any](as 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 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)
}

17
array/uniq.go Normal file
View 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
View 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
View 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
View 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
})
)

View File

@@ -34,5 +34,6 @@ func Commands() []*C.Command {
IOEitherCommand(),
IOCommand(),
IOOptionCommand(),
DICommand(),
}
}

231
cli/di.go Normal file
View 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),
)
},
}
}

View File

@@ -115,16 +115,14 @@ func generateEitherize(f *os.File, i int) {
fmt.Fprintf(f, "t%d T%d", j, j)
}
fmt.Fprintf(f, ") Either[error, R] {\n")
fmt.Fprintf(f, " return TryCatchError(func() (R, error) {\n")
fmt.Fprintf(f, " return f(")
fmt.Fprintf(f, " return TryCatchError(f(")
for j := 0; j < i; j++ {
if j > 0 {
fmt.Fprintf(f, ", ")
}
fmt.Fprintf(f, "t%d", j)
}
fmt.Fprintln(f, ")")
fmt.Fprintln(f, " })")
fmt.Fprintln(f, "))")
fmt.Fprintln(f, " }")
fmt.Fprintln(f, "}")
}

View File

@@ -295,15 +295,16 @@ func recurseCurry(f *os.File, indent string, total, count int) {
func generateCurry(f *os.File, i int) {
// 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, "func Curry%d[T0", i)
for j := 1; j <= i; j++ {
fmt.Fprintf(f, "func Curry%d[FCT ~func(T0", i)
for j := 1; j < i; j++ {
fmt.Fprintf(f, ", T%d", j)
}
fmt.Fprintf(f, " any](f func(T0")
for j := 2; j <= i; j++ {
fmt.Fprintf(f, ", T%d", j-1)
fmt.Fprintf(f, ") T%d", i)
// type arguments
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++ {
fmt.Fprintf(f, " func(T%d)", j-1)
}
@@ -315,15 +316,16 @@ func generateCurry(f *os.File, i int) {
func generateUncurry(f *os.File, i int) {
// 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, "func Uncurry%d[T0", i)
for j := 1; j <= i; j++ {
fmt.Fprintf(f, "func Uncurry%d[FCT ~func(T0)", i)
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, " any](f")
for j := 1; j <= i; j++ {
fmt.Fprintf(f, " func(T%d)", j-1)
}
fmt.Fprintf(f, " T%d) func(", i)
fmt.Fprintf(f, " any](f FCT) func(")
for j := 1; j <= i; j++ {
if j > 1 {
fmt.Fprintf(f, ", ")

View File

@@ -22,30 +22,28 @@ import (
)
type Const[E, A any] struct {
Value E
value E
}
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 {
return c.Value
return c.value
}
func Of[E, A any](m M.Monoid[E]) func(A) Const[E, A] {
return func(a A) Const[E, A] {
return Make[E, A](m.Empty())
}
return F.Constant1[A](Make[E, A](m.Empty()))
}
func MonadMap[E, A, B any](fa Const[E, A], f 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] {
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))
}
}

View File

@@ -19,15 +19,14 @@ import (
"context"
E "github.com/IBM/fp-go/either"
ET "github.com/IBM/fp-go/either"
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 {
return IOE.MakeIO[GIO](func() E.Either[error, A] {
if err := context.Cause(ctx); err != nil {
return ET.Left[A](err)
return E.Left[A](err)
}
return ma()
})

View File

@@ -1,35 +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"
)
// TraverseArray transforms an array
func TraverseArray[A, B any](f func(A) Reader[B]) func([]A) Reader[[]B] {
return R.TraverseArray[Reader[B], Reader[[]B], []A](f)
}
// TraverseArrayWithIndex transforms an array
func TraverseArrayWithIndex[A, B any](f func(int, A) Reader[B]) func([]A) Reader[[]B] {
return R.TraverseArrayWithIndex[Reader[B], Reader[[]B], []A](f)
}
// 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)
}

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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")
}

View File

@@ -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)
}

View File

@@ -34,8 +34,6 @@ var (
func command(name string, args []string, in []byte) RE.ReaderEither[exec.CommandOutput] {
return func(ctx context.Context) E.Either[error, exec.CommandOutput] {
return E.TryCatchError(func() (exec.CommandOutput, error) {
return GE.Exec(ctx, name, args, in)
})
return E.TryCatchError(GE.Exec(ctx, name, args, in))
}
}

View File

@@ -18,7 +18,6 @@ package readereither
import (
"context"
R "github.com/IBM/fp-go/context/reader"
ET "github.com/IBM/fp-go/either"
O "github.com/IBM/fp-go/option"
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)
}
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] {
return RE.Left[ReaderEither[A]](l)
}
@@ -48,10 +39,6 @@ func Right[A any](r A) ReaderEither[A] {
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] {
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)
}
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] {
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] {
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] {
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] {
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)
}

View File

@@ -1,36 +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 (
IO "github.com/IBM/fp-go/io"
R "github.com/IBM/fp-go/readerio/generic"
)
// TraverseArray transforms an array
func TraverseArray[A, B any](f func(A) ReaderIO[B]) func([]A) ReaderIO[[]B] {
return R.TraverseArray[ReaderIO[B], ReaderIO[[]B], IO.IO[B], IO.IO[[]B], []A](f)
}
// TraverseArrayWithIndex transforms an array
func TraverseArrayWithIndex[A, B any](f func(int, A) ReaderIO[B]) func([]A) ReaderIO[[]B] {
return R.TraverseArrayWithIndex[ReaderIO[B], ReaderIO[[]B], IO.IO[B], IO.IO[[]B], []A](f)
}
// SequenceArray converts a homogeneous sequence of either into an either of sequence
func SequenceArray[A any](ma []ReaderIO[A]) ReaderIO[[]A] {
return R.SequenceArray[ReaderIO[A], ReaderIO[[]A]](ma)
}

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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")
}

View File

@@ -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)
}

View 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,
)
}

View File

@@ -25,25 +25,31 @@ import (
F "github.com/IBM/fp-go/function"
"github.com/IBM/fp-go/internal/file"
IOE "github.com/IBM/fp-go/ioeither"
IOEF "github.com/IBM/fp-go/ioeither/file"
)
var (
openIOE = IOE.Eitherize1(os.Open)
// Open opens a file for reading within the given context
Open = F.Flow3(
openIOE,
IOEF.Open,
RIOE.FromIOEither[*os.File],
RIOE.WithContext[*os.File],
)
// Remove removes a file by name
Remove = F.Flow2(
IOEF.Remove,
RIOE.FromIOEither[string],
)
)
// Close closes an object
func Close[C io.Closer](c C) RIOE.ReaderIOEither[any] {
return RIOE.FromIOEither(func() ET.Either[error, any] {
return ET.TryCatchError(func() (any, error) {
return c, c.Close()
})
})
return F.Pipe2(
c,
IOEF.Close[C],
RIOE.FromIOEither[any],
)
}
// ReadFile reads a file in the scope of a context

View File

@@ -19,9 +19,7 @@ import (
"context"
"fmt"
RIO "github.com/IBM/fp-go/context/readerio"
R "github.com/IBM/fp-go/context/readerioeither"
"github.com/IBM/fp-go/errors"
F "github.com/IBM/fp-go/function"
IO "github.com/IBM/fp-go/io"
J "github.com/IBM/fp-go/json"
@@ -37,20 +35,17 @@ func getData(r RecordType) string {
func ExampleReadFile() {
data := F.Pipe4(
data := F.Pipe3(
ReadFile("./data/file.json"),
R.ChainEitherK(J.Unmarshal[RecordType]),
R.ChainFirstIOK(IO.Logf[RecordType]("Log: %v")),
R.Map(getData),
R.GetOrElse(F.Flow2(
errors.ToString,
RIO.Of[string],
)),
)
result := data(context.Background())
fmt.Println(result())
// Output: Carsten
// Output:
// Right[<nil>, string](Carsten)
}

View 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)
}

View 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())())
}

View 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(ctx 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])
}

View File

@@ -2,7 +2,7 @@ package readerioeither
// Code generated by go generate; DO NOT EDIT.
// This file was generated by robots at
// 2023-09-12 13:44:14.1022311 +0200 CEST m=+0.017763401
// 2023-10-23 08:30:39.012572 +0200 CEST m=+0.008846101
import (
"context"

View 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,
)
}

View File

@@ -2,7 +2,7 @@ package generic
// Code generated by go generate; DO NOT EDIT.
// This file was generated by robots at
// 2023-09-12 13:44:14.1036885 +0200 CEST m=+0.019220801
// 2023-10-23 08:30:39.012572 +0200 CEST m=+0.008846101
import (
"context"

View File

@@ -100,6 +100,28 @@ func Map[
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[
GRA ~func(context.Context) GIOA,
GRB ~func(context.Context) GIOB,
@@ -410,10 +432,10 @@ func FromIOEither[
func FromIO[
GRA ~func(context.Context) GIOA,
GIOA ~func() E.Either[error, A],
GIOB ~func() A,
GIOA ~func() E.Either[error, A],
A any](t GIOB) GRA {
return RIE.FromIO[GRA](t)
}
@@ -458,6 +480,34 @@ func ChainIOK[
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[
GRA ~func(context.Context) GIOA,
GIOA ~func() E.Either[error, A],
@@ -521,7 +571,7 @@ func Timer[
](delay time.Duration) GRA {
return F.Pipe2(
IO.Now[func() time.Time](),
FromIO[GRA, GIOA, func() time.Time],
FromIO[GRA, func() time.Time],
Delay[GRA](delay),
)
}
@@ -551,3 +601,74 @@ func MonadAlt[LAZY ~func() GEA, GEA ~func(context.Context) GIOA, GIOA ~func() E.
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)
}

View 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],
)),
)
}

View File

@@ -166,3 +166,289 @@ func SequenceRecord[K comparable,
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])
}

View File

@@ -17,6 +17,7 @@ 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"
)
@@ -34,3 +35,22 @@ func ApplicativeMonoidSeq[A any](m M.Monoid[A]) M.Monoid[ReaderIOEither[A]] {
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],
)
}

View File

@@ -19,8 +19,6 @@ import (
"context"
"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"
ET "github.com/IBM/fp-go/either"
IO "github.com/IBM/fp-go/io"
@@ -33,14 +31,6 @@ func FromEither[A any](e ET.Either[error, A]) ReaderIOEither[A] {
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] {
return G.Left[ReaderIOEither[A]](l)
}
@@ -49,10 +39,6 @@ func Right[A any](r A) ReaderIOEither[A] {
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] {
return G.MonadMap[ReaderIOEither[A], ReaderIOEither[B]](fa, f)
}
@@ -61,6 +47,14 @@ func Map[A, B any](f func(A) B) func(ReaderIOEither[A]) ReaderIOEither[B] {
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] {
return G.MonadChain(ma, f)
}
@@ -95,30 +89,14 @@ func FromPredicate[A any](pred func(A) bool, onFalse func(A) error) func(A) Read
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] {
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] {
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] {
return G.MonadChainEitherK[ReaderIOEither[A], ReaderIOEither[B]](ma, f)
}
@@ -147,6 +125,10 @@ func FromIO[A any](t IO.IO[A]) ReaderIOEither[A] {
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
func Never[A any]() ReaderIOEither[A] {
return G.Never[ReaderIOEither[A]]()
@@ -201,3 +183,24 @@ func MonadAlt[A any](first ReaderIOEither[A], second L.Lazy[ReaderIOEither[A]])
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)
}

View File

@@ -162,3 +162,125 @@ func TestRegularApply(t *testing.T) {
res := applied(context.Background())()
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)
}

View File

@@ -13,14 +13,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
// Package reader implements a specialization of the Reader monad assuming a golang context as the context of the monad
package reader
package readerioeither
import (
"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
type Reader[A any] R.Reader[context.Context, A]
// WithLock executes the provided IO operation in the scope of a lock
func WithLock[A any](lock ReaderIOEither[context.CancelFunc]) func(fa ReaderIOEither[A]) ReaderIOEither[A] {
return G.WithLock[ReaderIOEither[A]](lock)
}

View File

@@ -48,3 +48,63 @@ func TraverseRecordWithIndex[K comparable, A, B any](f func(K, A) ReaderIOEither
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)
}
// 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)
}

38
di/app.go Normal file
View 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
View 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 10 --filename gen.go

170
di/erasure/injector.go Normal file
View 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
View 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
View 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]
}

1013
di/gen.go Normal file

File diff suppressed because it is too large Load Diff

32
di/injector.go Normal file
View 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
View 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(params ...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
View 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)())
}

197
di/token.go Normal file
View File

@@ -0,0 +1,197 @@
// 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 token[T any] struct {
name string
id string
flag int
toType func(val any) E.Either[error, T]
providerFactory O.Option[DIE.ProviderFactory]
}
func (t *token[T]) Id() string {
return t.id
}
func (t *token[T]) Flag() int {
return t.flag
}
func (t *token[T]) String() string {
return t.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.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]{name, id, typ, unerase, providerFactory}
}
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.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]{name, id, DIE.Identity, toIdentity, providerFactory},
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]{containerName, id, DIE.Multi | DIE.Identity, toContainer, providerFactory},
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]{itemName, id, DIE.Item | DIE.Identity, toItem, providerFactory},
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}
}

77
di/utils.go Normal file
View 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 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"
)
// 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(
toType[O.Option[any]](),
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(
toType[IOE.IOEither[error, any]](),
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(
toType[IOO.IOOption[any]](),
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(
toType[[]any](),
E.Chain(E.TraverseArray(item)),
)
}

84
di/utils_test.go Normal file
View 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")))))
}

View File

@@ -61,14 +61,14 @@ func SequenceArray[E, A any](ma []Either[E, A]) Either[E, []A] {
return SequenceArrayG[[]A](ma)
}
// CompactArrayG discards the none values and keeps the some values
// 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, len(fa)))
}, make(A2, 0, len(fa)))
}
// CompactArray discards the none values and keeps the some values
// 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)
}

18
either/array_test.go Normal file
View File

@@ -0,0 +1,18 @@
package either
import (
"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))
}

View File

@@ -46,12 +46,12 @@ func (s Either[E, A]) Format(f fmt.State, c rune) {
}
}
// 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 {
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 {
return !val.isLeft
}

View File

@@ -22,6 +22,8 @@ package either
import (
E "github.com/IBM/fp-go/errors"
F "github.com/IBM/fp-go/function"
FC "github.com/IBM/fp-go/internal/functor"
L "github.com/IBM/fp-go/lazy"
O "github.com/IBM/fp-go/option"
)
@@ -87,23 +89,23 @@ func MonadChainFirst[E, A, B any](ma Either[E, A], f func(a A) Either[E, B]) Eit
})
}
func MonadChainTo[E, A, B any](ma Either[E, A], mb Either[E, B]) Either[E, B] {
func MonadChainTo[A, E, B any](ma Either[E, A], mb Either[E, B]) Either[E, B] {
return mb
}
func MonadChainOptionK[A, B, E any](onNone func() E, ma Either[E, A], f func(A) O.Option[B]) Either[E, B] {
return MonadChain(ma, F.Flow2(f, FromOption[E, B](onNone)))
return MonadChain(ma, F.Flow2(f, FromOption[B](onNone)))
}
func ChainOptionK[A, B, E any](onNone func() E) func(func(A) O.Option[B]) func(Either[E, A]) Either[E, B] {
from := FromOption[E, B](onNone)
from := FromOption[B](onNone)
return func(f func(A) O.Option[B]) func(Either[E, A]) Either[E, B] {
return Chain(F.Flow2(f, from))
}
}
func ChainTo[E, A, B any](mb Either[E, B]) func(Either[E, A]) Either[E, B] {
return F.Bind2nd(MonadChainTo[E, A, B], mb)
func ChainTo[A, E, B any](mb Either[E, B]) func(Either[E, A]) Either[E, B] {
return F.Bind2nd(MonadChainTo[A, E, B], mb)
}
func Chain[E, A, B any](f func(a A) Either[E, B]) func(Either[E, A]) Either[E, B] {
@@ -118,16 +120,15 @@ func Flatten[E, A any](mma Either[E, Either[E, A]]) Either[E, A] {
return MonadChain(mma, F.Identity[Either[E, A]])
}
func TryCatch[FA ~func() (A, error), FE func(error) E, E, A any](f FA, onThrow FE) Either[E, A] {
val, err := f()
func TryCatch[FE func(error) E, E, A any](val A, err error, onThrow FE) Either[E, A] {
if err != nil {
return F.Pipe2(err, onThrow, Left[A, E])
}
return F.Pipe1(val, Right[E, A])
}
func TryCatchError[F ~func() (A, error), A any](f F) Either[error, A] {
return TryCatch(f, E.IdentityError)
func TryCatchError[A any](val A, err error) Either[error, A] {
return TryCatch(val, err, E.IdentityError)
}
func Sequence2[E, T1, T2, R any](f func(T1, T2) Either[E, R]) func(Either[E, T1], Either[E, T2]) Either[E, R] {
@@ -142,7 +143,7 @@ func Sequence3[E, T1, T2, T3, R any](f func(T1, T2, T3) Either[E, R]) func(Eithe
}
}
func FromOption[E, A any](onNone func() E) func(O.Option[A]) Either[E, A] {
func FromOption[A, E any](onNone func() E) func(O.Option[A]) Either[E, A] {
return O.Fold(F.Nullary2(onNone, Left[A, E]), Right[E, A])
}
@@ -152,9 +153,7 @@ func ToOption[E, A any](ma Either[E, A]) O.Option[A] {
func FromError[A any](f func(a A) error) func(A) Either[error, A] {
return func(a A) Either[error, A] {
return TryCatchError(func() (A, error) {
return a, f(a)
})
return TryCatchError(a, f(a))
}
}
@@ -197,11 +196,11 @@ func Reduce[E, A, B any](f func(B, A) B, initial B) func(Either[E, A]) B {
)
}
func AltW[E, E1, A any](that func() Either[E1, A]) func(Either[E, A]) Either[E1, A] {
func AltW[E, E1, A any](that L.Lazy[Either[E1, A]]) func(Either[E, A]) Either[E1, A] {
return Fold(F.Ignore1of1[E](that), Right[E1, A])
}
func Alt[E, A any](that func() Either[E, A]) func(Either[E, A]) Either[E, A] {
func Alt[E, A any](that L.Lazy[Either[E, A]]) func(Either[E, A]) Either[E, A] {
return AltW[E](that)
}
@@ -245,3 +244,15 @@ func MonadSequence3[E, T1, T2, T3, R any](e1 Either[E, T1], e2 Either[E, T2], e3
func Swap[E, A any](val Either[E, A]) Either[A, E] {
return MonadFold(val, Right[A, E], Left[E, A])
}
func MonadFlap[E, B, A any](fab Either[E, func(A) B], a A) Either[E, B] {
return FC.MonadFlap(MonadMap[E, func(A) B, B], fab, a)
}
func Flap[E, B, A any](a A) func(Either[E, func(A) B]) Either[E, B] {
return F.Bind2nd(MonadFlap[E, B, A], a)
}
func MonadAlt[E, A any](fa Either[E, A], that L.Lazy[Either[E, A]]) Either[E, A] {
return MonadFold(fa, F.Ignore1of1[E](that), Of[E, A])
}

View File

@@ -112,6 +112,6 @@ func TestChainOptionK(t *testing.T) {
}
func TestFromOption(t *testing.T) {
assert.Equal(t, Left[int]("none"), FromOption[string, int](F.Constant("none"))(O.None[int]()))
assert.Equal(t, Right[string](1), FromOption[string, int](F.Constant("none"))(O.Some(1)))
assert.Equal(t, Left[int]("none"), FromOption[int](F.Constant("none"))(O.None[int]()))
assert.Equal(t, Right[string](1), FromOption[int](F.Constant("none"))(O.Some(1)))
}

View File

@@ -27,12 +27,10 @@ import (
var (
// Command executes a command
// use this version if the command does not produce any side effect, i.e. if the output is uniquely determined by by the input
// typically you'd rather use the IOEither version of the command
// typically you'd rather use the [IOEither] version of the command
Command = F.Curry3(command)
)
func command(name string, args []string, in []byte) E.Either[error, exec.CommandOutput] {
return E.TryCatchError(func() (exec.CommandOutput, error) {
return GE.Exec(context.Background(), name, args, in)
})
return E.TryCatchError(GE.Exec(context.Background(), name, args, in))
}

View File

@@ -1,6 +1,6 @@
// Code generated by go generate; DO NOT EDIT.
// This file was generated by robots at
// 2023-09-12 13:44:15.6356542 +0200 CEST m=+0.009418901
// 2023-10-23 08:30:40.410373 +0200 CEST m=+0.010337601
package either
@@ -13,9 +13,7 @@ import (
// The inverse function is [Uneitherize0]
func Eitherize0[F ~func() (R, error), R any](f F) func() Either[error, R] {
return func() Either[error, R] {
return TryCatchError(func() (R, error) {
return f()
})
return TryCatchError(f())
}
}
@@ -31,9 +29,7 @@ func Uneitherize0[F ~func() Either[error, R], R any](f F) func() (R, error) {
// The inverse function is [Uneitherize1]
func Eitherize1[F ~func(T0) (R, error), T0, R any](f F) func(T0) Either[error, R] {
return func(t0 T0) Either[error, R] {
return TryCatchError(func() (R, error) {
return f(t0)
})
return TryCatchError(f(t0))
}
}
@@ -76,9 +72,7 @@ func TraverseTuple1[F1 ~func(A1) Either[E, T1], E, A1, T1 any](f1 F1) func(T.Tup
// The inverse function is [Uneitherize2]
func Eitherize2[F ~func(T0, T1) (R, error), T0, T1, R any](f F) func(T0, T1) Either[error, R] {
return func(t0 T0, t1 T1) Either[error, R] {
return TryCatchError(func() (R, error) {
return f(t0, t1)
})
return TryCatchError(f(t0, t1))
}
}
@@ -126,9 +120,7 @@ func TraverseTuple2[F1 ~func(A1) Either[E, T1], F2 ~func(A2) Either[E, T2], E, A
// The inverse function is [Uneitherize3]
func Eitherize3[F ~func(T0, T1, T2) (R, error), T0, T1, T2, R any](f F) func(T0, T1, T2) Either[error, R] {
return func(t0 T0, t1 T1, t2 T2) Either[error, R] {
return TryCatchError(func() (R, error) {
return f(t0, t1, t2)
})
return TryCatchError(f(t0, t1, t2))
}
}
@@ -181,9 +173,7 @@ func TraverseTuple3[F1 ~func(A1) Either[E, T1], F2 ~func(A2) Either[E, T2], F3 ~
// The inverse function is [Uneitherize4]
func Eitherize4[F ~func(T0, T1, T2, T3) (R, error), T0, T1, T2, T3, R any](f F) func(T0, T1, T2, T3) Either[error, R] {
return func(t0 T0, t1 T1, t2 T2, t3 T3) Either[error, R] {
return TryCatchError(func() (R, error) {
return f(t0, t1, t2, t3)
})
return TryCatchError(f(t0, t1, t2, t3))
}
}
@@ -241,9 +231,7 @@ func TraverseTuple4[F1 ~func(A1) Either[E, T1], F2 ~func(A2) Either[E, T2], F3 ~
// The inverse function is [Uneitherize5]
func Eitherize5[F ~func(T0, T1, T2, T3, T4) (R, error), T0, T1, T2, T3, T4, R any](f F) func(T0, T1, T2, T3, T4) Either[error, R] {
return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4) Either[error, R] {
return TryCatchError(func() (R, error) {
return f(t0, t1, t2, t3, t4)
})
return TryCatchError(f(t0, t1, t2, t3, t4))
}
}
@@ -306,9 +294,7 @@ func TraverseTuple5[F1 ~func(A1) Either[E, T1], F2 ~func(A2) Either[E, T2], F3 ~
// The inverse function is [Uneitherize6]
func Eitherize6[F ~func(T0, T1, T2, T3, T4, T5) (R, error), T0, T1, T2, T3, T4, T5, R any](f F) func(T0, T1, T2, T3, T4, T5) Either[error, R] {
return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5) Either[error, R] {
return TryCatchError(func() (R, error) {
return f(t0, t1, t2, t3, t4, t5)
})
return TryCatchError(f(t0, t1, t2, t3, t4, t5))
}
}
@@ -376,9 +362,7 @@ func TraverseTuple6[F1 ~func(A1) Either[E, T1], F2 ~func(A2) Either[E, T2], F3 ~
// The inverse function is [Uneitherize7]
func Eitherize7[F ~func(T0, T1, T2, T3, T4, T5, T6) (R, error), T0, T1, T2, T3, T4, T5, T6, R any](f F) func(T0, T1, T2, T3, T4, T5, T6) Either[error, R] {
return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6) Either[error, R] {
return TryCatchError(func() (R, error) {
return f(t0, t1, t2, t3, t4, t5, t6)
})
return TryCatchError(f(t0, t1, t2, t3, t4, t5, t6))
}
}
@@ -451,9 +435,7 @@ func TraverseTuple7[F1 ~func(A1) Either[E, T1], F2 ~func(A2) Either[E, T2], F3 ~
// The inverse function is [Uneitherize8]
func Eitherize8[F ~func(T0, T1, T2, T3, T4, T5, T6, T7) (R, error), T0, T1, T2, T3, T4, T5, T6, T7, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7) Either[error, R] {
return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7) Either[error, R] {
return TryCatchError(func() (R, error) {
return f(t0, t1, t2, t3, t4, t5, t6, t7)
})
return TryCatchError(f(t0, t1, t2, t3, t4, t5, t6, t7))
}
}
@@ -531,9 +513,7 @@ func TraverseTuple8[F1 ~func(A1) Either[E, T1], F2 ~func(A2) Either[E, T2], F3 ~
// The inverse function is [Uneitherize9]
func Eitherize9[F ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8) (R, error), T0, T1, T2, T3, T4, T5, T6, T7, T8, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7, T8) Either[error, R] {
return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8) Either[error, R] {
return TryCatchError(func() (R, error) {
return f(t0, t1, t2, t3, t4, t5, t6, t7, t8)
})
return TryCatchError(f(t0, t1, t2, t3, t4, t5, t6, t7, t8))
}
}
@@ -616,9 +596,7 @@ func TraverseTuple9[F1 ~func(A1) Either[E, T1], F2 ~func(A2) Either[E, T2], F3 ~
// The inverse function is [Uneitherize10]
func Eitherize10[F ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) (R, error), T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) Either[error, R] {
return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9) Either[error, R] {
return TryCatchError(func() (R, error) {
return f(t0, t1, t2, t3, t4, t5, t6, t7, t8, t9)
})
return TryCatchError(f(t0, t1, t2, t3, t4, t5, t6, t7, t8, t9))
}
}

View File

@@ -35,17 +35,13 @@ var (
func bodyRequest(method string) func(string) func([]byte) E.Either[error, *http.Request] {
return func(url string) func([]byte) E.Either[error, *http.Request] {
return func(body []byte) E.Either[error, *http.Request] {
return E.TryCatchError(func() (*http.Request, error) {
return http.NewRequest(method, url, bytes.NewReader(body))
})
return E.TryCatchError(http.NewRequest(method, url, bytes.NewReader(body)))
}
}
}
func noBodyRequest(method string) func(string) E.Either[error, *http.Request] {
return func(url string) E.Either[error, *http.Request] {
return E.TryCatchError(func() (*http.Request, error) {
return http.NewRequest(method, url, nil)
})
return E.TryCatchError(http.NewRequest(method, url, nil))
}
}

View File

@@ -41,7 +41,7 @@ func Logger[E, A any](loggers ...*log.Logger) func(string) func(Either[E, A]) Ei
return func(ma Either[E, A]) Either[E, A] {
return F.Pipe1(
delegate(ma),
ChainTo[E, A](ma),
ChainTo[A](ma),
)
}
}

40
either/monoid.go Normal file
View File

@@ -0,0 +1,40 @@
// 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 (
L "github.com/IBM/fp-go/lazy"
M "github.com/IBM/fp-go/monoid"
)
// AlternativeMonoid is the alternative [Monoid] for an [Either]
func AlternativeMonoid[E, A any](m M.Monoid[A]) M.Monoid[Either[E, A]] {
return M.AlternativeMonoid(
Of[E, A],
MonadMap[E, A, func(A) A],
MonadAp[A, E, A],
MonadAlt[E, A],
m,
)
}
// AltMonoid is the alternative [Monoid] for an [Either]
func AltMonoid[E, A any](zero L.Lazy[Either[E, A]]) M.Monoid[Either[E, A]] {
return M.AltMonoid(
zero,
MonadAlt[E, A],
)
}

View File

@@ -25,20 +25,14 @@ import (
func TestWithResource(t *testing.T) {
onCreate := func() Either[error, *os.File] {
return TryCatchError(func() (*os.File, error) {
return os.CreateTemp("", "*")
})
return TryCatchError(os.CreateTemp("", "*"))
}
onDelete := F.Flow2(
func(f *os.File) Either[error, string] {
return TryCatchError(func() (string, error) {
return f.Name(), f.Close()
})
return TryCatchError(f.Name(), f.Close())
},
Chain(func(name string) Either[error, any] {
return TryCatchError(func() (any, error) {
return name, os.Remove(name)
})
return TryCatchError(any(name), os.Remove(name))
}),
)

View File

@@ -13,17 +13,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package file
package either
import (
"io"
IOE "github.com/IBM/fp-go/ioeither"
S "github.com/IBM/fp-go/semigroup"
)
// onClose closes a closeable resource
func onClose[R io.Closer](r R) IOE.IOEither[error, R] {
return IOE.TryCatchError(func() (R, error) {
return r, r.Close()
})
// AltSemigroup is the alternative [Semigroup] for an [Either]
func AltSemigroup[E, A any]() S.Semigroup[Either[E, A]] {
return S.AltSemigroup(
MonadAlt[E, A],
)
}

View File

@@ -17,80 +17,60 @@ package either
func Variadic0[V, R any](f func([]V) (R, error)) func(...V) Either[error, R] {
return func(v ...V) Either[error, R] {
return TryCatchError(func() (R, error) {
return f(v)
})
return TryCatchError(f(v))
}
}
func Variadic1[T1, V, R any](f func(T1, []V) (R, error)) func(T1, ...V) Either[error, R] {
return func(t1 T1, v ...V) Either[error, R] {
return TryCatchError(func() (R, error) {
return f(t1, v)
})
return TryCatchError(f(t1, v))
}
}
func Variadic2[T1, T2, V, R any](f func(T1, T2, []V) (R, error)) func(T1, T2, ...V) Either[error, R] {
return func(t1 T1, t2 T2, v ...V) Either[error, R] {
return TryCatchError(func() (R, error) {
return f(t1, t2, v)
})
return TryCatchError(f(t1, t2, v))
}
}
func Variadic3[T1, T2, T3, V, R any](f func(T1, T2, T3, []V) (R, error)) func(T1, T2, T3, ...V) Either[error, R] {
return func(t1 T1, t2 T2, t3 T3, v ...V) Either[error, R] {
return TryCatchError(func() (R, error) {
return f(t1, t2, t3, v)
})
return TryCatchError(f(t1, t2, t3, v))
}
}
func Variadic4[T1, T2, T3, T4, V, R any](f func(T1, T2, T3, T4, []V) (R, error)) func(T1, T2, T3, T4, ...V) Either[error, R] {
return func(t1 T1, t2 T2, t3 T3, t4 T4, v ...V) Either[error, R] {
return TryCatchError(func() (R, error) {
return f(t1, t2, t3, t4, v)
})
return TryCatchError(f(t1, t2, t3, t4, v))
}
}
func Unvariadic0[V, R any](f func(...V) (R, error)) func([]V) Either[error, R] {
return func(v []V) Either[error, R] {
return TryCatchError(func() (R, error) {
return f(v...)
})
return TryCatchError(f(v...))
}
}
func Unvariadic1[T1, V, R any](f func(T1, ...V) (R, error)) func(T1, []V) Either[error, R] {
return func(t1 T1, v []V) Either[error, R] {
return TryCatchError(func() (R, error) {
return f(t1, v...)
})
return TryCatchError(f(t1, v...))
}
}
func Unvariadic2[T1, T2, V, R any](f func(T1, T2, ...V) (R, error)) func(T1, T2, []V) Either[error, R] {
return func(t1 T1, t2 T2, v []V) Either[error, R] {
return TryCatchError(func() (R, error) {
return f(t1, t2, v...)
})
return TryCatchError(f(t1, t2, v...))
}
}
func Unvariadic3[T1, T2, T3, V, R any](f func(T1, T2, T3, ...V) (R, error)) func(T1, T2, T3, []V) Either[error, R] {
return func(t1 T1, t2 T2, t3 T3, v []V) Either[error, R] {
return TryCatchError(func() (R, error) {
return f(t1, t2, t3, v...)
})
return TryCatchError(f(t1, t2, t3, v...))
}
}
func Unvariadic4[T1, T2, T3, T4, V, R any](f func(T1, T2, T3, T4, ...V) (R, error)) func(T1, T2, T3, T4, []V) Either[error, R] {
return func(t1 T1, t2 T2, t3 T3, t4 T4, v []V) Either[error, R] {
return TryCatchError(func() (R, error) {
return f(t1, t2, t3, t4, v...)
})
return TryCatchError(f(t1, t2, t3, t4, v...))
}
}

30
endomorphism/curry.go Normal file
View 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 endomorphism
import (
G "github.com/IBM/fp-go/endomorphism/generic"
)
// Curry2 curries a binary function
func Curry2[FCT ~func(T0, T1) T1, T0, T1 any](f FCT) func(T0) Endomorphism[T1] {
return G.Curry2[Endomorphism[T1]](f)
}
// Curry3 curries a ternary function
func Curry3[FCT ~func(T0, T1, T2) T2, T0, T1, T2 any](f FCT) func(T0) func(T1) Endomorphism[T2] {
return G.Curry3[Endomorphism[T2]](f)
}

60
endomorphism/endo.go Normal file
View File

@@ -0,0 +1,60 @@
// 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 endomorphism
import (
G "github.com/IBM/fp-go/endomorphism/generic"
)
func MonadAp[A any](fab Endomorphism[A], fa A) A {
return G.MonadAp[Endomorphism[A]](fab, fa)
}
func Ap[A any](fa A) func(Endomorphism[A]) A {
return G.Ap[Endomorphism[A]](fa)
}
func MonadFlap[A any](fab Endomorphism[A], a A) A {
return G.MonadFlap[Endomorphism[A]](fab, a)
}
func Flap[A any](a A) func(Endomorphism[A]) A {
return G.Flap[Endomorphism[A]](a)
}
func MonadMap[A any](fa A, f Endomorphism[A]) A {
return G.MonadMap[Endomorphism[A]](fa, f)
}
func Map[A any](f Endomorphism[A]) Endomorphism[A] {
return G.Map[Endomorphism[A]](f)
}
func MonadChain[A any](ma A, f Endomorphism[A]) A {
return G.MonadChain[Endomorphism[A]](ma, f)
}
func Chain[A any](f Endomorphism[A]) Endomorphism[A] {
return G.Chain[Endomorphism[A], A](f)
}
func MonadChainFirst[A any](fa A, f Endomorphism[A]) A {
return G.MonadChainFirst[Endomorphism[A]](fa, f)
}
func ChainFirst[A any](f Endomorphism[A]) Endomorphism[A] {
return G.ChainFirst[Endomorphism[A]](f)
}

View File

@@ -0,0 +1,36 @@
// 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
// Curry2 is a duplicate of [F.Curry2] but because of the type system it's not compatible otherwise
func Curry2[GT1 ~func(T1) T1, FCT ~func(T0, T1) T1, T0, T1 any](f FCT) func(T0) GT1 {
return func(t0 T0) GT1 {
return func(t1 T1) T1 {
return f(t0, t1)
}
}
}
// Curry2 is a duplicate of [F.Curry2] but because of the type system it's not compatible otherwise
func Curry3[GT2 ~func(T2) T2, FCT ~func(T0, T1, T2) T2, T0, T1, T2 any](f FCT) func(T0) func(T1) GT2 {
return func(t0 T0) func(T1) GT2 {
return func(t1 T1) GT2 {
return func(t2 T2) T2 {
return f(t0, t1, t2)
}
}
}
}

View File

@@ -0,0 +1,60 @@
// 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 (
I "github.com/IBM/fp-go/identity/generic"
)
func MonadAp[GA ~func(A) A, A any](fab GA, fa A) A {
return I.MonadAp[GA, A, A](fab, fa)
}
func Ap[GA ~func(A) A, A any](fa A) func(GA) A {
return I.Ap[GA, A, A](fa)
}
func MonadFlap[GA ~func(A) A, A any](fab GA, a A) A {
return I.MonadFlap[GA, A, A](fab, a)
}
func Flap[GA ~func(A) A, A any](a A) func(GA) A {
return I.Flap[GA, A, A](a)
}
func MonadMap[GA ~func(A) A, A any](fa A, f GA) A {
return I.MonadMap[GA, A, A](fa, f)
}
func Map[GA ~func(A) A, A any](f GA) GA {
return I.Map[GA, A, A](f)
}
func MonadChain[GA ~func(A) A, A any](ma A, f GA) A {
return I.MonadChain[GA, A, A](ma, f)
}
func Chain[GA ~func(A) A, A any](f GA) GA {
return I.Chain[GA, A](f)
}
func MonadChainFirst[GA ~func(A) A, A any](fa A, f GA) A {
return I.MonadChainFirst[GA, A, A](fa, f)
}
func ChainFirst[GA ~func(A) A, A any](f GA) GA {
return I.ChainFirst[GA, A, A](f)
}

View 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 generic
import (
F "github.com/IBM/fp-go/function"
M "github.com/IBM/fp-go/monoid"
S "github.com/IBM/fp-go/semigroup"
)
// Of converts any function to an [Endomorphism]
func Of[ENDO ~func(A) A, F ~func(A) A, A any](f F) ENDO {
return func(a A) A {
return f(a)
}
}
func Identity[ENDO ~func(A) A, A any]() ENDO {
return Of[ENDO](F.Identity[A])
}
func Compose[ENDO ~func(A) A, A any](f1, f2 ENDO) ENDO {
return Of[ENDO](F.Flow2(f1, f2))
}
// Semigroup for the Endomorphism where the `concat` operation is the usual function composition.
func Semigroup[ENDO ~func(A) A, A any]() S.Semigroup[ENDO] {
return S.MakeSemigroup(Compose[ENDO])
}
// Monoid for the Endomorphism where the `concat` operation is the usual function composition.
func Monoid[ENDO ~func(A) A, A any]() M.Monoid[ENDO] {
return M.MakeMonoid(Compose[ENDO], Identity[ENDO]())
}

View File

@@ -16,17 +16,30 @@
package endomorphism
import (
F "github.com/IBM/fp-go/function"
G "github.com/IBM/fp-go/endomorphism/generic"
M "github.com/IBM/fp-go/monoid"
S "github.com/IBM/fp-go/semigroup"
)
// Endomorphism is a function that
type Endomorphism[A any] func(A) A
// Of converts any function to an [Endomorphism]
func Of[F ~func(A) A, A any](f F) Endomorphism[A] {
return G.Of[Endomorphism[A]](f)
}
// Identity returns the identity [Endomorphism]
func Identity[A any]() Endomorphism[A] {
return G.Identity[Endomorphism[A]]()
}
// Semigroup for the Endomorphism where the `concat` operation is the usual function composition.
func Semigroup[A any]() S.Semigroup[func(A) A] {
return S.MakeSemigroup(F.Flow2[func(A) A, func(A) A])
func Semigroup[A any]() S.Semigroup[Endomorphism[A]] {
return G.Semigroup[Endomorphism[A]]()
}
// Monoid for the Endomorphism where the `concat` operation is the usual function composition.
func Monoid[A any]() M.Monoid[func(A) A] {
return M.MakeMonoid(F.Flow2[func(A) A, func(A) A], F.Identity[A])
func Monoid[A any]() M.Monoid[Endomorphism[A]] {
return G.Monoid[Endomorphism[A]]()
}

View File

@@ -16,6 +16,8 @@
package erasure
import (
E "github.com/IBM/fp-go/either"
"github.com/IBM/fp-go/errors"
F "github.com/IBM/fp-go/function"
)
@@ -29,6 +31,15 @@ func Unerase[T any](t any) T {
return *t.(*T)
}
// SafeUnerase converts an erased variable back to its original value
func SafeUnerase[T any](t any) E.Either[error, T] {
return F.Pipe2(
t,
E.ToType[*T](errors.OnSome[any]("Value of type [%T] is not erased")),
E.Map[error](F.Deref[T]),
)
}
// Erase0 converts a type safe function into an erased function
func Erase0[T1 any](f func() T1) func() any {
return F.Nullary2(f, Erase[T1])

View File

@@ -20,11 +20,14 @@ import (
)
type (
// command output
// CommandOutput represents the output of executing a command. The first field in the [Tuple2] is
// stdout, the second one is stderr. Use [StdOut] and [StdErr] to access these fields
CommandOutput = T.Tuple2[[]byte, []byte]
)
var (
// StdOut returns the field of a [CommandOutput] representing `stdout`
StdOut = T.First[[]byte, []byte]
// StdErr returns the field of a [CommandOutput] representing `stderr`
StdErr = T.Second[[]byte, []byte]
)

View File

@@ -15,9 +15,24 @@
package file
import "os"
import (
"io"
"path/filepath"
)
// GetName is the getter for the `Name` property of [os.File]
func GetName(f *os.File) string {
return f.Name()
// Join appends a filename to a root path
func Join(name string) func(root string) string {
return func(root string) string {
return filepath.Join(root, name)
}
}
// ToReader converts a [io.Reader]
func ToReader[R io.Reader](r R) io.Reader {
return r
}
// ToCloser converts a [io.Closer]
func ToCloser[C io.Closer](c C) io.Closer {
return c
}

View File

@@ -1,6 +1,6 @@
// Code generated by go generate; DO NOT EDIT.
// This file was generated by robots at
// 2023-09-12 13:44:23.4226437 +0200 CEST m=+0.011841001
// 2023-12-18 09:38:59.1616876 +0100 CET m=+0.008641801
package function

View File

@@ -28,3 +28,14 @@ func Memoize[K comparable, T any](f func(K) T) func(K) T {
func ContramapMemoize[A any, K comparable, T any](kf func(A) K) func(func(A) T) func(A) T {
return G.ContramapMemoize[func(A) T](kf)
}
// CacheCallback converts a unary function into a unary function that caches the value depending on the parameter
func CacheCallback[
A any, K comparable, T any](kf func(A) K, getOrCreate func(K, func() func() T) func() T) func(func(A) T) func(A) T {
return G.CacheCallback[func(func(A) T) func(A) T](kf, getOrCreate)
}
// SingleElementCache creates a cache function for use with the [CacheCallback] method that has a maximum capacity of one single item
func SingleElementCache[K comparable, T any]() func(K, func() func() T) func() T {
return G.SingleElementCache[func() func() T, K]()
}

View File

@@ -16,6 +16,8 @@
package function
import (
"fmt"
"math/rand"
"testing"
"github.com/stretchr/testify/assert"
@@ -48,3 +50,21 @@ func TestCache(t *testing.T) {
assert.Equal(t, 10, cached(10))
assert.Equal(t, 2, count)
}
func TestSingleElementCache(t *testing.T) {
f := func(key string) string {
return fmt.Sprintf("%s: %d", key, rand.Int())
}
cb := CacheCallback(func(s string) string { return s }, SingleElementCache[string, string]())
cf := cb(f)
v1 := cf("1")
v2 := cf("1")
v3 := cf("2")
v4 := cf("1")
assert.Equal(t, v1, v2)
assert.NotEqual(t, v2, v3)
assert.NotEqual(t, v3, v4)
assert.NotEqual(t, v1, v4)
}

25
function/flip.go Normal file
View File

@@ -0,0 +1,25 @@
// 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 function
// Flip reverses the order of parameters of a curried function
func Flip[T1, T2, R any](f func(T1) func(T2) R) func(T2) func(T1) R {
return func(t2 T2) func(T1) R {
return func(t1 T1) R {
return f(t1)(t2)
}
}
}

36
function/flip_test.go Normal file
View File

@@ -0,0 +1,36 @@
// 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 function
import (
"fmt"
"testing"
"github.com/stretchr/testify/assert"
)
func TestFlip(t *testing.T) {
x := Curry2(func(a, b string) string {
return fmt.Sprintf("%s:%s", a, b)
})
assert.Equal(t, "a:b", x("a")("b"))
y := Flip(x)
assert.Equal(t, "b:a", y("a")("b"))
}

View File

@@ -1,6 +1,6 @@
// Code generated by go generate; DO NOT EDIT.
// This file was generated by robots at
// 2023-09-12 13:44:17.0002767 +0200 CEST m=+0.008233101
// 2023-12-18 09:38:51.4946446 +0100 CET m=+0.008838401
package function
@@ -55,7 +55,7 @@ func Nullary1[F1 ~func() T1, T1 any](f1 F1) func() T1 {
// Curry1 takes a function with 1 parameters and returns a cascade of functions each taking only one parameter.
// The inverse function is [Uncurry1]
func Curry1[T0, T1 any](f func(T0) T1) func(T0) T1 {
func Curry1[FCT ~func(T0) T1, T0, T1 any](f FCT) func(T0) T1 {
return func(t0 T0) T1 {
return f(t0)
}
@@ -63,7 +63,7 @@ func Curry1[T0, T1 any](f func(T0) T1) func(T0) T1 {
// Uncurry1 takes a cascade of 1 functions each taking only one parameter and returns a function with 1 parameters .
// The inverse function is [Curry1]
func Uncurry1[T0, T1 any](f func(T0) T1) func(T0) T1 {
func Uncurry1[FCT ~func(T0) T1, T0, T1 any](f FCT) func(T0) T1 {
return func(t0 T0) T1 {
return f(t0)
}
@@ -115,7 +115,7 @@ func Nullary2[F1 ~func() T1, F2 ~func(T1) T2, T1, T2 any](f1 F1, f2 F2) func() T
// Curry2 takes a function with 2 parameters and returns a cascade of functions each taking only one parameter.
// The inverse function is [Uncurry2]
func Curry2[T0, T1, T2 any](f func(T0, T1) T2) func(T0) func(T1) T2 {
func Curry2[FCT ~func(T0, T1) T2, T0, T1, T2 any](f FCT) func(T0) func(T1) T2 {
return func(t0 T0) func(t1 T1) T2 {
return func(t1 T1) T2 {
return f(t0, t1)
@@ -125,7 +125,7 @@ func Curry2[T0, T1, T2 any](f func(T0, T1) T2) func(T0) func(T1) T2 {
// Uncurry2 takes a cascade of 2 functions each taking only one parameter and returns a function with 2 parameters .
// The inverse function is [Curry2]
func Uncurry2[T0, T1, T2 any](f func(T0) func(T1) T2) func(T0, T1) T2 {
func Uncurry2[FCT ~func(T0) func(T1) T2, T0, T1, T2 any](f FCT) func(T0, T1) T2 {
return func(t0 T0, t1 T1) T2 {
return f(t0)(t1)
}
@@ -178,7 +178,7 @@ func Nullary3[F1 ~func() T1, F2 ~func(T1) T2, F3 ~func(T2) T3, T1, T2, T3 any](f
// Curry3 takes a function with 3 parameters and returns a cascade of functions each taking only one parameter.
// The inverse function is [Uncurry3]
func Curry3[T0, T1, T2, T3 any](f func(T0, T1, T2) T3) func(T0) func(T1) func(T2) T3 {
func Curry3[FCT ~func(T0, T1, T2) T3, T0, T1, T2, T3 any](f FCT) func(T0) func(T1) func(T2) T3 {
return func(t0 T0) func(t1 T1) func(t2 T2) T3 {
return func(t1 T1) func(t2 T2) T3 {
return func(t2 T2) T3 {
@@ -190,7 +190,7 @@ func Curry3[T0, T1, T2, T3 any](f func(T0, T1, T2) T3) func(T0) func(T1) func(T2
// Uncurry3 takes a cascade of 3 functions each taking only one parameter and returns a function with 3 parameters .
// The inverse function is [Curry3]
func Uncurry3[T0, T1, T2, T3 any](f func(T0) func(T1) func(T2) T3) func(T0, T1, T2) T3 {
func Uncurry3[FCT ~func(T0) func(T1) func(T2) T3, T0, T1, T2, T3 any](f FCT) func(T0, T1, T2) T3 {
return func(t0 T0, t1 T1, t2 T2) T3 {
return f(t0)(t1)(t2)
}
@@ -244,7 +244,7 @@ func Nullary4[F1 ~func() T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4,
// Curry4 takes a function with 4 parameters and returns a cascade of functions each taking only one parameter.
// The inverse function is [Uncurry4]
func Curry4[T0, T1, T2, T3, T4 any](f func(T0, T1, T2, T3) T4) func(T0) func(T1) func(T2) func(T3) T4 {
func Curry4[FCT ~func(T0, T1, T2, T3) T4, T0, T1, T2, T3, T4 any](f FCT) func(T0) func(T1) func(T2) func(T3) T4 {
return func(t0 T0) func(t1 T1) func(t2 T2) func(t3 T3) T4 {
return func(t1 T1) func(t2 T2) func(t3 T3) T4 {
return func(t2 T2) func(t3 T3) T4 {
@@ -258,7 +258,7 @@ func Curry4[T0, T1, T2, T3, T4 any](f func(T0, T1, T2, T3) T4) func(T0) func(T1)
// Uncurry4 takes a cascade of 4 functions each taking only one parameter and returns a function with 4 parameters .
// The inverse function is [Curry4]
func Uncurry4[T0, T1, T2, T3, T4 any](f func(T0) func(T1) func(T2) func(T3) T4) func(T0, T1, T2, T3) T4 {
func Uncurry4[FCT ~func(T0) func(T1) func(T2) func(T3) T4, T0, T1, T2, T3, T4 any](f FCT) func(T0, T1, T2, T3) T4 {
return func(t0 T0, t1 T1, t2 T2, t3 T3) T4 {
return f(t0)(t1)(t2)(t3)
}
@@ -313,7 +313,7 @@ func Nullary5[F1 ~func() T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4,
// Curry5 takes a function with 5 parameters and returns a cascade of functions each taking only one parameter.
// The inverse function is [Uncurry5]
func Curry5[T0, T1, T2, T3, T4, T5 any](f func(T0, T1, T2, T3, T4) T5) func(T0) func(T1) func(T2) func(T3) func(T4) T5 {
func Curry5[FCT ~func(T0, T1, T2, T3, T4) T5, T0, T1, T2, T3, T4, T5 any](f FCT) func(T0) func(T1) func(T2) func(T3) func(T4) T5 {
return func(t0 T0) func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) T5 {
return func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) T5 {
return func(t2 T2) func(t3 T3) func(t4 T4) T5 {
@@ -329,7 +329,7 @@ func Curry5[T0, T1, T2, T3, T4, T5 any](f func(T0, T1, T2, T3, T4) T5) func(T0)
// Uncurry5 takes a cascade of 5 functions each taking only one parameter and returns a function with 5 parameters .
// The inverse function is [Curry5]
func Uncurry5[T0, T1, T2, T3, T4, T5 any](f func(T0) func(T1) func(T2) func(T3) func(T4) T5) func(T0, T1, T2, T3, T4) T5 {
func Uncurry5[FCT ~func(T0) func(T1) func(T2) func(T3) func(T4) T5, T0, T1, T2, T3, T4, T5 any](f FCT) func(T0, T1, T2, T3, T4) T5 {
return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4) T5 {
return f(t0)(t1)(t2)(t3)(t4)
}
@@ -385,7 +385,7 @@ func Nullary6[F1 ~func() T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4,
// Curry6 takes a function with 6 parameters and returns a cascade of functions each taking only one parameter.
// The inverse function is [Uncurry6]
func Curry6[T0, T1, T2, T3, T4, T5, T6 any](f func(T0, T1, T2, T3, T4, T5) T6) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) T6 {
func Curry6[FCT ~func(T0, T1, T2, T3, T4, T5) T6, T0, T1, T2, T3, T4, T5, T6 any](f FCT) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) T6 {
return func(t0 T0) func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) T6 {
return func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) T6 {
return func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) T6 {
@@ -403,7 +403,7 @@ func Curry6[T0, T1, T2, T3, T4, T5, T6 any](f func(T0, T1, T2, T3, T4, T5) T6) f
// Uncurry6 takes a cascade of 6 functions each taking only one parameter and returns a function with 6 parameters .
// The inverse function is [Curry6]
func Uncurry6[T0, T1, T2, T3, T4, T5, T6 any](f func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) T6) func(T0, T1, T2, T3, T4, T5) T6 {
func Uncurry6[FCT ~func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) T6, T0, T1, T2, T3, T4, T5, T6 any](f FCT) func(T0, T1, T2, T3, T4, T5) T6 {
return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5) T6 {
return f(t0)(t1)(t2)(t3)(t4)(t5)
}
@@ -460,7 +460,7 @@ func Nullary7[F1 ~func() T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4,
// Curry7 takes a function with 7 parameters and returns a cascade of functions each taking only one parameter.
// The inverse function is [Uncurry7]
func Curry7[T0, T1, T2, T3, T4, T5, T6, T7 any](f func(T0, T1, T2, T3, T4, T5, T6) T7) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) T7 {
func Curry7[FCT ~func(T0, T1, T2, T3, T4, T5, T6) T7, T0, T1, T2, T3, T4, T5, T6, T7 any](f FCT) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) T7 {
return func(t0 T0) func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) T7 {
return func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) T7 {
return func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) T7 {
@@ -480,7 +480,7 @@ func Curry7[T0, T1, T2, T3, T4, T5, T6, T7 any](f func(T0, T1, T2, T3, T4, T5, T
// Uncurry7 takes a cascade of 7 functions each taking only one parameter and returns a function with 7 parameters .
// The inverse function is [Curry7]
func Uncurry7[T0, T1, T2, T3, T4, T5, T6, T7 any](f func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) T7) func(T0, T1, T2, T3, T4, T5, T6) T7 {
func Uncurry7[FCT ~func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) T7, T0, T1, T2, T3, T4, T5, T6, T7 any](f FCT) func(T0, T1, T2, T3, T4, T5, T6) T7 {
return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6) T7 {
return f(t0)(t1)(t2)(t3)(t4)(t5)(t6)
}
@@ -538,7 +538,7 @@ func Nullary8[F1 ~func() T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4,
// Curry8 takes a function with 8 parameters and returns a cascade of functions each taking only one parameter.
// The inverse function is [Uncurry8]
func Curry8[T0, T1, T2, T3, T4, T5, T6, T7, T8 any](f func(T0, T1, T2, T3, T4, T5, T6, T7) T8) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T8 {
func Curry8[FCT ~func(T0, T1, T2, T3, T4, T5, T6, T7) T8, T0, T1, T2, T3, T4, T5, T6, T7, T8 any](f FCT) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T8 {
return func(t0 T0) func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) T8 {
return func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) T8 {
return func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) T8 {
@@ -560,7 +560,7 @@ func Curry8[T0, T1, T2, T3, T4, T5, T6, T7, T8 any](f func(T0, T1, T2, T3, T4, T
// Uncurry8 takes a cascade of 8 functions each taking only one parameter and returns a function with 8 parameters .
// The inverse function is [Curry8]
func Uncurry8[T0, T1, T2, T3, T4, T5, T6, T7, T8 any](f func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T8) func(T0, T1, T2, T3, T4, T5, T6, T7) T8 {
func Uncurry8[FCT ~func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T8, T0, T1, T2, T3, T4, T5, T6, T7, T8 any](f FCT) func(T0, T1, T2, T3, T4, T5, T6, T7) T8 {
return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7) T8 {
return f(t0)(t1)(t2)(t3)(t4)(t5)(t6)(t7)
}
@@ -619,7 +619,7 @@ func Nullary9[F1 ~func() T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4,
// Curry9 takes a function with 9 parameters and returns a cascade of functions each taking only one parameter.
// The inverse function is [Uncurry9]
func Curry9[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9 any](f func(T0, T1, T2, T3, T4, T5, T6, T7, T8) T9) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T9 {
func Curry9[FCT ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8) T9, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9 any](f FCT) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T9 {
return func(t0 T0) func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) T9 {
return func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) T9 {
return func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) T9 {
@@ -643,7 +643,7 @@ func Curry9[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9 any](f func(T0, T1, T2, T3, T
// Uncurry9 takes a cascade of 9 functions each taking only one parameter and returns a function with 9 parameters .
// The inverse function is [Curry9]
func Uncurry9[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9 any](f func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T9) func(T0, T1, T2, T3, T4, T5, T6, T7, T8) T9 {
func Uncurry9[FCT ~func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T9, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9 any](f FCT) func(T0, T1, T2, T3, T4, T5, T6, T7, T8) T9 {
return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8) T9 {
return f(t0)(t1)(t2)(t3)(t4)(t5)(t6)(t7)(t8)
}
@@ -703,7 +703,7 @@ func Nullary10[F1 ~func() T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4,
// Curry10 takes a function with 10 parameters and returns a cascade of functions each taking only one parameter.
// The inverse function is [Uncurry10]
func Curry10[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any](f func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) T10) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T10 {
func Curry10[FCT ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) T10, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any](f FCT) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T10 {
return func(t0 T0) func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) T10 {
return func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) T10 {
return func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) T10 {
@@ -729,7 +729,7 @@ func Curry10[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any](f func(T0, T1, T2,
// Uncurry10 takes a cascade of 10 functions each taking only one parameter and returns a function with 10 parameters .
// The inverse function is [Curry10]
func Uncurry10[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any](f func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T10) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) T10 {
func Uncurry10[FCT ~func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T10, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any](f FCT) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) T10 {
return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9) T10 {
return f(t0)(t1)(t2)(t3)(t4)(t5)(t6)(t7)(t8)(t9)
}
@@ -790,7 +790,7 @@ func Nullary11[F1 ~func() T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4,
// Curry11 takes a function with 11 parameters and returns a cascade of functions each taking only one parameter.
// The inverse function is [Uncurry11]
func Curry11[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11 any](f func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10) T11) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T11 {
func Curry11[FCT ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10) T11, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11 any](f FCT) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T11 {
return func(t0 T0) func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) T11 {
return func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) T11 {
return func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) T11 {
@@ -818,7 +818,7 @@ func Curry11[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11 any](f func(T0, T1
// Uncurry11 takes a cascade of 11 functions each taking only one parameter and returns a function with 11 parameters .
// The inverse function is [Curry11]
func Uncurry11[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11 any](f func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T11) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10) T11 {
func Uncurry11[FCT ~func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T11, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11 any](f FCT) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10) T11 {
return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10) T11 {
return f(t0)(t1)(t2)(t3)(t4)(t5)(t6)(t7)(t8)(t9)(t10)
}
@@ -880,7 +880,7 @@ func Nullary12[F1 ~func() T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4,
// Curry12 takes a function with 12 parameters and returns a cascade of functions each taking only one parameter.
// The inverse function is [Uncurry12]
func Curry12[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12 any](f func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11) T12) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) T12 {
func Curry12[FCT ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11) T12, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12 any](f FCT) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) T12 {
return func(t0 T0) func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) T12 {
return func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) T12 {
return func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) T12 {
@@ -910,7 +910,7 @@ func Curry12[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12 any](f func(T
// Uncurry12 takes a cascade of 12 functions each taking only one parameter and returns a function with 12 parameters .
// The inverse function is [Curry12]
func Uncurry12[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12 any](f func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) T12) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11) T12 {
func Uncurry12[FCT ~func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) T12, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12 any](f FCT) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11) T12 {
return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11) T12 {
return f(t0)(t1)(t2)(t3)(t4)(t5)(t6)(t7)(t8)(t9)(t10)(t11)
}
@@ -973,7 +973,7 @@ func Nullary13[F1 ~func() T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4,
// Curry13 takes a function with 13 parameters and returns a cascade of functions each taking only one parameter.
// The inverse function is [Uncurry13]
func Curry13[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13 any](f func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12) T13) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) T13 {
func Curry13[FCT ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12) T13, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13 any](f FCT) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) T13 {
return func(t0 T0) func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) T13 {
return func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) T13 {
return func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) T13 {
@@ -1005,7 +1005,7 @@ func Curry13[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13 any](f f
// Uncurry13 takes a cascade of 13 functions each taking only one parameter and returns a function with 13 parameters .
// The inverse function is [Curry13]
func Uncurry13[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13 any](f func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) T13) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12) T13 {
func Uncurry13[FCT ~func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) T13, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13 any](f FCT) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12) T13 {
return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12) T13 {
return f(t0)(t1)(t2)(t3)(t4)(t5)(t6)(t7)(t8)(t9)(t10)(t11)(t12)
}
@@ -1069,7 +1069,7 @@ func Nullary14[F1 ~func() T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4,
// Curry14 takes a function with 14 parameters and returns a cascade of functions each taking only one parameter.
// The inverse function is [Uncurry14]
func Curry14[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14 any](f func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13) T14) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) T14 {
func Curry14[FCT ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13) T14, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14 any](f FCT) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) T14 {
return func(t0 T0) func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) T14 {
return func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) T14 {
return func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) T14 {
@@ -1103,7 +1103,7 @@ func Curry14[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14 any
// Uncurry14 takes a cascade of 14 functions each taking only one parameter and returns a function with 14 parameters .
// The inverse function is [Curry14]
func Uncurry14[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14 any](f func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) T14) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13) T14 {
func Uncurry14[FCT ~func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) T14, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14 any](f FCT) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13) T14 {
return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12, t13 T13) T14 {
return f(t0)(t1)(t2)(t3)(t4)(t5)(t6)(t7)(t8)(t9)(t10)(t11)(t12)(t13)
}
@@ -1168,7 +1168,7 @@ func Nullary15[F1 ~func() T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4,
// Curry15 takes a function with 15 parameters and returns a cascade of functions each taking only one parameter.
// The inverse function is [Uncurry15]
func Curry15[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15 any](f func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14) T15) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) T15 {
func Curry15[FCT ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14) T15, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15 any](f FCT) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) T15 {
return func(t0 T0) func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) T15 {
return func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) T15 {
return func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) T15 {
@@ -1204,7 +1204,7 @@ func Curry15[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T1
// Uncurry15 takes a cascade of 15 functions each taking only one parameter and returns a function with 15 parameters .
// The inverse function is [Curry15]
func Uncurry15[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15 any](f func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) T15) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14) T15 {
func Uncurry15[FCT ~func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) T15, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15 any](f FCT) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14) T15 {
return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12, t13 T13, t14 T14) T15 {
return f(t0)(t1)(t2)(t3)(t4)(t5)(t6)(t7)(t8)(t9)(t10)(t11)(t12)(t13)(t14)
}
@@ -1270,7 +1270,7 @@ func Nullary16[F1 ~func() T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4,
// Curry16 takes a function with 16 parameters and returns a cascade of functions each taking only one parameter.
// The inverse function is [Uncurry16]
func Curry16[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16 any](f func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15) T16) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) T16 {
func Curry16[FCT ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15) T16, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16 any](f FCT) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) T16 {
return func(t0 T0) func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) T16 {
return func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) T16 {
return func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) T16 {
@@ -1308,7 +1308,7 @@ func Curry16[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T1
// Uncurry16 takes a cascade of 16 functions each taking only one parameter and returns a function with 16 parameters .
// The inverse function is [Curry16]
func Uncurry16[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16 any](f func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) T16) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15) T16 {
func Uncurry16[FCT ~func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) T16, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16 any](f FCT) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15) T16 {
return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12, t13 T13, t14 T14, t15 T15) T16 {
return f(t0)(t1)(t2)(t3)(t4)(t5)(t6)(t7)(t8)(t9)(t10)(t11)(t12)(t13)(t14)(t15)
}
@@ -1375,7 +1375,7 @@ func Nullary17[F1 ~func() T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4,
// Curry17 takes a function with 17 parameters and returns a cascade of functions each taking only one parameter.
// The inverse function is [Uncurry17]
func Curry17[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17 any](f func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16) T17) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) func(T16) T17 {
func Curry17[FCT ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16) T17, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17 any](f FCT) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) func(T16) T17 {
return func(t0 T0) func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) T17 {
return func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) T17 {
return func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) T17 {
@@ -1415,7 +1415,7 @@ func Curry17[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T1
// Uncurry17 takes a cascade of 17 functions each taking only one parameter and returns a function with 17 parameters .
// The inverse function is [Curry17]
func Uncurry17[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17 any](f func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) func(T16) T17) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16) T17 {
func Uncurry17[FCT ~func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) func(T16) T17, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17 any](f FCT) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16) T17 {
return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12, t13 T13, t14 T14, t15 T15, t16 T16) T17 {
return f(t0)(t1)(t2)(t3)(t4)(t5)(t6)(t7)(t8)(t9)(t10)(t11)(t12)(t13)(t14)(t15)(t16)
}
@@ -1483,7 +1483,7 @@ func Nullary18[F1 ~func() T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4,
// Curry18 takes a function with 18 parameters and returns a cascade of functions each taking only one parameter.
// The inverse function is [Uncurry18]
func Curry18[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18 any](f func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17) T18) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) func(T16) func(T17) T18 {
func Curry18[FCT ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17) T18, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18 any](f FCT) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) func(T16) func(T17) T18 {
return func(t0 T0) func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) func(t17 T17) T18 {
return func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) func(t17 T17) T18 {
return func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) func(t17 T17) T18 {
@@ -1525,7 +1525,7 @@ func Curry18[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T1
// Uncurry18 takes a cascade of 18 functions each taking only one parameter and returns a function with 18 parameters .
// The inverse function is [Curry18]
func Uncurry18[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18 any](f func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) func(T16) func(T17) T18) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17) T18 {
func Uncurry18[FCT ~func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) func(T16) func(T17) T18, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18 any](f FCT) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17) T18 {
return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12, t13 T13, t14 T14, t15 T15, t16 T16, t17 T17) T18 {
return f(t0)(t1)(t2)(t3)(t4)(t5)(t6)(t7)(t8)(t9)(t10)(t11)(t12)(t13)(t14)(t15)(t16)(t17)
}
@@ -1594,7 +1594,7 @@ func Nullary19[F1 ~func() T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4,
// Curry19 takes a function with 19 parameters and returns a cascade of functions each taking only one parameter.
// The inverse function is [Uncurry19]
func Curry19[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19 any](f func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18) T19) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) func(T16) func(T17) func(T18) T19 {
func Curry19[FCT ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18) T19, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19 any](f FCT) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) func(T16) func(T17) func(T18) T19 {
return func(t0 T0) func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) func(t17 T17) func(t18 T18) T19 {
return func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) func(t17 T17) func(t18 T18) T19 {
return func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) func(t17 T17) func(t18 T18) T19 {
@@ -1638,7 +1638,7 @@ func Curry19[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T1
// Uncurry19 takes a cascade of 19 functions each taking only one parameter and returns a function with 19 parameters .
// The inverse function is [Curry19]
func Uncurry19[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19 any](f func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) func(T16) func(T17) func(T18) T19) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18) T19 {
func Uncurry19[FCT ~func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) func(T16) func(T17) func(T18) T19, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19 any](f FCT) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18) T19 {
return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12, t13 T13, t14 T14, t15 T15, t16 T16, t17 T17, t18 T18) T19 {
return f(t0)(t1)(t2)(t3)(t4)(t5)(t6)(t7)(t8)(t9)(t10)(t11)(t12)(t13)(t14)(t15)(t16)(t17)(t18)
}
@@ -1708,7 +1708,7 @@ func Nullary20[F1 ~func() T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4,
// Curry20 takes a function with 20 parameters and returns a cascade of functions each taking only one parameter.
// The inverse function is [Uncurry20]
func Curry20[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20 any](f func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19) T20) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) func(T16) func(T17) func(T18) func(T19) T20 {
func Curry20[FCT ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19) T20, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20 any](f FCT) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) func(T16) func(T17) func(T18) func(T19) T20 {
return func(t0 T0) func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) func(t17 T17) func(t18 T18) func(t19 T19) T20 {
return func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) func(t17 T17) func(t18 T18) func(t19 T19) T20 {
return func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) func(t17 T17) func(t18 T18) func(t19 T19) T20 {
@@ -1754,7 +1754,7 @@ func Curry20[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T1
// Uncurry20 takes a cascade of 20 functions each taking only one parameter and returns a function with 20 parameters .
// The inverse function is [Curry20]
func Uncurry20[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20 any](f func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) func(T16) func(T17) func(T18) func(T19) T20) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19) T20 {
func Uncurry20[FCT ~func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) func(T16) func(T17) func(T18) func(T19) T20, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20 any](f FCT) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19) T20 {
return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12, t13 T13, t14 T14, t15 T15, t16 T16, t17 T17, t18 T18, t19 T19) T20 {
return f(t0)(t1)(t2)(t3)(t4)(t5)(t6)(t7)(t8)(t9)(t10)(t11)(t12)(t13)(t14)(t15)(t16)(t17)(t18)(t19)
}

View File

@@ -28,7 +28,7 @@ func Memoize[F ~func(K) T, K comparable, T any](f F) F {
// ContramapMemoize converts a unary function into a unary function that caches the value depending on the parameter
func ContramapMemoize[F ~func(A) T, KF func(A) K, A any, K comparable, T any](kf KF) func(F) F {
return CacheCallback[F](kf, getOrCreate[K, T]())
return CacheCallback[func(F) F, func() func() T](kf, getOrCreate[K, T]())
}
// getOrCreate is a naive implementation of a cache, without bounds
@@ -50,13 +50,51 @@ func getOrCreate[K comparable, T any]() func(K, func() func() T) func() T {
}
}
// SingleElementCache is a cache with a capacity of a single element
func SingleElementCache[
LLT ~func() LT, // generator of the generator
K comparable, // key into the cache
LT ~func() T, // generator of a value
T any, // the cached data type
]() func(K, LLT) LT {
var l sync.Mutex
var key K
var value LT
hasKey := false
return func(k K, gen LLT) LT {
l.Lock()
existing := value
if !hasKey || key != k {
existing = gen()
// update state
key = k
value = existing
hasKey = true
}
l.Unlock()
return existing
}
}
// CacheCallback converts a unary function into a unary function that caches the value depending on the parameter
func CacheCallback[F ~func(A) T, KF func(A) K, C ~func(K, func() func() T) func() T, A any, K comparable, T any](kf KF, getOrCreate C) func(F) F {
func CacheCallback[
EM ~func(F) F, // endomorphism of the function
LLT ~func() LT, // generator of the generator
LT ~func() T, // generator of a value
F ~func(A) T, // function to actually cache
KF func(A) K, // extracts the cache key from the input
C ~func(K, LLT) LT, // the cache callback function
A any, K comparable, T any](kf KF, getOrCreate C) EM {
return func(f F) F {
return func(a A) T {
// cache entry
return getOrCreate(kf(a), func() func() T {
return L.Memoize[func() T](func() T {
return getOrCreate(kf(a), func() LT {
return L.Memoize[LT](func() T {
return f(a)
})
})()

View File

@@ -0,0 +1,28 @@
// 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
// Switch applies a handler to different cases. The handers are stored in a map. A key function
// extracts the case from a value.
func Switch[HF ~func(T) R, N ~map[K]HF, KF ~func(T) K, K comparable, T, R any](kf KF, n N, d HF) HF {
return func(t T) R {
f, ok := n[kf(t)]
if ok {
return f(t)
}
return d(t)
}
}

26
function/switch.go Normal file
View 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 function
import (
G "github.com/IBM/fp-go/function/generic"
)
// Switch applies a handler to different cases. The handers are stored in a map. A key function
// extracts the case from a value.
func Switch[K comparable, T, R any](kf func(T) K, n map[K]func(T) R, d func(T) R) func(T) R {
return G.Switch(kf, n, d)
}

6
go.mod
View File

@@ -4,14 +4,14 @@ go 1.20
require (
github.com/stretchr/testify v1.8.4
github.com/urfave/cli/v2 v2.25.7
github.com/urfave/cli/v2 v2.26.0
)
require (
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
github.com/xrash/smetrics v0.0.0-20231213231151-1d8dd44e695e // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

8
go.sum
View File

@@ -1,5 +1,5 @@
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM=
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@@ -10,8 +10,12 @@ github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcU
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs=
github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ=
github.com/urfave/cli/v2 v2.26.0 h1:3f3AMg3HpThFNT4I++TKOejZO8yU55t3JnnSr4S4QEI=
github.com/urfave/cli/v2 v2.26.0/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
github.com/xrash/smetrics v0.0.0-20231213231151-1d8dd44e695e h1:+SOyEddqYF09QP7vr7CgJ1eti3pY9Fn3LHO1M1r/0sI=
github.com/xrash/smetrics v0.0.0-20231213231151-1d8dd44e695e/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

Some files were not shown because too many files have changed in this diff Show More