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

Compare commits

...

30 Commits

Author SHA1 Message Date
Dr. Carsten Leue
01f14a327a fix: http interface of IOEither
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-11-27 12:11:23 +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
156 changed files with 4289 additions and 991 deletions

View File

@@ -29,7 +29,7 @@ jobs:
go-version: [ '1.20.x', '1.21.x' ]
steps:
# full checkout for semantic-release
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
fetch-depth: 0
- name: Set up go ${{ matrix.go-version }}
@@ -55,12 +55,12 @@ jobs:
steps:
# full checkout for semantic-release
- name: Full checkout
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
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@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4.0.0
with:
node-version: ${{ env.NODE_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

@@ -52,6 +52,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 +64,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 +88,38 @@ func filterMapRef[A, B any](fa []A, pred func(a *A) bool, f func(a *A) B) []B {
return result
}
// Filter returns a new array with all elements from the original array that match a predicate
func Filter[A any](pred func(A) bool) func([]A) []A {
return F.Bind2nd(filter[A], pred)
return G.Filter[[]A](pred)
}
// 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) func([]A) []A {
return G.FilterWithIndex[[]A](pred)
}
func FilterRef[A any](pred func(*A) bool) func([]A) []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 +141,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 {
@@ -300,6 +317,11 @@ 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)
@@ -309,10 +331,10 @@ func Push[A any](a A) func([]A) []A {
return G.Push[[]A](a)
}
func MonadFlap[A, B any](fab []func(A) B, a A) []B {
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[A, B any](a A) func([]func(A) B) []B {
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)
}

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

@@ -29,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
@@ -118,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),
@@ -143,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]()
@@ -242,6 +314,16 @@ 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())

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

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

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

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

@@ -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,89 +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"
L "github.com/IBM/fp-go/lazy"
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 MonadChainIOK[A, B any](fa ReaderIO[A], f func(A) IO.IO[B]) ReaderIO[B] {
return R.MonadChainIOK[ReaderIO[A], ReaderIO[B]](fa, f)
}
func ChainIOK[A, B any](f func(A) IO.IO[B]) func(ReaderIO[A]) ReaderIO[B] {
return R.ChainIOK[ReaderIO[A], ReaderIO[B]](f)
}
func MonadChainFirstIOK[A, B any](fa ReaderIO[A], f func(A) IO.IO[B]) ReaderIO[A] {
return R.MonadChainFirstIOK[ReaderIO[A], ReaderIO[B]](fa, f)
}
func ChainFirstIOK[A, B any](f func(A) IO.IO[B]) func(ReaderIO[A]) ReaderIO[A] {
return R.ChainFirstIOK[ReaderIO[A], ReaderIO[B]](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 L.Lazy[ReaderIO[A]]) ReaderIO[A] {
return R.Defer[ReaderIO[A]](gen)
}
// Memoize computes the value of the provided [ReaderIO] 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 ReaderIO[A]) ReaderIO[A] {
return R.Memoize[ReaderIO[A]](rdr)
}
// Flatten converts a nested [ReaderIO] into a [ReaderIO]
func Flatten[A any](mma ReaderIO[ReaderIO[A]]) ReaderIO[A] {
return R.Flatten[ReaderIO[A]](mma)
}

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

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

@@ -19,7 +19,6 @@ import (
"os"
RIOE "github.com/IBM/fp-go/context/readerioeither"
FL "github.com/IBM/fp-go/file"
F "github.com/IBM/fp-go/function"
IO "github.com/IBM/fp-go/io"
IOF "github.com/IBM/fp-go/io/file"
@@ -32,7 +31,7 @@ var (
// destroy handler
onReleaseTempFile = F.Flow4(
IOF.Close[*os.File],
IO.Map(FL.GetName),
IO.Map((*os.File).Name),
RIOE.FromIO[string],
RIOE.Chain(Remove),
)

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

@@ -432,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)
}
@@ -571,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),
)
}
@@ -664,3 +664,11 @@ func LeftReaderIO[
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

@@ -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)
}
@@ -103,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)
}
@@ -164,14 +134,6 @@ func Never[A any]() ReaderIOEither[A] {
return G.Never[ReaderIOEither[A]]()
}
func MonadChainReaderIOK[A, B any](ma ReaderIOEither[A], f func(A) RIO.ReaderIO[B]) ReaderIOEither[B] {
return G.MonadChainReaderIOK[ReaderIOEither[B], ReaderIOEither[A]](ma, f)
}
func ChainReaderIOK[A, B any](f func(A) RIO.ReaderIO[B]) func(ma ReaderIOEither[A]) ReaderIOEither[B] {
return G.ChainReaderIOK[ReaderIOEither[B], ReaderIOEither[A]](f)
}
func MonadChainIOK[A, B any](ma ReaderIOEither[A], f func(A) IO.IO[B]) ReaderIOEither[B] {
return G.MonadChainIOK[ReaderIOEither[B], ReaderIOEither[A]](ma, f)
}
@@ -235,18 +197,10 @@ func Flatten[
return G.Flatten[ReaderIOEither[ReaderIOEither[A]]](rdr)
}
func MonadFromReaderIO[A any](a A, f func(A) RIO.ReaderIO[A]) ReaderIOEither[A] {
return G.MonadFromReaderIO[ReaderIOEither[A]](a, f)
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 FromReaderIO[A any](f func(A) RIO.ReaderIO[A]) func(A) ReaderIOEither[A] {
return G.FromReaderIO[ReaderIOEither[A]](f)
}
func RightReaderIO[A any](ma RIO.ReaderIO[A]) ReaderIOEither[A] {
return G.RightReaderIO[ReaderIOEither[A]](ma)
}
func LeftReaderIO[A any](ma RIO.ReaderIO[error]) ReaderIOEither[A] {
return G.LeftReaderIO[ReaderIOEither[A]](ma)
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

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

168
di/erasure/injector.go Normal file
View File

@@ -0,0 +1,168 @@
// 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
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
}

174
di/erasure/provider.go Normal file
View File

@@ -0,0 +1,174 @@
// 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 = func(Dependency) IOE.IOEither[error, any]
type ProviderFactory = func(InjectableFactory) IOE.IOEither[error, any]
type paramIndex = map[int]int
type paramValue = map[int]any
type handler = func(paramIndex) func([]IOE.IOEither[error, any]) IOE.IOEither[error, paramValue]
type 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
}
type 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 (
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])
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),
IOO.FromIOEither[error, any],
F.ToAny[IOO.IOOption[any]],
)),
IOE.Of[error, paramValue],
)
}
},
}
)
type Mapping = map[int]paramIndex
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(
F.Curry2(A.MonadMap[Dependency, IOE.IOEither[error, any]])(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]
}

33
di/injector.go Normal file
View File

@@ -0,0 +1,33 @@
// 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),
)
}

166
di/provider.go Normal file
View File

@@ -0,0 +1,166 @@
// 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"
T "github.com/IBM/fp-go/tuple"
)
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 eraseProviderFactory0[R any](f func() 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 eraseProviderFactory1[T1 any, R any](
d1 Dependency[T1],
f func(T1) IOE.IOEither[error, R]) func(params ...any) IOE.IOEither[error, any] {
ft := T.Tupled1(f)
t1 := lookupAt[T1](0, d1)
return func(params ...any) IOE.IOEither[error, any] {
return F.Pipe3(
E.SequenceT1(t1(params)),
IOE.FromEither[error, T.Tuple1[T1]],
IOE.Chain(ft),
IOE.Map[error](F.ToAny[R]),
)
}
}
func eraseProviderFactory2[T1, T2 any, R any](
d1 Dependency[T1],
d2 Dependency[T2],
f func(T1, T2) IOE.IOEither[error, R]) func(params ...any) IOE.IOEither[error, any] {
ft := T.Tupled2(f)
t1 := lookupAt[T1](0, d1)
t2 := lookupAt[T2](1, d2)
return func(params ...any) IOE.IOEither[error, any] {
return F.Pipe3(
E.SequenceT2(t1(params), t2(params)),
IOE.FromEither[error, T.Tuple2[T1, T2]],
IOE.Chain(ft),
IOE.Map[error](F.ToAny[R]),
)
}
}
func MakeProviderFactory0[R any](
fct func() IOE.IOEither[error, R],
) DIE.ProviderFactory {
return DIE.MakeProviderFactory(
A.Empty[DIE.Dependency](),
eraseProviderFactory0(fct),
)
}
// MakeTokenWithDefault0 create a unique `InjectionToken` for a specific type with an attached default provider
func MakeTokenWithDefault0[R any](name string, fct func() IOE.IOEither[error, R]) InjectionToken[R] {
return MakeTokenWithDefault[R](name, MakeProviderFactory0(fct))
}
func MakeProvider0[R any](
token InjectionToken[R],
fct func() IOE.IOEither[error, R],
) DIE.Provider {
return DIE.MakeProvider(
token,
MakeProviderFactory0(fct),
)
}
func MakeProviderFactory1[T1, R any](
d1 Dependency[T1],
fct func(T1) IOE.IOEither[error, R],
) DIE.ProviderFactory {
return DIE.MakeProviderFactory(
A.From[DIE.Dependency](d1),
eraseProviderFactory1(d1, fct),
)
}
// MakeTokenWithDefault1 create a unique `InjectionToken` for a specific type with an attached default provider
func MakeTokenWithDefault1[T1, R any](name string,
d1 Dependency[T1],
fct func(T1) IOE.IOEither[error, R]) InjectionToken[R] {
return MakeTokenWithDefault[R](name, MakeProviderFactory1(d1, fct))
}
func MakeProvider1[T1, R any](
token InjectionToken[R],
d1 Dependency[T1],
fct func(T1) IOE.IOEither[error, R],
) DIE.Provider {
return DIE.MakeProvider(
token,
MakeProviderFactory1(d1, fct),
)
}
func MakeProviderFactory2[T1, T2, R any](
d1 Dependency[T1],
d2 Dependency[T2],
fct func(T1, T2) IOE.IOEither[error, R],
) DIE.ProviderFactory {
return DIE.MakeProviderFactory(
A.From[DIE.Dependency](d1, d2),
eraseProviderFactory2(d1, d2, fct),
)
}
// MakeTokenWithDefault2 create a unique `InjectionToken` for a specific type with an attached default provider
func MakeTokenWithDefault2[T1, T2, R any](name string,
d1 Dependency[T1],
d2 Dependency[T2],
fct func(T1, T2) IOE.IOEither[error, R]) InjectionToken[R] {
return MakeTokenWithDefault[R](name, MakeProviderFactory2(d1, d2, fct))
}
func MakeProvider2[T1, T2, R any](
token InjectionToken[R],
d1 Dependency[T1],
d2 Dependency[T2],
fct func(T1, T2) IOE.IOEither[error, R],
) DIE.Provider {
return DIE.MakeProvider(
token,
MakeProviderFactory2(d1, d2, 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, F.Constant(IOE.Of[error](value)))
}

351
di/provider_test.go Normal file
View File

@@ -0,0 +1,351 @@
// 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) func() IOE.IOEither[error, string] {
return func() 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) func() IOE.IOEither[error, string] {
return func() 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) func() IOE.IOEither[error, string] {
return func() 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", F.Constant(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", F.Constant(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)())
}

196
di/token.go Normal file
View File

@@ -0,0 +1,196 @@
// 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.
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}
}

76
di/utils.go Normal file
View File

@@ -0,0 +1,76 @@
// 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)),
)
}

83
di/utils_test.go Normal file
View File

@@ -0,0 +1,83 @@
// 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

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

@@ -120,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] {
@@ -154,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))
}
}
@@ -248,12 +245,12 @@ func Swap[E, A any](val Either[E, A]) Either[A, E] {
return MonadFold(val, Right[A, E], Left[E, A])
}
func MonadFlap[E, A, B any](fab Either[E, func(A) B], a A) Either[E, B] {
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, A, B any](a A) func(Either[E, func(A) B]) Either[E, B] {
return F.Bind2nd(MonadFlap[E, A, B], 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] {

View File

@@ -32,7 +32,5 @@ var (
)
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

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

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

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

@@ -15,9 +15,11 @@
package file
import "os"
import "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)
}
}

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-10-23 08:30:44.6474482 +0200 CEST m=+0.150851901
package function

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-10-23 08:30:41.7972101 +0200 CEST m=+0.008029101
package function

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

2
go.mod
View File

@@ -8,7 +8,7 @@ require (
)
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

4
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=

View File

@@ -68,10 +68,8 @@ const (
// ParseMediaType parses a media type into a tuple
func ParseMediaType(mediaType string) E.Either[error, ParsedMediaType] {
return E.TryCatchError(func() (ParsedMediaType, error) {
m, p, err := mime.ParseMediaType(mediaType)
return T.MakeTuple2(m, p), err
})
m, p, err := mime.ParseMediaType(mediaType)
return E.TryCatchError(T.MakeTuple2(m, p), err)
}
func GetHeader(resp *H.Response) H.Header {

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:24.9409324 +0200 CEST m=+0.008573601
// 2023-10-23 08:30:50.5492271 +0200 CEST m=+0.023274501
package identity

View File

@@ -57,6 +57,6 @@ func MonadFlap[GAB ~func(A) B, A, B any](fab GAB, a A) B {
return FC.MonadFlap(MonadMap[func(GAB) B, GAB, B], fab, a)
}
func Flap[GAB ~func(A) B, A, B any](a A) func(GAB) B {
func Flap[GAB ~func(A) B, B, A any](a A) func(GAB) B {
return F.Bind2nd(MonadFlap[GAB, A, B], a)
}

View File

@@ -64,10 +64,10 @@ func ChainFirst[A, B any](f func(A) B) func(A) A {
return G.ChainFirst(f)
}
func MonadFlap[A, B any](fab func(A) B, a A) B {
func MonadFlap[B, A any](fab func(A) B, a A) B {
return G.MonadFlap[func(A) B](fab, a)
}
func Flap[A, B any](a A) func(func(A) B) B {
func Flap[B, A any](a A) func(func(A) B) B {
return G.Flap[func(A) B](a)
}

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:26.2499883 +0200 CEST m=+0.007601301
// 2023-10-23 08:30:54.1909432 +0200 CEST m=+0.014252201
package apply

View File

@@ -51,6 +51,24 @@ func ReduceWithIndex[GA ~[]A, A, B any](fa GA, f func(int, B, A) B, initial B) B
return current
}
func ReduceRight[GA ~[]A, A, B any](fa GA, f func(A, B) B, initial B) B {
current := initial
count := len(fa)
for i := count - 1; i >= 0; i-- {
current = f(fa[i], current)
}
return current
}
func ReduceRightWithIndex[GA ~[]A, A, B any](fa GA, f func(int, A, B) B, initial B) B {
current := initial
count := len(fa)
for i := count - 1; i >= 0; i-- {
current = f(i, fa[i], current)
}
return current
}
func Append[GA ~[]A, A any](as GA, a A) GA {
return append(as, a)
}
@@ -88,6 +106,15 @@ func MonadMap[GA ~[]A, GB ~[]B, A, B any](as GA, f func(a A) B) GB {
return bs
}
func MonadMapWithIndex[GA ~[]A, GB ~[]B, A, B any](as GA, f func(idx int, a A) B) GB {
count := len(as)
bs := make(GB, count)
for i := count - 1; i >= 0; i-- {
bs[i] = f(i, as[i])
}
return bs
}
func ConstNil[GA ~[]A, A any]() GA {
return (GA)(nil)
}

View File

@@ -46,9 +46,7 @@ func MakeReader(ctx context.Context, rdr io.Reader) io.Reader {
// ReadAll reads the content of a reader and allows it to be canceled
func ReadAll(ctx context.Context, rdr io.Reader) E.Either[error, []byte] {
return E.TryCatchError(func() ([]byte, error) {
var buffer bytes.Buffer
_, err := io.Copy(&buffer, MakeReader(ctx, rdr))
return buffer.Bytes(), err
})
var buffer bytes.Buffer
_, err := io.Copy(&buffer, MakeReader(ctx, rdr))
return E.TryCatchError(buffer.Bytes(), err)
}

30
io/bracket.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 io
import (
G "github.com/IBM/fp-go/io/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[A, B, ANY any](
acquire IO[A],
use func(A) IO[B],
release func(A, B) IO[ANY],
) IO[B] {
return G.Bracket(acquire, use, release)
}

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:27.9813739 +0200 CEST m=+0.011088001
// 2023-10-23 08:30:56.7105551 +0200 CEST m=+0.011255201
package io

44
io/generic/bracket.go Normal file
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 (
G "github.com/IBM/fp-go/internal/bracket"
)
// 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() A,
GB ~func() B,
GANY ~func() ANY,
A, B, ANY any](
acquire GA,
use func(A) GB,
release func(A, B) GANY,
) GB {
return G.Bracket[GA, GB, GANY, B, A, B](
Of[GB, B],
MonadChain[GA, GB, A, B],
MonadChain[GB, GB, B, B],
MonadChain[GANY, GB, ANY, B],
acquire,
use,
release,
)
}

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:27.9813739 +0200 CEST m=+0.011088001
// 2023-10-23 08:30:56.7105551 +0200 CEST m=+0.011255201
package generic
import (

30
io/generic/sync.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 generic
import (
"context"
)
// WithLock executes the provided IO operation in the scope of a lock
func WithLock[GA ~func() A, A any](lock func() context.CancelFunc) func(fa GA) GA {
return func(fa GA) GA {
return func() A {
defer lock()()
return fa()
}
}
}

View File

@@ -57,7 +57,7 @@ func SequenceArray[GA ~func() A, GAS ~func() AAS, AAS ~[]A, GAAS ~[]GA, A any](t
}
// MonadTraverseRecord transforms a record using an IO transform an IO of a record
func MonadTraverseRecord[GB ~func() B, GBS ~func() MB, MA ~map[K]A, MB ~map[K]B, K comparable, A, B any](ma MA, f func(A) GB) GBS {
func MonadTraverseRecord[GBS ~func() MB, MA ~map[K]A, GB ~func() B, MB ~map[K]B, K comparable, A, B any](ma MA, f func(A) GB) GBS {
return RR.MonadTraverse[MA](
Of[GBS, MB],
Map[GBS, func() func(B) MB, MB, func(B) MB],
@@ -67,7 +67,7 @@ func MonadTraverseRecord[GB ~func() B, GBS ~func() MB, MA ~map[K]A, MB ~map[K]B,
}
// TraverseRecord transforms a record using an IO transform an IO of a record
func TraverseRecord[GB ~func() B, GBS ~func() MB, MA ~map[K]A, MB ~map[K]B, K comparable, A, B any](f func(A) GB) func(MA) GBS {
func TraverseRecord[GBS ~func() MB, MA ~map[K]A, GB ~func() B, MB ~map[K]B, K comparable, A, B any](f func(A) GB) func(MA) GBS {
return RR.Traverse[MA](
Of[GBS, MB],
Map[GBS, func() func(B) MB, MB, func(B) MB],
@@ -87,5 +87,5 @@ func TraverseRecordWithIndex[GB ~func() B, GBS ~func() MB, MA ~map[K]A, MB ~map[
}
func SequenceRecord[GA ~func() A, GAS ~func() AAS, AAS ~map[K]A, GAAS ~map[K]GA, K comparable, A any](tas GAAS) GAS {
return MonadTraverseRecord[GA, GAS](tas, F.Identity[GA])
return MonadTraverseRecord[GAS](tas, F.Identity[GA])
}

View File

@@ -139,7 +139,7 @@ func Defer[A any](gen func() IO[A]) IO[A] {
return G.Defer[IO[A]](gen)
}
func MonadFlap[A, B any](fab IO[func(A) B], a A) IO[B] {
func MonadFlap[B, A any](fab IO[func(A) B], a A) IO[B] {
return G.MonadFlap[func(A) B, IO[func(A) B], IO[B], A, B](fab, a)
}

27
io/sync.go Normal file
View File

@@ -0,0 +1,27 @@
// 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 io
import (
"context"
G "github.com/IBM/fp-go/io/generic"
)
// WithLock executes the provided [IO] operation in the scope of a lock
func WithLock[A any](lock IO[context.CancelFunc]) func(fa IO[A]) IO[A] {
return G.WithLock[IO[A]](lock)
}

View File

@@ -41,13 +41,13 @@ func SequenceArray[A any](tas []IO[A]) IO[[]A] {
}
func MonadTraverseRecord[K comparable, A, B any](tas map[K]A, f func(A) IO[B]) IO[map[K]B] {
return G.MonadTraverseRecord[IO[B], IO[map[K]B]](tas, f)
return G.MonadTraverseRecord[IO[map[K]B]](tas, f)
}
// TraverseRecord applies a function returning an [IO] to all elements in a record and the
// transforms this into an [IO] of that record
func TraverseRecord[K comparable, A, B any](f func(A) IO[B]) func(map[K]A) IO[map[K]B] {
return G.TraverseRecord[IO[B], IO[map[K]B], map[K]A](f)
return G.TraverseRecord[IO[map[K]B], map[K]A, IO[B]](f)
}
// TraverseRecordWithIndex applies a function returning an [IO] to all elements in a record and the

View File

@@ -18,7 +18,6 @@ package file
import (
"os"
FL "github.com/IBM/fp-go/file"
F "github.com/IBM/fp-go/function"
IO "github.com/IBM/fp-go/io"
IOF "github.com/IBM/fp-go/io/file"
@@ -33,7 +32,7 @@ var (
// destroy handler
onReleaseTempFile = F.Flow4(
IOF.Close[*os.File],
IO.Map(FL.GetName),
IO.Map((*os.File).Name),
IOE.FromIO[error, string],
IOE.Chain(Remove),
)

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:29.4935658 +0200 CEST m=+0.015377401
// 2023-10-23 08:30:58.6457744 +0200 CEST m=+0.080336501
package ioeither

View File

@@ -17,7 +17,7 @@ package generic
import (
ET "github.com/IBM/fp-go/either"
G "github.com/IBM/fp-go/internal/file"
G "github.com/IBM/fp-go/internal/bracket"
I "github.com/IBM/fp-go/io/generic"
)

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:29.4935658 +0200 CEST m=+0.015377401
// 2023-10-23 08:30:58.6556525 +0200 CEST m=+0.090214601
package generic
import (

View File

@@ -19,12 +19,12 @@ import (
"time"
ET "github.com/IBM/fp-go/either"
"github.com/IBM/fp-go/errors"
F "github.com/IBM/fp-go/function"
C "github.com/IBM/fp-go/internal/chain"
"github.com/IBM/fp-go/internal/eithert"
FE "github.com/IBM/fp-go/internal/fromeither"
FI "github.com/IBM/fp-go/internal/fromio"
FC "github.com/IBM/fp-go/internal/functor"
IO "github.com/IBM/fp-go/io/generic"
O "github.com/IBM/fp-go/option"
)
@@ -183,12 +183,15 @@ func Flatten[GA ~func() ET.Either[E, A], GAA ~func() ET.Either[E, GA], E, A any]
func TryCatch[GA ~func() ET.Either[E, A], E, A any](f func() (A, error), onThrow func(error) E) GA {
return MakeIO(func() ET.Either[E, A] {
return ET.TryCatch(f, onThrow)
a, err := f()
return ET.TryCatch(a, err, onThrow)
})
}
func TryCatchError[GA ~func() ET.Either[error, A], A any](f func() (A, error)) GA {
return TryCatch[GA](f, errors.IdentityError)
return MakeIO(func() ET.Either[error, A] {
return ET.TryCatchError(f())
})
}
// Memoize computes the value of the provided IO monad lazily but exactly once
@@ -272,6 +275,27 @@ func ChainFirstIOK[GA ~func() ET.Either[E, A], GIOB ~func() B, E, A, B any](f fu
)
}
// MonadChainFirstEitherK runs the monad returned by the function but returns the result of the original monad
func MonadChainFirstEitherK[GA ~func() ET.Either[E, A], E, A, B any](first GA, f func(A) ET.Either[E, B]) GA {
return FE.MonadChainFirstEitherK(
MonadChain[GA, GA, E, A, A],
MonadMap[func() ET.Either[E, B], GA, E, B, A],
FromEither[func() ET.Either[E, B], E, B],
first,
f,
)
}
// ChainFirstEitherK runs the monad returned by the function but returns the result of the original monad
func ChainFirstEitherK[GA ~func() ET.Either[E, A], E, A, B any](f func(A) ET.Either[E, B]) func(GA) GA {
return FE.ChainFirstEitherK(
MonadChain[GA, GA, E, A, A],
MonadMap[func() ET.Either[E, B], GA, E, B, A],
FromEither[func() ET.Either[E, B], E, B],
f,
)
}
// Swap changes the order of type parameters
func Swap[GEA ~func() ET.Either[E, A], GAE ~func() ET.Either[A, E], E, A any](val GEA) GAE {
return MonadFold(val, Right[GAE], Left[GAE])
@@ -304,3 +328,11 @@ func MonadAlt[LAZY ~func() GIOA, GIOA ~func() ET.Either[E, A], E, A any](first G
func Alt[LAZY ~func() GIOA, GIOA ~func() ET.Either[E, A], E, A any](second LAZY) func(GIOA) GIOA {
return F.Bind2nd(MonadAlt[LAZY], second)
}
func MonadFlap[GEAB ~func() ET.Either[E, func(A) B], GEB ~func() ET.Either[E, B], E, B, A any](fab GEAB, a A) GEB {
return FC.MonadFlap(MonadMap[GEAB, GEB], fab, a)
}
func Flap[GEAB ~func() ET.Either[E, func(A) B], GEB ~func() ET.Either[E, B], E, B, A any](a A) func(GEAB) GEB {
return FC.Flap(MonadMap[GEAB, GEB], a)
}

28
ioeither/generic/sync.go Normal file
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
import (
"context"
ET "github.com/IBM/fp-go/either"
G "github.com/IBM/fp-go/io/generic"
)
// WithLock executes the provided IO operation in the scope of a lock
func WithLock[GA ~func() ET.Either[E, A], E, A any](lock func() context.CancelFunc) func(fa GA) GA {
return G.WithLock[GA](lock)
}

View File

@@ -20,54 +20,94 @@ import (
"net/http"
B "github.com/IBM/fp-go/bytes"
ER "github.com/IBM/fp-go/errors"
F "github.com/IBM/fp-go/function"
H "github.com/IBM/fp-go/http"
IOE "github.com/IBM/fp-go/ioeither"
IOEF "github.com/IBM/fp-go/ioeither/file"
J "github.com/IBM/fp-go/json"
T "github.com/IBM/fp-go/tuple"
)
type Client interface {
Do(req *http.Request) IOE.IOEither[error, *http.Response]
}
type (
// Requester is a reader that constructs a request
Requester = IOE.IOEither[error, *http.Request]
type client struct {
delegate *http.Client
}
Client interface {
Do(Requester) IOE.IOEither[error, *http.Response]
}
func (client client) Do(req *http.Request) IOE.IOEither[error, *http.Response] {
return IOE.TryCatch(func() (*http.Response, error) {
return client.delegate.Do(req)
}, ER.IdentityError)
client struct {
delegate *http.Client
doIOE func(*http.Request) IOE.IOEither[error, *http.Response]
}
)
var (
// MakeRequest is an eitherized version of [http.NewRequest]
MakeRequest = IOE.Eitherize3(http.NewRequest)
makeRequest = F.Bind13of3(MakeRequest)
// specialize
MakeGetRequest = makeRequest("GET", nil)
)
func (client client) Do(req Requester) IOE.IOEither[error, *http.Response] {
return F.Pipe1(
req,
IOE.Chain(client.doIOE),
)
}
func MakeClient(httpClient *http.Client) Client {
return client{delegate: httpClient}
return client{delegate: httpClient, doIOE: IOE.Eitherize1(httpClient.Do)}
}
func ReadAll(client Client) func(*http.Request) IOE.IOEither[error, []byte] {
return func(req *http.Request) IOE.IOEither[error, []byte] {
return IOEF.ReadAll(F.Pipe2(
req,
client.Do,
IOE.Map[error](func(resp *http.Response) io.ReadCloser {
return resp.Body
}),
),
)
}
// ReadFullResponse sends a request, reads the response as a byte array and represents the result as a tuple
func ReadFullResponse(client Client) func(Requester) IOE.IOEither[error, H.FullResponse] {
return F.Flow3(
client.Do,
IOE.ChainEitherK(H.ValidateResponse),
IOE.Chain(func(resp *http.Response) IOE.IOEither[error, H.FullResponse] {
return F.Pipe1(
F.Pipe3(
resp,
H.GetBody,
IOE.Of[error, io.ReadCloser],
IOEF.ReadAll[io.ReadCloser],
),
IOE.Map[error](F.Bind1st(T.MakeTuple2[*http.Response, []byte], resp)),
)
}),
)
}
func ReadText(client Client) func(*http.Request) IOE.IOEither[error, string] {
// ReadAll sends a request and reads the response as bytes
func ReadAll(client Client) func(Requester) IOE.IOEither[error, []byte] {
return F.Flow2(
ReadFullResponse(client),
IOE.Map[error](H.Body),
)
}
// ReadText sends a request, reads the response and represents the response as a text string
func ReadText(client Client) func(Requester) IOE.IOEither[error, string] {
return F.Flow2(
ReadAll(client),
IOE.Map[error](B.ToString),
)
}
func ReadJson[A any](client Client) func(*http.Request) IOE.IOEither[error, A] {
return F.Flow2(
ReadAll(client),
IOE.ChainEitherK(J.Unmarshal[A]),
// ReadJson sends a request, reads the response and parses the response as JSON
func ReadJson[A any](client Client) func(Requester) IOE.IOEither[error, A] {
return F.Flow3(
ReadFullResponse(client),
IOE.ChainFirstEitherK(F.Flow2(
H.Response,
H.ValidateJsonResponse,
)),
IOE.ChainEitherK(F.Flow2(
H.Body,
J.Unmarshal[A],
)),
)
}

View File

@@ -23,7 +23,6 @@ import (
AR "github.com/IBM/fp-go/array"
E "github.com/IBM/fp-go/either"
HE "github.com/IBM/fp-go/either/http"
"github.com/IBM/fp-go/errors"
F "github.com/IBM/fp-go/function"
IOE "github.com/IBM/fp-go/ioeither"
@@ -53,17 +52,16 @@ func TestRetryHttp(t *testing.T) {
client := MakeClient(&http.Client{})
action := func(status R.RetryStatus) IOE.IOEither[error, *PostItem] {
return F.Pipe2(
HE.GetRequest(urls[status.IterNumber]),
IOE.FromEither[error, *http.Request],
IOE.Chain(ReadJson[*PostItem](client)),
return F.Pipe1(
MakeGetRequest(urls[status.IterNumber]),
ReadJson[*PostItem](client),
)
}
check := E.Fold(
F.Flow2(
errors.As[*net.DNSError](),
O.Fold(F.Constant(false), F.Constant1[*net.DNSError](true)),
O.IsSome[*net.DNSError],
),
F.Constant1[*PostItem](false),
)

View File

@@ -125,10 +125,29 @@ func MonadAp[B, E, A any](mab IOEither[E, func(A) B], ma IOEither[E, A]) IOEithe
return G.MonadAp[IOEither[E, B]](mab, ma)
}
// Ap is an alias of [ApPar]
func Ap[B, E, A any](ma IOEither[E, A]) func(IOEither[E, func(A) B]) IOEither[E, B] {
return G.Ap[IOEither[E, B], IOEither[E, func(A) B]](ma)
}
func MonadApPar[B, E, A any](mab IOEither[E, func(A) B], ma IOEither[E, A]) IOEither[E, B] {
return G.MonadApPar[IOEither[E, B]](mab, ma)
}
// ApPar applies function and value in parallel
func ApPar[B, E, A any](ma IOEither[E, A]) func(IOEither[E, func(A) B]) IOEither[E, B] {
return G.ApPar[IOEither[E, B], IOEither[E, func(A) B]](ma)
}
func MonadApSeq[B, E, A any](mab IOEither[E, func(A) B], ma IOEither[E, A]) IOEither[E, B] {
return G.MonadApSeq[IOEither[E, B]](mab, ma)
}
// ApSeq applies function and value sequentially
func ApSeq[B, E, A any](ma IOEither[E, A]) func(IOEither[E, func(A) B]) IOEither[E, B] {
return G.ApSeq[IOEither[E, B], IOEither[E, func(A) B]](ma)
}
func Flatten[E, A any](mma IOEither[E, IOEither[E, A]]) IOEither[E, A] {
return G.Flatten(mma)
}
@@ -192,6 +211,14 @@ func ChainFirst[E, A, B any](f func(A) IOEither[E, B]) func(IOEither[E, A]) IOEi
return G.ChainFirst[IOEither[E, A]](f)
}
func MonadChainFirstEitherK[A, E, B any](ma IOEither[E, A], f func(A) ET.Either[E, B]) IOEither[E, A] {
return G.MonadChainFirstEitherK[IOEither[E, A]](ma, f)
}
func ChainFirstEitherK[A, E, B any](f func(A) ET.Either[E, B]) func(ma IOEither[E, A]) IOEither[E, A] {
return G.ChainFirstEitherK[IOEither[E, A]](f)
}
// MonadChainFirstIOK runs [IO] the monad returned by the function but returns the result of the original monad
func MonadChainFirstIOK[E, A, B any](ma IOEither[E, A], f func(A) I.IO[B]) IOEither[E, A] {
return G.MonadChainFirstIOK(ma, f)
@@ -231,3 +258,11 @@ func MonadAlt[E, A any](first IOEither[E, A], second L.Lazy[IOEither[E, A]]) IOE
func Alt[E, A any](second L.Lazy[IOEither[E, A]]) func(IOEither[E, A]) IOEither[E, A] {
return G.Alt(second)
}
func MonadFlap[E, B, A any](fab IOEither[E, func(A) B], a A) IOEither[E, B] {
return G.MonadFlap[IOEither[E, func(A) B], IOEither[E, B]](fab, a)
}
func Flap[E, B, A any](a A) func(IOEither[E, func(A) B]) IOEither[E, B] {
return G.Flap[IOEither[E, func(A) B], IOEither[E, B]](a)
}

28
ioeither/sync.go Normal file
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 ioeither
import (
"context"
IO "github.com/IBM/fp-go/io"
G "github.com/IBM/fp-go/ioeither/generic"
)
// WithLock executes the provided IO operation in the scope of a lock
func WithLock[E, A any](lock IO.IO[context.CancelFunc]) func(fa IOEither[E, A]) IOEither[E, A] {
return G.WithLock[IOEither[E, A]](lock)
}

31
iooption/bracket.go Normal file
View File

@@ -0,0 +1,31 @@
// 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 iooption
import (
G "github.com/IBM/fp-go/iooption/generic"
O "github.com/IBM/fp-go/option"
)
// 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[E, A, B, ANY any](
acquire IOOption[A],
use func(A) IOOption[B],
release func(A, O.Option[B]) IOOption[ANY],
) IOOption[B] {
return G.Bracket(acquire, use, release)
}

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:32.1514823 +0200 CEST m=+0.098931001
// 2023-10-23 08:31:01.9227118 +0200 CEST m=+0.009977901
package iooption

View File

@@ -0,0 +1,46 @@
// 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 (
G "github.com/IBM/fp-go/internal/bracket"
I "github.com/IBM/fp-go/io/generic"
O "github.com/IBM/fp-go/option"
)
// 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() O.Option[A],
GB ~func() O.Option[B],
GANY ~func() O.Option[ANY],
A, B, ANY any](
acquire GA,
use func(A) GB,
release func(A, O.Option[B]) GANY,
) GB {
return G.Bracket[GA, GB, GANY, O.Option[B], A, B](
I.Of[GB, O.Option[B]],
MonadChain[GA, GB, A, B],
I.MonadChain[GB, GB, O.Option[B], O.Option[B]],
MonadChain[GANY, GB, ANY, B],
acquire,
use,
release,
)
}

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:32.1546277 +0200 CEST m=+0.102076401
// 2023-10-23 08:31:01.9227118 +0200 CEST m=+0.009977901
package generic
import (

28
iooption/generic/sync.go Normal file
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
import (
"context"
G "github.com/IBM/fp-go/io/generic"
O "github.com/IBM/fp-go/option"
)
// WithLock executes the provided IO operation in the scope of a lock
func WithLock[GA ~func() O.Option[A], A any](lock func() context.CancelFunc) func(fa GA) GA {
return G.WithLock[GA](lock)
}

28
iooption/sync.go Normal file
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 iooption
import (
"context"
IO "github.com/IBM/fp-go/io"
G "github.com/IBM/fp-go/iooption/generic"
)
// WithLock executes the provided IO operation in the scope of a lock
func WithLock[E, A any](lock IO.IO[context.CancelFunc]) func(fa IOOption[A]) IOOption[A] {
return G.WithLock[IOOption[A]](lock)
}

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