mirror of
https://github.com/IBM/fp-go.git
synced 2025-12-09 23:11:40 +02:00
Compare commits
19 Commits
cleue-add-
...
cleue-add-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a0e4fd9e14 | ||
|
|
b4bf511f03 | ||
|
|
211340952b | ||
|
|
56860425c4 | ||
|
|
c0b16c675b | ||
|
|
4b68e66528 | ||
|
|
5b7e5b153b | ||
|
|
d43fbeb375 | ||
|
|
57d507c1ba | ||
|
|
aa19857127 | ||
|
|
7766787cc1 | ||
|
|
6a9f38f990 | ||
|
|
e9584bc247 | ||
|
|
55252c5680 | ||
|
|
88bf35c4af | ||
|
|
da3c9683eb | ||
|
|
08d9fed9af | ||
|
|
83a0c6cdef | ||
|
|
e46cfd89d4 |
6
.github/workflows/build.yml
vendored
6
.github/workflows/build.yml
vendored
@@ -29,7 +29,7 @@ jobs:
|
|||||||
go-version: [ '1.20.x', '1.21.x' ]
|
go-version: [ '1.20.x', '1.21.x' ]
|
||||||
steps:
|
steps:
|
||||||
# full checkout for semantic-release
|
# full checkout for semantic-release
|
||||||
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
|
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Set up go ${{ matrix.go-version }}
|
- name: Set up go ${{ matrix.go-version }}
|
||||||
@@ -55,12 +55,12 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
# full checkout for semantic-release
|
# full checkout for semantic-release
|
||||||
- name: Full checkout
|
- name: Full checkout
|
||||||
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
|
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Set up Node.js ${{ env.NODE_VERSION }}
|
- name: Set up Node.js ${{ env.NODE_VERSION }}
|
||||||
uses: actions/setup-node@e33196f7422957bea03ed53f6fbb155025ffc7b8 # v3.7.0
|
uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4.0.0
|
||||||
with:
|
with:
|
||||||
node-version: ${{ env.NODE_VERSION }}
|
node-version: ${{ env.NODE_VERSION }}
|
||||||
|
|
||||||
|
|||||||
30
array/any.go
Normal file
30
array/any.go
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package array
|
||||||
|
|
||||||
|
import (
|
||||||
|
G "github.com/IBM/fp-go/array/generic"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AnyWithIndex tests if any of the elements in the array matches the predicate
|
||||||
|
func AnyWithIndex[A any](pred func(int, A) bool) func([]A) bool {
|
||||||
|
return G.AnyWithIndex[[]A](pred)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Any tests if any of the elements in the array matches the predicate
|
||||||
|
func Any[A any](pred func(A) bool) func([]A) bool {
|
||||||
|
return G.Any[[]A](pred)
|
||||||
|
}
|
||||||
@@ -13,14 +13,18 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
// Package reader implements a specialization of the Reader monad assuming a golang context as the context of the monad
|
package array
|
||||||
package reader
|
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"testing"
|
||||||
|
|
||||||
R "github.com/IBM/fp-go/reader"
|
F "github.com/IBM/fp-go/function"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Reader is a specialization of the Reader monad assuming a golang context as the context of the monad
|
func TestAny(t *testing.T) {
|
||||||
type Reader[A any] R.Reader[context.Context, A]
|
anyBool := Any(F.Identity[bool])
|
||||||
|
|
||||||
|
assert.True(t, anyBool(From(false, true, false)))
|
||||||
|
assert.False(t, anyBool(From(false, false, false)))
|
||||||
|
}
|
||||||
@@ -52,6 +52,10 @@ func MonadMapRef[A, B any](as []A, f func(a *A) B) []B {
|
|||||||
return bs
|
return bs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func MapWithIndex[A, B any](f func(int, A) B) func([]A) []B {
|
||||||
|
return G.MapWithIndex[[]A, []B](f)
|
||||||
|
}
|
||||||
|
|
||||||
func Map[A, B any](f func(a A) B) func([]A) []B {
|
func Map[A, B any](f func(a A) B) func([]A) []B {
|
||||||
return F.Bind2nd(MonadMap[A, B], f)
|
return 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)
|
return F.Bind2nd(MonadMapRef[A, B], f)
|
||||||
}
|
}
|
||||||
|
|
||||||
func filter[A any](fa []A, pred func(A) bool) []A {
|
|
||||||
var result []A
|
|
||||||
count := len(fa)
|
|
||||||
for i := 0; i < count; i++ {
|
|
||||||
a := fa[i]
|
|
||||||
if pred(a) {
|
|
||||||
result = append(result, a)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func filterRef[A any](fa []A, pred func(a *A) bool) []A {
|
func filterRef[A any](fa []A, pred func(a *A) bool) []A {
|
||||||
var result []A
|
var result []A
|
||||||
count := len(fa)
|
count := len(fa)
|
||||||
@@ -96,23 +88,38 @@ func filterMapRef[A, B any](fa []A, pred func(a *A) bool, f func(a *A) B) []B {
|
|||||||
return result
|
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 {
|
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 {
|
func FilterRef[A any](pred func(*A) bool) func([]A) []A {
|
||||||
return F.Bind2nd(filterRef[A], pred)
|
return F.Bind2nd(filterRef[A], pred)
|
||||||
}
|
}
|
||||||
|
|
||||||
func MonadFilterMap[A, B any](fa []A, f func(a A) O.Option[B]) []B {
|
func MonadFilterMap[A, B any](fa []A, f func(A) O.Option[B]) []B {
|
||||||
return G.MonadFilterMap[[]A, []B](fa, f)
|
return G.MonadFilterMap[[]A, []B](fa, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FilterChain maps an array with an iterating function that returns an [O.Option] and it keeps only the Some values discarding the Nones.
|
func MonadFilterMapWithIndex[A, B any](fa []A, f func(int, A) O.Option[B]) []B {
|
||||||
func FilterMap[A, B any](f func(a A) O.Option[B]) func([]A) []B {
|
return G.MonadFilterMapWithIndex[[]A, []B](fa, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FilterMap maps an array with an iterating function that returns an [O.Option] and it keeps only the Some values discarding the Nones.
|
||||||
|
func FilterMap[A, B any](f func(A) O.Option[B]) func([]A) []B {
|
||||||
return G.FilterMap[[]A, []B](f)
|
return G.FilterMap[[]A, []B](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FilterMapWithIndex maps an array with an iterating function that returns an [O.Option] and it keeps only the Some values discarding the Nones.
|
||||||
|
func FilterMapWithIndex[A, B any](f func(int, A) O.Option[B]) func([]A) []B {
|
||||||
|
return G.FilterMapWithIndex[[]A, []B](f)
|
||||||
|
}
|
||||||
|
|
||||||
// FilterChain maps an array with an iterating function that returns an [O.Option] of an array. It keeps only the Some values discarding the Nones and then flattens the result.
|
// FilterChain maps an array with an iterating function that returns an [O.Option] of an array. It keeps only the Some values discarding the Nones and then flattens the result.
|
||||||
func FilterChain[A, B any](f func(A) O.Option[[]B]) func([]A) []B {
|
func FilterChain[A, B any](f func(A) O.Option[[]B]) func([]A) []B {
|
||||||
return G.FilterChain[[]A](f)
|
return G.FilterChain[[]A](f)
|
||||||
@@ -134,9 +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 {
|
func Reduce[A, B any](f func(B, A) B, initial B) func([]A) B {
|
||||||
return func(as []A) B {
|
return G.Reduce[[]A](f, initial)
|
||||||
return array.Reduce(as, f, initial)
|
}
|
||||||
}
|
|
||||||
|
func ReduceWithIndex[A, B any](f func(int, B, A) B, initial B) func([]A) B {
|
||||||
|
return G.ReduceWithIndex[[]A](f, initial)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReduceRight[A, B any](f func(A, B) B, initial B) func([]A) B {
|
||||||
|
return G.ReduceRight[[]A](f, initial)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReduceRightWithIndex[A, B any](f func(int, A, B) B, initial B) func([]A) B {
|
||||||
|
return G.ReduceRightWithIndex[[]A](f, initial)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ReduceRef[A, B any](f func(B, *A) B, initial B) func([]A) B {
|
func ReduceRef[A, B any](f func(B, *A) B, initial B) func([]A) B {
|
||||||
@@ -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)
|
return G.FoldMap[[]A](m)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FoldMapWithIndex maps and folds an array. Map the Array passing each value to the iterating function. Then fold the results using the provided Monoid.
|
||||||
|
func FoldMapWithIndex[A, B any](m M.Monoid[B]) func(func(int, A) B) func([]A) B {
|
||||||
|
return G.FoldMapWithIndex[[]A](m)
|
||||||
|
}
|
||||||
|
|
||||||
// Fold folds the array using the provided Monoid.
|
// Fold folds the array using the provided Monoid.
|
||||||
func Fold[A any](m M.Monoid[A]) func([]A) A {
|
func Fold[A any](m M.Monoid[A]) func([]A) A {
|
||||||
return G.Fold[[]A](m)
|
return G.Fold[[]A](m)
|
||||||
|
|||||||
@@ -59,6 +59,17 @@ func TestMap(t *testing.T) {
|
|||||||
assert.Equal(t, dst, []string{"A", "B", "C"})
|
assert.Equal(t, dst, []string{"A", "B", "C"})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestReduceRight(t *testing.T) {
|
||||||
|
values := From("a", "b", "c")
|
||||||
|
f := func(a, acc string) string {
|
||||||
|
return fmt.Sprintf("%s%s", acc, a)
|
||||||
|
}
|
||||||
|
b := ""
|
||||||
|
|
||||||
|
assert.Equal(t, "cba", ReduceRight(f, b)(values))
|
||||||
|
assert.Equal(t, "", ReduceRight(f, b)(Empty[string]()))
|
||||||
|
}
|
||||||
|
|
||||||
func TestReduce(t *testing.T) {
|
func TestReduce(t *testing.T) {
|
||||||
|
|
||||||
values := MakeBy(101, F.Identity[int])
|
values := MakeBy(101, F.Identity[int])
|
||||||
|
|||||||
77
array/example_any_test.go
Normal file
77
array/example_any_test.go
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package array
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
F "github.com/IBM/fp-go/function"
|
||||||
|
O "github.com/IBM/fp-go/option"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Example_any() {
|
||||||
|
|
||||||
|
pred := func(val int) bool {
|
||||||
|
return val&2 == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
data1 := From(1, 2, 3)
|
||||||
|
|
||||||
|
fmt.Println(Any(pred)(data1))
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// true
|
||||||
|
}
|
||||||
|
|
||||||
|
func Example_any_filter() {
|
||||||
|
|
||||||
|
pred := func(val int) bool {
|
||||||
|
return val&2 == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
data1 := From(1, 2, 3)
|
||||||
|
|
||||||
|
// Any tests if any of the entries in the array matches the condition
|
||||||
|
Any := F.Flow2(
|
||||||
|
Filter(pred),
|
||||||
|
IsNonEmpty[int],
|
||||||
|
)
|
||||||
|
|
||||||
|
fmt.Println(Any(data1))
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// true
|
||||||
|
}
|
||||||
|
|
||||||
|
func Example_any_find() {
|
||||||
|
|
||||||
|
pred := func(val int) bool {
|
||||||
|
return val&2 == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
data1 := From(1, 2, 3)
|
||||||
|
|
||||||
|
// Any tests if any of the entries in the array matches the condition
|
||||||
|
Any := F.Flow2(
|
||||||
|
FindFirst(pred),
|
||||||
|
O.IsSome[int],
|
||||||
|
)
|
||||||
|
|
||||||
|
fmt.Println(Any(data1))
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// true
|
||||||
|
}
|
||||||
55
array/example_find_test.go
Normal file
55
array/example_find_test.go
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package array
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
F "github.com/IBM/fp-go/function"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Example_find() {
|
||||||
|
|
||||||
|
pred := func(val int) bool {
|
||||||
|
return val&2 == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
data1 := From(1, 2, 3)
|
||||||
|
|
||||||
|
fmt.Println(FindFirst(pred)(data1))
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// Some[int](1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Example_find_filter() {
|
||||||
|
|
||||||
|
pred := func(val int) bool {
|
||||||
|
return val&2 == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
data1 := From(1, 2, 3)
|
||||||
|
|
||||||
|
Find := F.Flow2(
|
||||||
|
Filter(pred),
|
||||||
|
Head[int],
|
||||||
|
)
|
||||||
|
|
||||||
|
fmt.Println(Find(data1))
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// Some[int](1)
|
||||||
|
}
|
||||||
61
array/find.go
Normal file
61
array/find.go
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package array
|
||||||
|
|
||||||
|
import (
|
||||||
|
G "github.com/IBM/fp-go/array/generic"
|
||||||
|
O "github.com/IBM/fp-go/option"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FindFirst finds the first element which satisfies a predicate (or a refinement) function
|
||||||
|
func FindFirst[A any](pred func(A) bool) func([]A) O.Option[A] {
|
||||||
|
return G.FindFirst[[]A](pred)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindFirstWithIndex finds the first element which satisfies a predicate (or a refinement) function
|
||||||
|
func FindFirstWithIndex[A any](pred func(int, A) bool) func([]A) O.Option[A] {
|
||||||
|
return G.FindFirstWithIndex[[]A](pred)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindFirstMap finds the first element returned by an [O.Option] based selector function
|
||||||
|
func FindFirstMap[A, B any](sel func(A) O.Option[B]) func([]A) O.Option[B] {
|
||||||
|
return G.FindFirstMap[[]A](sel)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindFirstMapWithIndex finds the first element returned by an [O.Option] based selector function
|
||||||
|
func FindFirstMapWithIndex[A, B any](sel func(int, A) O.Option[B]) func([]A) O.Option[B] {
|
||||||
|
return G.FindFirstMapWithIndex[[]A](sel)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindLast finds the Last element which satisfies a predicate (or a refinement) function
|
||||||
|
func FindLast[A any](pred func(A) bool) func([]A) O.Option[A] {
|
||||||
|
return G.FindLast[[]A](pred)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindLastWithIndex finds the Last element which satisfies a predicate (or a refinement) function
|
||||||
|
func FindLastWithIndex[A any](pred func(int, A) bool) func([]A) O.Option[A] {
|
||||||
|
return G.FindLastWithIndex[[]A](pred)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindLastMap finds the Last element returned by an [O.Option] based selector function
|
||||||
|
func FindLastMap[A, B any](sel func(A) O.Option[B]) func([]A) O.Option[B] {
|
||||||
|
return G.FindLastMap[[]A](sel)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindLastMapWithIndex finds the Last element returned by an [O.Option] based selector function
|
||||||
|
func FindLastMapWithIndex[A, B any](sel func(int, A) O.Option[B]) func([]A) O.Option[B] {
|
||||||
|
return G.FindLastMapWithIndex[[]A](sel)
|
||||||
|
}
|
||||||
34
array/generic/any.go
Normal file
34
array/generic/any.go
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package generic
|
||||||
|
|
||||||
|
import (
|
||||||
|
F "github.com/IBM/fp-go/function"
|
||||||
|
O "github.com/IBM/fp-go/option"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AnyWithIndex tests if any of the elements in the array matches the predicate
|
||||||
|
func AnyWithIndex[AS ~[]A, PRED ~func(int, A) bool, A any](pred PRED) func(AS) bool {
|
||||||
|
return F.Flow2(
|
||||||
|
FindFirstWithIndex[AS](pred),
|
||||||
|
O.IsSome[A],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Any tests if any of the elements in the array matches the predicate
|
||||||
|
func Any[AS ~[]A, PRED ~func(A) bool, A any](pred PRED) func(AS) bool {
|
||||||
|
return AnyWithIndex[AS](F.Ignore1of2[int](pred))
|
||||||
|
}
|
||||||
@@ -29,14 +29,46 @@ func Of[GA ~[]A, A any](value A) GA {
|
|||||||
return GA{value}
|
return GA{value}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Reduce[GA ~[]A, A, B any](fa GA, f func(B, A) B, initial B) B {
|
func Reduce[GA ~[]A, A, B any](f func(B, A) B, initial B) func(GA) B {
|
||||||
|
return func(as GA) B {
|
||||||
|
return MonadReduce[GA](as, f, initial)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReduceWithIndex[GA ~[]A, A, B any](f func(int, B, A) B, initial B) func(GA) B {
|
||||||
|
return func(as GA) B {
|
||||||
|
return MonadReduceWithIndex[GA](as, f, initial)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReduceRight[GA ~[]A, A, B any](f func(A, B) B, initial B) func(GA) B {
|
||||||
|
return func(as GA) B {
|
||||||
|
return MonadReduceRight[GA](as, f, initial)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReduceRightWithIndex[GA ~[]A, A, B any](f func(int, A, B) B, initial B) func(GA) B {
|
||||||
|
return func(as GA) B {
|
||||||
|
return MonadReduceRightWithIndex[GA](as, f, initial)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func MonadReduce[GA ~[]A, A, B any](fa GA, f func(B, A) B, initial B) B {
|
||||||
return array.Reduce(fa, f, initial)
|
return array.Reduce(fa, f, initial)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ReduceWithIndex[GA ~[]A, A, B any](fa GA, f func(int, B, A) B, initial B) B {
|
func MonadReduceWithIndex[GA ~[]A, A, B any](fa GA, f func(int, B, A) B, initial B) B {
|
||||||
return array.ReduceWithIndex(fa, f, initial)
|
return array.ReduceWithIndex(fa, f, initial)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func MonadReduceRight[GA ~[]A, A, B any](fa GA, f func(A, B) B, initial B) B {
|
||||||
|
return array.ReduceRight(fa, f, initial)
|
||||||
|
}
|
||||||
|
|
||||||
|
func MonadReduceRightWithIndex[GA ~[]A, A, B any](fa GA, f func(int, A, B) B, initial B) B {
|
||||||
|
return array.ReduceRightWithIndex(fa, f, initial)
|
||||||
|
}
|
||||||
|
|
||||||
// From constructs an array from a set of variadic arguments
|
// From constructs an array from a set of variadic arguments
|
||||||
func From[GA ~[]A, A any](data ...A) GA {
|
func From[GA ~[]A, A any](data ...A) GA {
|
||||||
return data
|
return data
|
||||||
@@ -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)
|
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 {
|
func Size[GA ~[]A, A any](as GA) int {
|
||||||
return len(as)
|
return len(as)
|
||||||
}
|
}
|
||||||
|
|
||||||
func filterMap[GA ~[]A, GB ~[]B, A, B any](fa GA, f func(a A) O.Option[B]) GB {
|
func filterMap[GA ~[]A, GB ~[]B, A, B any](fa GA, f func(A) O.Option[B]) GB {
|
||||||
return array.Reduce(fa, func(bs GB, a A) GB {
|
return array.Reduce(fa, func(bs GB, a A) GB {
|
||||||
return O.MonadFold(f(a), F.Constant(bs), F.Bind1st(Append[GB, B], bs))
|
return O.MonadFold(f(a), F.Constant(bs), F.Bind1st(Append[GB, B], bs))
|
||||||
}, Empty[GB]())
|
}, Empty[GB]())
|
||||||
}
|
}
|
||||||
|
|
||||||
func MonadFilterMap[GA ~[]A, GB ~[]B, A, B any](fa GA, f func(a A) O.Option[B]) GB {
|
func filterMapWithIndex[GA ~[]A, GB ~[]B, A, B any](fa GA, f func(int, A) O.Option[B]) GB {
|
||||||
|
return array.ReduceWithIndex(fa, func(idx int, bs GB, a A) GB {
|
||||||
|
return O.MonadFold(f(idx, a), F.Constant(bs), F.Bind1st(Append[GB, B], bs))
|
||||||
|
}, Empty[GB]())
|
||||||
|
}
|
||||||
|
|
||||||
|
func MonadFilterMap[GA ~[]A, GB ~[]B, A, B any](fa GA, f func(A) O.Option[B]) GB {
|
||||||
return filterMap[GA, GB](fa, f)
|
return filterMap[GA, GB](fa, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func MonadFilterMapWithIndex[GA ~[]A, GB ~[]B, A, B any](fa GA, f func(int, A) O.Option[B]) GB {
|
||||||
|
return filterMapWithIndex[GA, GB](fa, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func filterWithIndex[AS ~[]A, PRED ~func(int, A) bool, A any](fa AS, pred PRED) AS {
|
||||||
|
result := make(AS, 0, len(fa))
|
||||||
|
for i, a := range fa {
|
||||||
|
if pred(i, a) {
|
||||||
|
result = append(result, a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func FilterWithIndex[AS ~[]A, PRED ~func(int, A) bool, A any](pred PRED) func(AS) AS {
|
||||||
|
return F.Bind2nd(filterWithIndex[AS, PRED, A], pred)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Filter[AS ~[]A, PRED ~func(A) bool, A any](pred PRED) func(AS) AS {
|
||||||
|
return FilterWithIndex[AS](F.Ignore1of2[int](pred))
|
||||||
|
}
|
||||||
|
|
||||||
func FilterChain[GA ~[]A, GB ~[]B, A, B any](f func(a A) O.Option[GB]) func(GA) GB {
|
func FilterChain[GA ~[]A, GB ~[]B, A, B any](f func(a A) O.Option[GB]) func(GA) GB {
|
||||||
return F.Flow2(
|
return F.Flow2(
|
||||||
FilterMap[GA, []GB](f),
|
FilterMap[GA, []GB](f),
|
||||||
@@ -143,10 +211,14 @@ func Flatten[GAA ~[]GA, GA ~[]A, A any](mma GAA) GA {
|
|||||||
return MonadChain(mma, F.Identity[GA])
|
return MonadChain(mma, F.Identity[GA])
|
||||||
}
|
}
|
||||||
|
|
||||||
func FilterMap[GA ~[]A, GB ~[]B, A, B any](f func(a A) O.Option[B]) func(GA) GB {
|
func FilterMap[GA ~[]A, GB ~[]B, A, B any](f func(A) O.Option[B]) func(GA) GB {
|
||||||
return F.Bind2nd(MonadFilterMap[GA, GB, A, B], f)
|
return F.Bind2nd(MonadFilterMap[GA, GB, A, B], f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func FilterMapWithIndex[GA ~[]A, GB ~[]B, A, B any](f func(int, A) O.Option[B]) func(GA) GB {
|
||||||
|
return F.Bind2nd(MonadFilterMapWithIndex[GA, GB, A, B], f)
|
||||||
|
}
|
||||||
|
|
||||||
func MonadPartition[GA ~[]A, A any](as GA, pred func(A) bool) tuple.Tuple2[GA, GA] {
|
func MonadPartition[GA ~[]A, A any](as GA, pred func(A) bool) tuple.Tuple2[GA, GA] {
|
||||||
left := Empty[GA]()
|
left := Empty[GA]()
|
||||||
right := Empty[GA]()
|
right := Empty[GA]()
|
||||||
@@ -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 {
|
func Fold[AS ~[]A, A any](m M.Monoid[A]) func(AS) A {
|
||||||
return func(as AS) A {
|
return func(as AS) A {
|
||||||
return array.Reduce(as, m.Concat, m.Empty())
|
return array.Reduce(as, m.Concat, m.Empty())
|
||||||
|
|||||||
97
array/generic/find.go
Normal file
97
array/generic/find.go
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package generic
|
||||||
|
|
||||||
|
import (
|
||||||
|
F "github.com/IBM/fp-go/function"
|
||||||
|
O "github.com/IBM/fp-go/option"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FindFirstWithIndex finds the first element which satisfies a predicate (or a refinement) function
|
||||||
|
func FindFirstWithIndex[AS ~[]A, PRED ~func(int, A) bool, A any](pred PRED) func(AS) O.Option[A] {
|
||||||
|
none := O.None[A]()
|
||||||
|
return func(as AS) O.Option[A] {
|
||||||
|
for i, a := range as {
|
||||||
|
if pred(i, a) {
|
||||||
|
return O.Some(a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return none
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindFirst finds the first element which satisfies a predicate (or a refinement) function
|
||||||
|
func FindFirst[AS ~[]A, PRED ~func(A) bool, A any](pred PRED) func(AS) O.Option[A] {
|
||||||
|
return FindFirstWithIndex[AS](F.Ignore1of2[int](pred))
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindFirstMapWithIndex finds the first element returned by an [O.Option] based selector function
|
||||||
|
func FindFirstMapWithIndex[AS ~[]A, PRED ~func(int, A) O.Option[B], A, B any](pred PRED) func(AS) O.Option[B] {
|
||||||
|
none := O.None[B]()
|
||||||
|
return func(as AS) O.Option[B] {
|
||||||
|
count := len(as)
|
||||||
|
for i := 0; i < count; i++ {
|
||||||
|
out := pred(i, as[i])
|
||||||
|
if O.IsSome(out) {
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return none
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindFirstMap finds the first element returned by an [O.Option] based selector function
|
||||||
|
func FindFirstMap[AS ~[]A, PRED ~func(A) O.Option[B], A, B any](pred PRED) func(AS) O.Option[B] {
|
||||||
|
return FindFirstMapWithIndex[AS](F.Ignore1of2[int](pred))
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindLastWithIndex finds the first element which satisfies a predicate (or a refinement) function
|
||||||
|
func FindLastWithIndex[AS ~[]A, PRED ~func(int, A) bool, A any](pred PRED) func(AS) O.Option[A] {
|
||||||
|
none := O.None[A]()
|
||||||
|
return func(as AS) O.Option[A] {
|
||||||
|
for i := len(as) - 1; i >= 0; i-- {
|
||||||
|
a := as[i]
|
||||||
|
if pred(i, a) {
|
||||||
|
return O.Some(a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return none
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindLast finds the first element which satisfies a predicate (or a refinement) function
|
||||||
|
func FindLast[AS ~[]A, PRED ~func(A) bool, A any](pred PRED) func(AS) O.Option[A] {
|
||||||
|
return FindLastWithIndex[AS](F.Ignore1of2[int](pred))
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindLastMapWithIndex finds the first element returned by an [O.Option] based selector function
|
||||||
|
func FindLastMapWithIndex[AS ~[]A, PRED ~func(int, A) O.Option[B], A, B any](pred PRED) func(AS) O.Option[B] {
|
||||||
|
none := O.None[B]()
|
||||||
|
return func(as AS) O.Option[B] {
|
||||||
|
for i := len(as) - 1; i >= 0; i-- {
|
||||||
|
out := pred(i, as[i])
|
||||||
|
if O.IsSome(out) {
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return none
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindLastMap finds the first element returned by an [O.Option] based selector function
|
||||||
|
func FindLastMap[AS ~[]A, PRED ~func(A) O.Option[B], A, B any](pred PRED) func(AS) O.Option[B] {
|
||||||
|
return FindLastMapWithIndex[AS](F.Ignore1of2[int](pred))
|
||||||
|
}
|
||||||
@@ -18,6 +18,7 @@ package array
|
|||||||
import (
|
import (
|
||||||
"github.com/IBM/fp-go/internal/array"
|
"github.com/IBM/fp-go/internal/array"
|
||||||
M "github.com/IBM/fp-go/monoid"
|
M "github.com/IBM/fp-go/monoid"
|
||||||
|
S "github.com/IBM/fp-go/semigroup"
|
||||||
)
|
)
|
||||||
|
|
||||||
func concat[T any](left, right []T) []T {
|
func concat[T any](left, right []T) []T {
|
||||||
@@ -40,6 +41,10 @@ func Monoid[T any]() M.Monoid[[]T] {
|
|||||||
return M.MakeMonoid(concat[T], Empty[T]())
|
return M.MakeMonoid(concat[T], Empty[T]())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Semigroup[T any]() S.Semigroup[[]T] {
|
||||||
|
return S.MakeSemigroup(concat[T])
|
||||||
|
}
|
||||||
|
|
||||||
func addLen[A any](count int, data []A) int {
|
func addLen[A any](count int, data []A) int {
|
||||||
return count + len(data)
|
return count + len(data)
|
||||||
}
|
}
|
||||||
|
|||||||
109
assert/assert_test.go
Normal file
109
assert/assert_test.go
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package assert
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
E "github.com/IBM/fp-go/either"
|
||||||
|
EQ "github.com/IBM/fp-go/eq"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errTest = fmt.Errorf("test failure")
|
||||||
|
|
||||||
|
// Eq is the equal predicate checking if objects are equal
|
||||||
|
Eq = EQ.FromEquals(assert.ObjectsAreEqual)
|
||||||
|
)
|
||||||
|
|
||||||
|
func wrap1[T any](wrapped func(t assert.TestingT, expected, actual any, msgAndArgs ...any) bool, t *testing.T, expected T) func(actual T) E.Either[error, T] {
|
||||||
|
return func(actual T) E.Either[error, T] {
|
||||||
|
ok := wrapped(t, expected, actual)
|
||||||
|
if ok {
|
||||||
|
return E.Of[error](actual)
|
||||||
|
}
|
||||||
|
return E.Left[T](errTest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NotEqual tests if the expected and the actual values are not equal
|
||||||
|
func NotEqual[T any](t *testing.T, expected T) func(actual T) E.Either[error, T] {
|
||||||
|
return wrap1(assert.NotEqual, t, expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equal tests if the expected and the actual values are equal
|
||||||
|
func Equal[T any](t *testing.T, expected T) func(actual T) E.Either[error, T] {
|
||||||
|
return wrap1(assert.Equal, t, expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Length tests if an array has the expected length
|
||||||
|
func Length[T any](t *testing.T, expected int) func(actual []T) E.Either[error, []T] {
|
||||||
|
return func(actual []T) E.Either[error, []T] {
|
||||||
|
ok := assert.Len(t, actual, expected)
|
||||||
|
if ok {
|
||||||
|
return E.Of[error](actual)
|
||||||
|
}
|
||||||
|
return E.Left[[]T](errTest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NoError validates that there is no error
|
||||||
|
func NoError[T any](t *testing.T) func(actual E.Either[error, T]) E.Either[error, T] {
|
||||||
|
return func(actual E.Either[error, T]) E.Either[error, T] {
|
||||||
|
return E.MonadFold(actual, func(e error) E.Either[error, T] {
|
||||||
|
assert.NoError(t, e)
|
||||||
|
return E.Left[T](e)
|
||||||
|
}, func(value T) E.Either[error, T] {
|
||||||
|
assert.NoError(t, nil)
|
||||||
|
return E.Right[error](value)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ArrayContains tests if a value is contained in an array
|
||||||
|
func ArrayContains[T any](t *testing.T, expected T) func(actual []T) E.Either[error, []T] {
|
||||||
|
return func(actual []T) E.Either[error, []T] {
|
||||||
|
ok := assert.Contains(t, actual, expected)
|
||||||
|
if ok {
|
||||||
|
return E.Of[error](actual)
|
||||||
|
}
|
||||||
|
return E.Left[[]T](errTest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainsKey tests if a key is contained in a map
|
||||||
|
func ContainsKey[T any, K comparable](t *testing.T, expected K) func(actual map[K]T) E.Either[error, map[K]T] {
|
||||||
|
return func(actual map[K]T) E.Either[error, map[K]T] {
|
||||||
|
ok := assert.Contains(t, actual, expected)
|
||||||
|
if ok {
|
||||||
|
return E.Of[error](actual)
|
||||||
|
}
|
||||||
|
return E.Left[map[K]T](errTest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NotContainsKey tests if a key is not contained in a map
|
||||||
|
func NotContainsKey[T any, K comparable](t *testing.T, expected K) func(actual map[K]T) E.Either[error, map[K]T] {
|
||||||
|
return func(actual map[K]T) E.Either[error, map[K]T] {
|
||||||
|
ok := assert.NotContains(t, actual, expected)
|
||||||
|
if ok {
|
||||||
|
return E.Of[error](actual)
|
||||||
|
}
|
||||||
|
return E.Left[map[K]T](errTest)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -115,16 +115,14 @@ func generateEitherize(f *os.File, i int) {
|
|||||||
fmt.Fprintf(f, "t%d T%d", j, j)
|
fmt.Fprintf(f, "t%d T%d", j, j)
|
||||||
}
|
}
|
||||||
fmt.Fprintf(f, ") Either[error, R] {\n")
|
fmt.Fprintf(f, ") Either[error, R] {\n")
|
||||||
fmt.Fprintf(f, " return TryCatchError(func() (R, error) {\n")
|
fmt.Fprintf(f, " return TryCatchError(f(")
|
||||||
fmt.Fprintf(f, " return f(")
|
|
||||||
for j := 0; j < i; j++ {
|
for j := 0; j < i; j++ {
|
||||||
if j > 0 {
|
if j > 0 {
|
||||||
fmt.Fprintf(f, ", ")
|
fmt.Fprintf(f, ", ")
|
||||||
}
|
}
|
||||||
fmt.Fprintf(f, "t%d", j)
|
fmt.Fprintf(f, "t%d", j)
|
||||||
}
|
}
|
||||||
fmt.Fprintln(f, ")")
|
fmt.Fprintln(f, "))")
|
||||||
fmt.Fprintln(f, " })")
|
|
||||||
fmt.Fprintln(f, " }")
|
fmt.Fprintln(f, " }")
|
||||||
fmt.Fprintln(f, "}")
|
fmt.Fprintln(f, "}")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,30 +22,28 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Const[E, A any] struct {
|
type Const[E, A any] struct {
|
||||||
Value E
|
value E
|
||||||
}
|
}
|
||||||
|
|
||||||
func Make[E, A any](e E) Const[E, A] {
|
func Make[E, A any](e E) Const[E, A] {
|
||||||
return Const[E, A]{Value: e}
|
return Const[E, A]{value: e}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Unwrap[E, A any](c Const[E, A]) E {
|
func Unwrap[E, A any](c Const[E, A]) E {
|
||||||
return c.Value
|
return c.value
|
||||||
}
|
}
|
||||||
|
|
||||||
func Of[E, A any](m M.Monoid[E]) func(A) Const[E, A] {
|
func Of[E, A any](m M.Monoid[E]) func(A) Const[E, A] {
|
||||||
return func(a A) Const[E, A] {
|
return F.Constant1[A](Make[E, A](m.Empty()))
|
||||||
return Make[E, A](m.Empty())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func MonadMap[E, A, B any](fa Const[E, A], f func(A) B) Const[E, B] {
|
func MonadMap[E, A, B any](fa Const[E, A], 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] {
|
func MonadAp[E, A, B any](s S.Semigroup[E]) func(fab Const[E, func(A) B], fa Const[E, A]) Const[E, B] {
|
||||||
return func(fab Const[E, func(A) B], fa Const[E, A]) Const[E, B] {
|
return func(fab Const[E, func(A) B], fa Const[E, A]) Const[E, B] {
|
||||||
return Make[E, B](s.Concat(fab.Value, fa.Value))
|
return Make[E, B](s.Concat(fab.value, fa.value))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,15 +19,14 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
|
|
||||||
E "github.com/IBM/fp-go/either"
|
E "github.com/IBM/fp-go/either"
|
||||||
ET "github.com/IBM/fp-go/either"
|
|
||||||
IOE "github.com/IBM/fp-go/ioeither/generic"
|
IOE "github.com/IBM/fp-go/ioeither/generic"
|
||||||
)
|
)
|
||||||
|
|
||||||
// withContext wraps an existing IOEither and performs a context check for cancellation before delegating
|
// WithContext wraps an existing IOEither and performs a context check for cancellation before delegating
|
||||||
func WithContext[GIO ~func() E.Either[error, A], A any](ctx context.Context, ma GIO) GIO {
|
func WithContext[GIO ~func() E.Either[error, A], A any](ctx context.Context, ma GIO) GIO {
|
||||||
return IOE.MakeIO[GIO](func() E.Either[error, A] {
|
return IOE.MakeIO[GIO](func() E.Either[error, A] {
|
||||||
if err := context.Cause(ctx); err != nil {
|
if err := context.Cause(ctx); err != nil {
|
||||||
return ET.Left[A](err)
|
return E.Left[A](err)
|
||||||
}
|
}
|
||||||
return ma()
|
return ma()
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,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)
|
|
||||||
}
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
// Copyright (c) 2023 IBM Corp.
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package reader
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
|
|
||||||
R "github.com/IBM/fp-go/reader/generic"
|
|
||||||
)
|
|
||||||
|
|
||||||
// these functions curry a golang function with the context as the firsr parameter into a either reader with the context as the last parameter
|
|
||||||
// this goes back to the advice in https://pkg.go.dev/context to put the context as a first parameter as a convention
|
|
||||||
|
|
||||||
func Curry0[A any](f func(context.Context) A) Reader[A] {
|
|
||||||
return R.Curry0[Reader[A]](f)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Curry1[T1, A any](f func(context.Context, T1) A) func(T1) Reader[A] {
|
|
||||||
return R.Curry1[Reader[A]](f)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Curry2[T1, T2, A any](f func(context.Context, T1, T2) A) func(T1) func(T2) Reader[A] {
|
|
||||||
return R.Curry2[Reader[A]](f)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Curry3[T1, T2, T3, A any](f func(context.Context, T1, T2, T3) A) func(T1) func(T2) func(T3) Reader[A] {
|
|
||||||
return R.Curry3[Reader[A]](f)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Uncurry1[T1, A any](f func(T1) Reader[A]) func(context.Context, T1) A {
|
|
||||||
return R.Uncurry1(f)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Uncurry2[T1, T2, A any](f func(T1) func(T2) Reader[A]) func(context.Context, T1, T2) A {
|
|
||||||
return R.Uncurry2(f)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Uncurry3[T1, T2, T3, A any](f func(T1) func(T2) func(T3) Reader[A]) func(context.Context, T1, T2, T3) A {
|
|
||||||
return R.Uncurry3(f)
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
// Copyright (c) 2023 IBM Corp.
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package reader
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
|
|
||||||
R "github.com/IBM/fp-go/reader/generic"
|
|
||||||
)
|
|
||||||
|
|
||||||
// these functions curry a golang function with the context as the firsr parameter into a either reader with the context as the last parameter
|
|
||||||
// this goes back to the advice in https://pkg.go.dev/context to put the context as a first parameter as a convention
|
|
||||||
|
|
||||||
func From0[A any](f func(context.Context) A) func() Reader[A] {
|
|
||||||
return R.From0[Reader[A]](f)
|
|
||||||
}
|
|
||||||
|
|
||||||
func From1[T1, A any](f func(context.Context, T1) A) func(T1) Reader[A] {
|
|
||||||
return R.From1[Reader[A]](f)
|
|
||||||
}
|
|
||||||
|
|
||||||
func From2[T1, T2, A any](f func(context.Context, T1, T2) A) func(T1, T2) Reader[A] {
|
|
||||||
return R.From2[Reader[A]](f)
|
|
||||||
}
|
|
||||||
|
|
||||||
func From3[T1, T2, T3, A any](f func(context.Context, T1, T2, T3) A) func(T1, T2, T3) Reader[A] {
|
|
||||||
return R.From3[Reader[A]](f)
|
|
||||||
}
|
|
||||||
@@ -1,66 +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)
|
|
||||||
}
|
|
||||||
|
|
||||||
func MonadFlap[B, A any](fab Reader[func(A) B], a A) Reader[B] {
|
|
||||||
return R.MonadFlap[Reader[func(A) B], Reader[B]](fab, a)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Flap[B, A any](a A) func(Reader[func(A) B]) Reader[B] {
|
|
||||||
return R.Flap[Reader[func(A) B], Reader[B]](a)
|
|
||||||
}
|
|
||||||
@@ -1,75 +0,0 @@
|
|||||||
// Copyright (c) 2023 IBM Corp.
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package reader
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
F "github.com/IBM/fp-go/function"
|
|
||||||
T "github.com/IBM/fp-go/tuple"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func GoFunction(ctx context.Context, data string) string {
|
|
||||||
return strings.ToUpper(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
func GoIntFunction(ctx context.Context, data string, number int) string {
|
|
||||||
return fmt.Sprintf("%s: %d", data, number)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestReaderFrom(t *testing.T) {
|
|
||||||
ctx := context.Background()
|
|
||||||
f := From1(GoFunction)
|
|
||||||
|
|
||||||
result := f("input")(ctx)
|
|
||||||
|
|
||||||
assert.Equal(t, result, "INPUT")
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func MyFinalResult(left, right string) string {
|
|
||||||
return fmt.Sprintf("%s-%s", left, right)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestReadersFrom(t *testing.T) {
|
|
||||||
ctx := context.Background()
|
|
||||||
|
|
||||||
f1 := From1(GoFunction)
|
|
||||||
f2 := From2(GoIntFunction)
|
|
||||||
|
|
||||||
result1 := f1("input")(ctx)
|
|
||||||
result2 := f2("input", 10)(ctx)
|
|
||||||
|
|
||||||
result3 := MyFinalResult(result1, result2)
|
|
||||||
|
|
||||||
h := F.Pipe1(
|
|
||||||
SequenceT2(f1("input"), f2("input", 10)),
|
|
||||||
Map(T.Tupled2(MyFinalResult)),
|
|
||||||
)
|
|
||||||
|
|
||||||
composedResult := h(ctx)
|
|
||||||
|
|
||||||
assert.Equal(t, result1, "INPUT")
|
|
||||||
assert.Equal(t, result2, "input: 10")
|
|
||||||
assert.Equal(t, result3, "INPUT-input: 10")
|
|
||||||
|
|
||||||
assert.Equal(t, composedResult, "INPUT-input: 10")
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
// Copyright (c) 2023 IBM Corp.
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package reader
|
|
||||||
|
|
||||||
import (
|
|
||||||
R "github.com/IBM/fp-go/reader/generic"
|
|
||||||
T "github.com/IBM/fp-go/tuple"
|
|
||||||
)
|
|
||||||
|
|
||||||
// SequenceT converts n inputs of higher kinded types into a higher kinded types of n strongly typed values, represented as a tuple
|
|
||||||
|
|
||||||
func SequenceT1[A any](a Reader[A]) Reader[T.Tuple1[A]] {
|
|
||||||
return R.SequenceT1[
|
|
||||||
Reader[A],
|
|
||||||
Reader[T.Tuple1[A]],
|
|
||||||
](a)
|
|
||||||
}
|
|
||||||
|
|
||||||
func SequenceT2[A, B any](a Reader[A], b Reader[B]) Reader[T.Tuple2[A, B]] {
|
|
||||||
return R.SequenceT2[
|
|
||||||
Reader[A],
|
|
||||||
Reader[B],
|
|
||||||
Reader[T.Tuple2[A, B]],
|
|
||||||
](a, b)
|
|
||||||
}
|
|
||||||
|
|
||||||
func SequenceT3[A, B, C any](a Reader[A], b Reader[B], c Reader[C]) Reader[T.Tuple3[A, B, C]] {
|
|
||||||
return R.SequenceT3[
|
|
||||||
Reader[A],
|
|
||||||
Reader[B],
|
|
||||||
Reader[C],
|
|
||||||
Reader[T.Tuple3[A, B, C]],
|
|
||||||
](a, b, c)
|
|
||||||
}
|
|
||||||
|
|
||||||
func SequenceT4[A, B, C, D any](a Reader[A], b Reader[B], c Reader[C], d Reader[D]) Reader[T.Tuple4[A, B, C, D]] {
|
|
||||||
return R.SequenceT4[
|
|
||||||
Reader[A],
|
|
||||||
Reader[B],
|
|
||||||
Reader[C],
|
|
||||||
Reader[D],
|
|
||||||
Reader[T.Tuple4[A, B, C, D]],
|
|
||||||
](a, b, c, d)
|
|
||||||
}
|
|
||||||
@@ -34,8 +34,6 @@ var (
|
|||||||
|
|
||||||
func command(name string, args []string, in []byte) RE.ReaderEither[exec.CommandOutput] {
|
func command(name string, args []string, in []byte) RE.ReaderEither[exec.CommandOutput] {
|
||||||
return func(ctx context.Context) E.Either[error, exec.CommandOutput] {
|
return func(ctx context.Context) E.Either[error, exec.CommandOutput] {
|
||||||
return E.TryCatchError(func() (exec.CommandOutput, error) {
|
return E.TryCatchError(GE.Exec(ctx, name, args, in))
|
||||||
return GE.Exec(ctx, name, args, in)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ package readereither
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
R "github.com/IBM/fp-go/context/reader"
|
|
||||||
ET "github.com/IBM/fp-go/either"
|
ET "github.com/IBM/fp-go/either"
|
||||||
O "github.com/IBM/fp-go/option"
|
O "github.com/IBM/fp-go/option"
|
||||||
RE "github.com/IBM/fp-go/readereither/generic"
|
RE "github.com/IBM/fp-go/readereither/generic"
|
||||||
@@ -32,14 +31,6 @@ func FromEither[A any](e ET.Either[error, A]) ReaderEither[A] {
|
|||||||
return RE.FromEither[ReaderEither[A]](e)
|
return RE.FromEither[ReaderEither[A]](e)
|
||||||
}
|
}
|
||||||
|
|
||||||
func RightReader[A any](r R.Reader[A]) ReaderEither[A] {
|
|
||||||
return RE.RightReader[R.Reader[A], ReaderEither[A]](r)
|
|
||||||
}
|
|
||||||
|
|
||||||
func LeftReader[A any](l R.Reader[error]) ReaderEither[A] {
|
|
||||||
return RE.LeftReader[R.Reader[error], ReaderEither[A]](l)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Left[A any](l error) ReaderEither[A] {
|
func Left[A any](l error) ReaderEither[A] {
|
||||||
return RE.Left[ReaderEither[A]](l)
|
return RE.Left[ReaderEither[A]](l)
|
||||||
}
|
}
|
||||||
@@ -48,10 +39,6 @@ func Right[A any](r A) ReaderEither[A] {
|
|||||||
return RE.Right[ReaderEither[A]](r)
|
return RE.Right[ReaderEither[A]](r)
|
||||||
}
|
}
|
||||||
|
|
||||||
func FromReader[A any](r R.Reader[A]) ReaderEither[A] {
|
|
||||||
return RE.FromReader[R.Reader[A], ReaderEither[A]](r)
|
|
||||||
}
|
|
||||||
|
|
||||||
func MonadMap[A, B any](fa ReaderEither[A], f func(A) B) ReaderEither[B] {
|
func MonadMap[A, B any](fa ReaderEither[A], f func(A) B) ReaderEither[B] {
|
||||||
return RE.MonadMap[ReaderEither[A], ReaderEither[B]](fa, f)
|
return RE.MonadMap[ReaderEither[A], ReaderEither[B]](fa, f)
|
||||||
}
|
}
|
||||||
@@ -84,30 +71,14 @@ func FromPredicate[A any](pred func(A) bool, onFalse func(A) error) func(A) Read
|
|||||||
return RE.FromPredicate[ReaderEither[A]](pred, onFalse)
|
return RE.FromPredicate[ReaderEither[A]](pred, onFalse)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Fold[A, B any](onLeft func(error) R.Reader[B], onRight func(A) R.Reader[B]) func(ReaderEither[A]) R.Reader[B] {
|
|
||||||
return RE.Fold[ReaderEither[A]](onLeft, onRight)
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetOrElse[A any](onLeft func(error) R.Reader[A]) func(ReaderEither[A]) R.Reader[A] {
|
|
||||||
return RE.GetOrElse[ReaderEither[A]](onLeft)
|
|
||||||
}
|
|
||||||
|
|
||||||
func OrElse[A any](onLeft func(error) ReaderEither[A]) func(ReaderEither[A]) ReaderEither[A] {
|
func OrElse[A any](onLeft func(error) ReaderEither[A]) func(ReaderEither[A]) ReaderEither[A] {
|
||||||
return RE.OrElse[ReaderEither[A]](onLeft)
|
return RE.OrElse[ReaderEither[A]](onLeft)
|
||||||
}
|
}
|
||||||
|
|
||||||
func OrLeft[A any](onLeft func(error) R.Reader[error]) func(ReaderEither[A]) ReaderEither[A] {
|
|
||||||
return RE.OrLeft[ReaderEither[A], ReaderEither[A]](onLeft)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Ask() ReaderEither[context.Context] {
|
func Ask() ReaderEither[context.Context] {
|
||||||
return RE.Ask[ReaderEither[context.Context]]()
|
return RE.Ask[ReaderEither[context.Context]]()
|
||||||
}
|
}
|
||||||
|
|
||||||
func Asks[A any](r R.Reader[A]) ReaderEither[A] {
|
|
||||||
return RE.Asks[R.Reader[A], ReaderEither[A]](r)
|
|
||||||
}
|
|
||||||
|
|
||||||
func MonadChainEitherK[A, B any](ma ReaderEither[A], f func(A) ET.Either[error, B]) ReaderEither[B] {
|
func MonadChainEitherK[A, B any](ma ReaderEither[A], f func(A) ET.Either[error, B]) ReaderEither[B] {
|
||||||
return RE.MonadChainEitherK[ReaderEither[A], ReaderEither[B]](ma, f)
|
return RE.MonadChainEitherK[ReaderEither[A], ReaderEither[B]](ma, f)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
// Copyright (c) 2023 IBM Corp.
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package readerio
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
|
|
||||||
IO "github.com/IBM/fp-go/io"
|
|
||||||
R "github.com/IBM/fp-go/readerio/generic"
|
|
||||||
)
|
|
||||||
|
|
||||||
// these functions curry a golang function with the context as the firsr parameter into a either reader with the context as the last parameter
|
|
||||||
// this goes back to the advice in https://pkg.go.dev/context to put the context as a first parameter as a convention
|
|
||||||
|
|
||||||
func From0[A any](f func(context.Context) IO.IO[A]) func() ReaderIO[A] {
|
|
||||||
return R.From0[ReaderIO[A]](f)
|
|
||||||
}
|
|
||||||
|
|
||||||
func From1[T1, A any](f func(context.Context, T1) IO.IO[A]) func(T1) ReaderIO[A] {
|
|
||||||
return R.From1[ReaderIO[A]](f)
|
|
||||||
}
|
|
||||||
|
|
||||||
func From2[T1, T2, A any](f func(context.Context, T1, T2) IO.IO[A]) func(T1, T2) ReaderIO[A] {
|
|
||||||
return R.From2[ReaderIO[A]](f)
|
|
||||||
}
|
|
||||||
|
|
||||||
func From3[T1, T2, T3, A any](f func(context.Context, T1, T2, T3) IO.IO[A]) func(T1, T2, T3) ReaderIO[A] {
|
|
||||||
return R.From3[ReaderIO[A]](f)
|
|
||||||
}
|
|
||||||
@@ -1,102 +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"
|
|
||||||
)
|
|
||||||
|
|
||||||
// FromIO converts an [IO.IO] to a [ReaderIO]
|
|
||||||
func FromIO[A any](a IO.IO[A]) ReaderIO[A] {
|
|
||||||
return R.FromIO[ReaderIO[A]](a)
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
func MonadFlap[B, A any](fab ReaderIO[func(A) B], a A) ReaderIO[B] {
|
|
||||||
return R.MonadFlap[ReaderIO[func(A) B], ReaderIO[B]](fab, a)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Flap[B, A any](a A) func(ReaderIO[func(A) B]) ReaderIO[B] {
|
|
||||||
return R.Flap[ReaderIO[func(A) B], ReaderIO[B]](a)
|
|
||||||
}
|
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
// Copyright (c) 2023 IBM Corp.
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package readerio
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
F "github.com/IBM/fp-go/function"
|
|
||||||
IO "github.com/IBM/fp-go/io"
|
|
||||||
T "github.com/IBM/fp-go/tuple"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func GoFunction(ctx context.Context, data string) IO.IO[string] {
|
|
||||||
return func() string {
|
|
||||||
return strings.ToUpper(data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func GoIntFunction(ctx context.Context, data string, number int) IO.IO[string] {
|
|
||||||
return func() string {
|
|
||||||
return fmt.Sprintf("%s: %d", data, number)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestReaderFrom(t *testing.T) {
|
|
||||||
ctx := context.Background()
|
|
||||||
f := From1(GoFunction)
|
|
||||||
|
|
||||||
result := f("input")(ctx)
|
|
||||||
|
|
||||||
assert.Equal(t, result(), "INPUT")
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func MyFinalResult(left, right string) string {
|
|
||||||
return fmt.Sprintf("%s-%s", left, right)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestReadersFrom(t *testing.T) {
|
|
||||||
ctx := context.Background()
|
|
||||||
|
|
||||||
f1 := From1(GoFunction)
|
|
||||||
f2 := From2(GoIntFunction)
|
|
||||||
|
|
||||||
result1 := f1("input")(ctx)
|
|
||||||
result2 := f2("input", 10)(ctx)
|
|
||||||
|
|
||||||
result3 := MyFinalResult(result1(), result2())
|
|
||||||
|
|
||||||
h := F.Pipe1(
|
|
||||||
SequenceT2(f1("input"), f2("input", 10)),
|
|
||||||
Map(T.Tupled2(MyFinalResult)),
|
|
||||||
)
|
|
||||||
|
|
||||||
composedResult := h(ctx)
|
|
||||||
|
|
||||||
assert.Equal(t, result1(), "INPUT")
|
|
||||||
assert.Equal(t, result2(), "input: 10")
|
|
||||||
assert.Equal(t, result3, "INPUT-input: 10")
|
|
||||||
|
|
||||||
assert.Equal(t, composedResult(), "INPUT-input: 10")
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
// Copyright (c) 2023 IBM Corp.
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package readerio
|
|
||||||
|
|
||||||
import (
|
|
||||||
R "github.com/IBM/fp-go/readerio/generic"
|
|
||||||
T "github.com/IBM/fp-go/tuple"
|
|
||||||
)
|
|
||||||
|
|
||||||
// SequenceT converts n inputs of higher kinded types into a higher kinded types of n strongly typed values, represented as a tuple
|
|
||||||
|
|
||||||
func SequenceT1[A any](a ReaderIO[A]) ReaderIO[T.Tuple1[A]] {
|
|
||||||
return R.SequenceT1[
|
|
||||||
ReaderIO[A],
|
|
||||||
ReaderIO[T.Tuple1[A]],
|
|
||||||
](a)
|
|
||||||
}
|
|
||||||
|
|
||||||
func SequenceT2[A, B any](a ReaderIO[A], b ReaderIO[B]) ReaderIO[T.Tuple2[A, B]] {
|
|
||||||
return R.SequenceT2[
|
|
||||||
ReaderIO[A],
|
|
||||||
ReaderIO[B],
|
|
||||||
ReaderIO[T.Tuple2[A, B]],
|
|
||||||
](a, b)
|
|
||||||
}
|
|
||||||
|
|
||||||
func SequenceT3[A, B, C any](a ReaderIO[A], b ReaderIO[B], c ReaderIO[C]) ReaderIO[T.Tuple3[A, B, C]] {
|
|
||||||
return R.SequenceT3[
|
|
||||||
ReaderIO[A],
|
|
||||||
ReaderIO[B],
|
|
||||||
ReaderIO[C],
|
|
||||||
ReaderIO[T.Tuple3[A, B, C]],
|
|
||||||
](a, b, c)
|
|
||||||
}
|
|
||||||
|
|
||||||
func SequenceT4[A, B, C, D any](a ReaderIO[A], b ReaderIO[B], c ReaderIO[C], d ReaderIO[D]) ReaderIO[T.Tuple4[A, B, C, D]] {
|
|
||||||
return R.SequenceT4[
|
|
||||||
ReaderIO[A],
|
|
||||||
ReaderIO[B],
|
|
||||||
ReaderIO[C],
|
|
||||||
ReaderIO[D],
|
|
||||||
ReaderIO[T.Tuple4[A, B, C, D]],
|
|
||||||
](a, b, c, d)
|
|
||||||
}
|
|
||||||
@@ -19,9 +19,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
RIO "github.com/IBM/fp-go/context/readerio"
|
|
||||||
R "github.com/IBM/fp-go/context/readerioeither"
|
R "github.com/IBM/fp-go/context/readerioeither"
|
||||||
"github.com/IBM/fp-go/errors"
|
|
||||||
F "github.com/IBM/fp-go/function"
|
F "github.com/IBM/fp-go/function"
|
||||||
IO "github.com/IBM/fp-go/io"
|
IO "github.com/IBM/fp-go/io"
|
||||||
J "github.com/IBM/fp-go/json"
|
J "github.com/IBM/fp-go/json"
|
||||||
@@ -37,20 +35,17 @@ func getData(r RecordType) string {
|
|||||||
|
|
||||||
func ExampleReadFile() {
|
func ExampleReadFile() {
|
||||||
|
|
||||||
data := F.Pipe4(
|
data := F.Pipe3(
|
||||||
ReadFile("./data/file.json"),
|
ReadFile("./data/file.json"),
|
||||||
R.ChainEitherK(J.Unmarshal[RecordType]),
|
R.ChainEitherK(J.Unmarshal[RecordType]),
|
||||||
R.ChainFirstIOK(IO.Logf[RecordType]("Log: %v")),
|
R.ChainFirstIOK(IO.Logf[RecordType]("Log: %v")),
|
||||||
R.Map(getData),
|
R.Map(getData),
|
||||||
R.GetOrElse(F.Flow2(
|
|
||||||
errors.ToString,
|
|
||||||
RIO.Of[string],
|
|
||||||
)),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
result := data(context.Background())
|
result := data(context.Background())
|
||||||
|
|
||||||
fmt.Println(result())
|
fmt.Println(result())
|
||||||
|
|
||||||
// Output: Carsten
|
// Output:
|
||||||
|
// Right[<nil>, string](Carsten)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package readerioeither
|
|||||||
|
|
||||||
// Code generated by go generate; DO NOT EDIT.
|
// Code generated by go generate; DO NOT EDIT.
|
||||||
// This file was generated by robots at
|
// This file was generated by robots at
|
||||||
// 2023-09-12 13:44:14.1022311 +0200 CEST m=+0.017763401
|
// 2023-10-23 08:30:39.012572 +0200 CEST m=+0.008846101
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package generic
|
|||||||
|
|
||||||
// Code generated by go generate; DO NOT EDIT.
|
// Code generated by go generate; DO NOT EDIT.
|
||||||
// This file was generated by robots at
|
// This file was generated by robots at
|
||||||
// 2023-09-12 13:44:14.1036885 +0200 CEST m=+0.019220801
|
// 2023-10-23 08:30:39.012572 +0200 CEST m=+0.008846101
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|||||||
@@ -19,8 +19,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
R "github.com/IBM/fp-go/context/reader"
|
|
||||||
RIO "github.com/IBM/fp-go/context/readerio"
|
|
||||||
G "github.com/IBM/fp-go/context/readerioeither/generic"
|
G "github.com/IBM/fp-go/context/readerioeither/generic"
|
||||||
ET "github.com/IBM/fp-go/either"
|
ET "github.com/IBM/fp-go/either"
|
||||||
IO "github.com/IBM/fp-go/io"
|
IO "github.com/IBM/fp-go/io"
|
||||||
@@ -33,14 +31,6 @@ func FromEither[A any](e ET.Either[error, A]) ReaderIOEither[A] {
|
|||||||
return G.FromEither[ReaderIOEither[A]](e)
|
return G.FromEither[ReaderIOEither[A]](e)
|
||||||
}
|
}
|
||||||
|
|
||||||
func RightReader[A any](r R.Reader[A]) ReaderIOEither[A] {
|
|
||||||
return G.RightReader[ReaderIOEither[A]](r)
|
|
||||||
}
|
|
||||||
|
|
||||||
func LeftReader[A any](l R.Reader[error]) ReaderIOEither[A] {
|
|
||||||
return G.LeftReader[ReaderIOEither[A]](l)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Left[A any](l error) ReaderIOEither[A] {
|
func Left[A any](l error) ReaderIOEither[A] {
|
||||||
return G.Left[ReaderIOEither[A]](l)
|
return G.Left[ReaderIOEither[A]](l)
|
||||||
}
|
}
|
||||||
@@ -49,10 +39,6 @@ func Right[A any](r A) ReaderIOEither[A] {
|
|||||||
return G.Right[ReaderIOEither[A]](r)
|
return G.Right[ReaderIOEither[A]](r)
|
||||||
}
|
}
|
||||||
|
|
||||||
func FromReader[A any](r R.Reader[A]) ReaderIOEither[A] {
|
|
||||||
return G.FromReader[ReaderIOEither[A]](r)
|
|
||||||
}
|
|
||||||
|
|
||||||
func MonadMap[A, B any](fa ReaderIOEither[A], f func(A) B) ReaderIOEither[B] {
|
func MonadMap[A, B any](fa ReaderIOEither[A], f func(A) B) ReaderIOEither[B] {
|
||||||
return G.MonadMap[ReaderIOEither[A], ReaderIOEither[B]](fa, f)
|
return G.MonadMap[ReaderIOEither[A], ReaderIOEither[B]](fa, f)
|
||||||
}
|
}
|
||||||
@@ -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)
|
return G.FromPredicate[ReaderIOEither[A]](pred, onFalse)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Fold[A, B any](onLeft func(error) RIO.ReaderIO[B], onRight func(A) RIO.ReaderIO[B]) func(ReaderIOEither[A]) RIO.ReaderIO[B] {
|
|
||||||
return G.Fold[RIO.ReaderIO[B], ReaderIOEither[A]](onLeft, onRight)
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetOrElse[A any](onLeft func(error) RIO.ReaderIO[A]) func(ReaderIOEither[A]) RIO.ReaderIO[A] {
|
|
||||||
return G.GetOrElse[RIO.ReaderIO[A], ReaderIOEither[A]](onLeft)
|
|
||||||
}
|
|
||||||
|
|
||||||
func OrElse[A any](onLeft func(error) ReaderIOEither[A]) func(ReaderIOEither[A]) ReaderIOEither[A] {
|
func OrElse[A any](onLeft func(error) ReaderIOEither[A]) func(ReaderIOEither[A]) ReaderIOEither[A] {
|
||||||
return G.OrElse[ReaderIOEither[A]](onLeft)
|
return G.OrElse[ReaderIOEither[A]](onLeft)
|
||||||
}
|
}
|
||||||
|
|
||||||
func OrLeft[A any](onLeft func(error) RIO.ReaderIO[error]) func(ReaderIOEither[A]) ReaderIOEither[A] {
|
|
||||||
return G.OrLeft[ReaderIOEither[A], RIO.ReaderIO[error]](onLeft)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Ask() ReaderIOEither[context.Context] {
|
func Ask() ReaderIOEither[context.Context] {
|
||||||
return G.Ask[ReaderIOEither[context.Context]]()
|
return G.Ask[ReaderIOEither[context.Context]]()
|
||||||
}
|
}
|
||||||
|
|
||||||
func Asks[A any](r R.Reader[A]) ReaderIOEither[A] {
|
|
||||||
return G.Asks[ReaderIOEither[A]](r)
|
|
||||||
}
|
|
||||||
|
|
||||||
func MonadChainEitherK[A, B any](ma ReaderIOEither[A], f func(A) ET.Either[error, B]) ReaderIOEither[B] {
|
func MonadChainEitherK[A, B any](ma ReaderIOEither[A], f func(A) ET.Either[error, B]) ReaderIOEither[B] {
|
||||||
return G.MonadChainEitherK[ReaderIOEither[A], ReaderIOEither[B]](ma, f)
|
return G.MonadChainEitherK[ReaderIOEither[A], ReaderIOEither[B]](ma, f)
|
||||||
}
|
}
|
||||||
@@ -164,14 +134,6 @@ func Never[A any]() ReaderIOEither[A] {
|
|||||||
return G.Never[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] {
|
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)
|
return G.MonadChainIOK[ReaderIOEither[B], ReaderIOEither[A]](ma, f)
|
||||||
}
|
}
|
||||||
@@ -235,22 +197,6 @@ func Flatten[
|
|||||||
return G.Flatten[ReaderIOEither[ReaderIOEither[A]]](rdr)
|
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 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 MonadFlap[B, A any](fab ReaderIOEither[func(A) B], a A) ReaderIOEither[B] {
|
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)
|
return G.MonadFlap[ReaderIOEither[func(A) B], ReaderIOEither[B]](fab, a)
|
||||||
}
|
}
|
||||||
|
|||||||
168
di/erasure/injector.go
Normal file
168
di/erasure/injector.go
Normal 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
174
di/erasure/provider.go
Normal 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),
|
||||||
|
IOE.ToIOOption[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
45
di/erasure/token.go
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package erasure
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
O "github.com/IBM/fp-go/option"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
BehaviourMask = 0x0f
|
||||||
|
Identity = 0 // required dependency
|
||||||
|
Option = 1 // optional dependency
|
||||||
|
IOEither = 2 // lazy and required
|
||||||
|
IOOption = 3 // lazy and optional
|
||||||
|
|
||||||
|
TypeMask = 0xf0
|
||||||
|
Multi = 1 << 4 // array of implementations
|
||||||
|
Item = 2 << 4 // item of a multi token
|
||||||
|
)
|
||||||
|
|
||||||
|
// Dependency describes the relationship to a service
|
||||||
|
type Dependency interface {
|
||||||
|
fmt.Stringer
|
||||||
|
// Id returns a unique identifier for a token that can be used as a cache key
|
||||||
|
Id() string
|
||||||
|
// Flag returns a tag that identifies the behaviour of the dependency
|
||||||
|
Flag() int
|
||||||
|
// ProviderFactory optionally returns an attached [ProviderFactory] that represents the default for this dependency
|
||||||
|
ProviderFactory() O.Option[ProviderFactory]
|
||||||
|
}
|
||||||
33
di/injector.go
Normal file
33
di/injector.go
Normal 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
166
di/provider.go
Normal 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
351
di/provider_test.go
Normal 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
196
di/token.go
Normal 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
76
di/utils.go
Normal 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
83
di/utils_test.go
Normal 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")))))
|
||||||
|
}
|
||||||
@@ -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 {
|
func IsLeft[E, A any](val Either[E, A]) bool {
|
||||||
return val.isLeft
|
return val.isLeft
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsLeft tests if the either is a right value. Rather use [Fold] if you need to access the values. Inverse is [IsLeft].
|
// IsLeft tests if the [Either] is a right value. Rather use [Fold] if you need to access the values. Inverse is [IsLeft].
|
||||||
func IsRight[E, A any](val Either[E, A]) bool {
|
func IsRight[E, A any](val Either[E, A]) bool {
|
||||||
return !val.isLeft
|
return !val.isLeft
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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]])
|
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] {
|
func TryCatch[FE func(error) E, E, A any](val A, err error, onThrow FE) Either[E, A] {
|
||||||
val, err := f()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return F.Pipe2(err, onThrow, Left[A, E])
|
return F.Pipe2(err, onThrow, Left[A, E])
|
||||||
}
|
}
|
||||||
return F.Pipe1(val, Right[E, A])
|
return F.Pipe1(val, Right[E, A])
|
||||||
}
|
}
|
||||||
|
|
||||||
func TryCatchError[F ~func() (A, error), A any](f F) Either[error, A] {
|
func TryCatchError[A any](val A, err error) Either[error, A] {
|
||||||
return TryCatch(f, E.IdentityError)
|
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] {
|
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] {
|
func FromError[A any](f func(a A) error) func(A) Either[error, A] {
|
||||||
return func(a A) Either[error, A] {
|
return func(a A) Either[error, A] {
|
||||||
return TryCatchError(func() (A, error) {
|
return TryCatchError(a, f(a))
|
||||||
return a, f(a)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,5 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func command(name string, args []string, in []byte) E.Either[error, exec.CommandOutput] {
|
func command(name string, args []string, in []byte) E.Either[error, exec.CommandOutput] {
|
||||||
return E.TryCatchError(func() (exec.CommandOutput, error) {
|
return E.TryCatchError(GE.Exec(context.Background(), name, args, in))
|
||||||
return GE.Exec(context.Background(), name, args, in)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Code generated by go generate; DO NOT EDIT.
|
// Code generated by go generate; DO NOT EDIT.
|
||||||
// This file was generated by robots at
|
// This file was generated by robots at
|
||||||
// 2023-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
|
package either
|
||||||
|
|
||||||
@@ -13,9 +13,7 @@ import (
|
|||||||
// The inverse function is [Uneitherize0]
|
// The inverse function is [Uneitherize0]
|
||||||
func Eitherize0[F ~func() (R, error), R any](f F) func() Either[error, R] {
|
func Eitherize0[F ~func() (R, error), R any](f F) func() Either[error, R] {
|
||||||
return func() Either[error, R] {
|
return func() Either[error, R] {
|
||||||
return TryCatchError(func() (R, error) {
|
return TryCatchError(f())
|
||||||
return f()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31,9 +29,7 @@ func Uneitherize0[F ~func() Either[error, R], R any](f F) func() (R, error) {
|
|||||||
// The inverse function is [Uneitherize1]
|
// The inverse function is [Uneitherize1]
|
||||||
func Eitherize1[F ~func(T0) (R, error), T0, R any](f F) func(T0) Either[error, R] {
|
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 func(t0 T0) Either[error, R] {
|
||||||
return TryCatchError(func() (R, error) {
|
return TryCatchError(f(t0))
|
||||||
return 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]
|
// 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] {
|
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 func(t0 T0, t1 T1) Either[error, R] {
|
||||||
return TryCatchError(func() (R, error) {
|
return TryCatchError(f(t0, t1))
|
||||||
return 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]
|
// 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] {
|
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 func(t0 T0, t1 T1, t2 T2) Either[error, R] {
|
||||||
return TryCatchError(func() (R, error) {
|
return TryCatchError(f(t0, t1, t2))
|
||||||
return 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]
|
// 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] {
|
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 func(t0 T0, t1 T1, t2 T2, t3 T3) Either[error, R] {
|
||||||
return TryCatchError(func() (R, error) {
|
return TryCatchError(f(t0, t1, t2, t3))
|
||||||
return 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]
|
// 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] {
|
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 func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4) Either[error, R] {
|
||||||
return TryCatchError(func() (R, error) {
|
return TryCatchError(f(t0, t1, t2, t3, t4))
|
||||||
return 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]
|
// 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] {
|
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 func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5) Either[error, R] {
|
||||||
return TryCatchError(func() (R, error) {
|
return TryCatchError(f(t0, t1, t2, t3, t4, t5))
|
||||||
return 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]
|
// 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] {
|
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 func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6) Either[error, R] {
|
||||||
return TryCatchError(func() (R, error) {
|
return TryCatchError(f(t0, t1, t2, t3, t4, t5, t6))
|
||||||
return 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]
|
// 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] {
|
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 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 TryCatchError(f(t0, t1, t2, t3, t4, t5, t6, t7))
|
||||||
return 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]
|
// 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] {
|
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 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 TryCatchError(f(t0, t1, t2, t3, t4, t5, t6, t7, t8))
|
||||||
return 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]
|
// 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] {
|
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 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 TryCatchError(f(t0, t1, t2, t3, t4, t5, t6, t7, t8, t9))
|
||||||
return f(t0, t1, t2, t3, t4, t5, t6, t7, t8, t9)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,17 +35,13 @@ var (
|
|||||||
func bodyRequest(method string) func(string) func([]byte) E.Either[error, *http.Request] {
|
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(url string) func([]byte) E.Either[error, *http.Request] {
|
||||||
return func(body []byte) E.Either[error, *http.Request] {
|
return func(body []byte) E.Either[error, *http.Request] {
|
||||||
return E.TryCatchError(func() (*http.Request, error) {
|
return E.TryCatchError(http.NewRequest(method, url, bytes.NewReader(body)))
|
||||||
return http.NewRequest(method, url, bytes.NewReader(body))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func noBodyRequest(method string) func(string) E.Either[error, *http.Request] {
|
func noBodyRequest(method string) func(string) E.Either[error, *http.Request] {
|
||||||
return func(url string) E.Either[error, *http.Request] {
|
return func(url string) E.Either[error, *http.Request] {
|
||||||
return E.TryCatchError(func() (*http.Request, error) {
|
return E.TryCatchError(http.NewRequest(method, url, nil))
|
||||||
return http.NewRequest(method, url, nil)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,20 +25,14 @@ import (
|
|||||||
|
|
||||||
func TestWithResource(t *testing.T) {
|
func TestWithResource(t *testing.T) {
|
||||||
onCreate := func() Either[error, *os.File] {
|
onCreate := func() Either[error, *os.File] {
|
||||||
return TryCatchError(func() (*os.File, error) {
|
return TryCatchError(os.CreateTemp("", "*"))
|
||||||
return os.CreateTemp("", "*")
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
onDelete := F.Flow2(
|
onDelete := F.Flow2(
|
||||||
func(f *os.File) Either[error, string] {
|
func(f *os.File) Either[error, string] {
|
||||||
return TryCatchError(func() (string, error) {
|
return TryCatchError(f.Name(), f.Close())
|
||||||
return f.Name(), f.Close()
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
Chain(func(name string) Either[error, any] {
|
Chain(func(name string) Either[error, any] {
|
||||||
return TryCatchError(func() (any, error) {
|
return TryCatchError(any(name), os.Remove(name))
|
||||||
return name, os.Remove(name)
|
|
||||||
})
|
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -17,80 +17,60 @@ package either
|
|||||||
|
|
||||||
func Variadic0[V, R any](f func([]V) (R, error)) func(...V) Either[error, R] {
|
func Variadic0[V, R any](f func([]V) (R, error)) func(...V) Either[error, R] {
|
||||||
return func(v ...V) Either[error, R] {
|
return func(v ...V) Either[error, R] {
|
||||||
return TryCatchError(func() (R, error) {
|
return TryCatchError(f(v))
|
||||||
return f(v)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Variadic1[T1, V, R any](f func(T1, []V) (R, error)) func(T1, ...V) Either[error, R] {
|
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 func(t1 T1, v ...V) Either[error, R] {
|
||||||
return TryCatchError(func() (R, error) {
|
return TryCatchError(f(t1, v))
|
||||||
return f(t1, v)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Variadic2[T1, T2, V, R any](f func(T1, T2, []V) (R, error)) func(T1, T2, ...V) Either[error, R] {
|
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 func(t1 T1, t2 T2, v ...V) Either[error, R] {
|
||||||
return TryCatchError(func() (R, error) {
|
return TryCatchError(f(t1, t2, v))
|
||||||
return 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] {
|
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 func(t1 T1, t2 T2, t3 T3, v ...V) Either[error, R] {
|
||||||
return TryCatchError(func() (R, error) {
|
return TryCatchError(f(t1, t2, t3, v))
|
||||||
return 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] {
|
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 func(t1 T1, t2 T2, t3 T3, t4 T4, v ...V) Either[error, R] {
|
||||||
return TryCatchError(func() (R, error) {
|
return TryCatchError(f(t1, t2, t3, t4, v))
|
||||||
return f(t1, t2, t3, t4, v)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Unvariadic0[V, R any](f func(...V) (R, error)) func([]V) Either[error, R] {
|
func Unvariadic0[V, R any](f func(...V) (R, error)) func([]V) Either[error, R] {
|
||||||
return func(v []V) Either[error, R] {
|
return func(v []V) Either[error, R] {
|
||||||
return TryCatchError(func() (R, error) {
|
return TryCatchError(f(v...))
|
||||||
return f(v...)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Unvariadic1[T1, V, R any](f func(T1, ...V) (R, error)) func(T1, []V) Either[error, R] {
|
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 func(t1 T1, v []V) Either[error, R] {
|
||||||
return TryCatchError(func() (R, error) {
|
return TryCatchError(f(t1, v...))
|
||||||
return f(t1, v...)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Unvariadic2[T1, T2, V, R any](f func(T1, T2, ...V) (R, error)) func(T1, T2, []V) Either[error, R] {
|
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 func(t1 T1, t2 T2, v []V) Either[error, R] {
|
||||||
return TryCatchError(func() (R, error) {
|
return TryCatchError(f(t1, t2, v...))
|
||||||
return 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] {
|
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 func(t1 T1, t2 T2, t3 T3, v []V) Either[error, R] {
|
||||||
return TryCatchError(func() (R, error) {
|
return TryCatchError(f(t1, t2, t3, v...))
|
||||||
return 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] {
|
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 func(t1 T1, t2 T2, t3 T3, t4 T4, v []V) Either[error, R] {
|
||||||
return TryCatchError(func() (R, error) {
|
return TryCatchError(f(t1, t2, t3, t4, v...))
|
||||||
return f(t1, t2, t3, t4, v...)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,8 @@
|
|||||||
package erasure
|
package erasure
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
E "github.com/IBM/fp-go/either"
|
||||||
|
"github.com/IBM/fp-go/errors"
|
||||||
F "github.com/IBM/fp-go/function"
|
F "github.com/IBM/fp-go/function"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -29,6 +31,15 @@ func Unerase[T any](t any) T {
|
|||||||
return *t.(*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
|
// Erase0 converts a type safe function into an erased function
|
||||||
func Erase0[T1 any](f func() T1) func() any {
|
func Erase0[T1 any](f func() T1) func() any {
|
||||||
return F.Nullary2(f, Erase[T1])
|
return F.Nullary2(f, Erase[T1])
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Code generated by go generate; DO NOT EDIT.
|
// Code generated by go generate; DO NOT EDIT.
|
||||||
// This file was generated by robots at
|
// This file was generated by robots at
|
||||||
// 2023-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
|
package function
|
||||||
|
|
||||||
|
|||||||
25
function/flip.go
Normal file
25
function/flip.go
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,14 +13,24 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
// Package readerio implements a specialization of the ReaderIO monad assuming a golang context as the context of the monad
|
package function
|
||||||
package readerio
|
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
R "github.com/IBM/fp-go/readerio"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ReaderIO is a specialization of the ReaderIO monad assuming a golang context as the context of the monad
|
func TestFlip(t *testing.T) {
|
||||||
type ReaderIO[A any] R.ReaderIO[context.Context, A]
|
|
||||||
|
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"))
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
// Code generated by go generate; DO NOT EDIT.
|
// Code generated by go generate; DO NOT EDIT.
|
||||||
// This file was generated by robots at
|
// This file was generated by robots at
|
||||||
// 2023-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
|
package function
|
||||||
|
|
||||||
|
|||||||
28
function/generic/switch.go
Normal file
28
function/generic/switch.go
Normal 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
26
function/switch.go
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package 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
2
go.mod
@@ -8,7 +8,7 @@ require (
|
|||||||
)
|
)
|
||||||
|
|
||||||
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/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||||
|
|||||||
4
go.sum
4
go.sum
@@ -1,5 +1,5 @@
|
|||||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
|
github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
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=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
|||||||
@@ -68,10 +68,8 @@ const (
|
|||||||
|
|
||||||
// ParseMediaType parses a media type into a tuple
|
// ParseMediaType parses a media type into a tuple
|
||||||
func ParseMediaType(mediaType string) E.Either[error, ParsedMediaType] {
|
func ParseMediaType(mediaType string) E.Either[error, ParsedMediaType] {
|
||||||
return E.TryCatchError(func() (ParsedMediaType, error) {
|
m, p, err := mime.ParseMediaType(mediaType)
|
||||||
m, p, err := mime.ParseMediaType(mediaType)
|
return E.TryCatchError(T.MakeTuple2(m, p), err)
|
||||||
return T.MakeTuple2(m, p), err
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetHeader(resp *H.Response) H.Header {
|
func GetHeader(resp *H.Response) H.Header {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Code generated by go generate; DO NOT EDIT.
|
// Code generated by go generate; DO NOT EDIT.
|
||||||
// This file was generated by robots at
|
// This file was generated by robots at
|
||||||
// 2023-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
|
package identity
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
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)
|
return F.Bind2nd(MonadFlap[GAB, A, B], a)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Code generated by go generate; DO NOT EDIT.
|
// Code generated by go generate; DO NOT EDIT.
|
||||||
// This file was generated by robots at
|
// This file was generated by robots at
|
||||||
// 2023-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
|
package apply
|
||||||
|
|
||||||
|
|||||||
@@ -51,6 +51,24 @@ func ReduceWithIndex[GA ~[]A, A, B any](fa GA, f func(int, B, A) B, initial B) B
|
|||||||
return current
|
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 {
|
func Append[GA ~[]A, A any](as GA, a A) GA {
|
||||||
return append(as, a)
|
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
|
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 {
|
func ConstNil[GA ~[]A, A any]() GA {
|
||||||
return (GA)(nil)
|
return (GA)(nil)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
// 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] {
|
func ReadAll(ctx context.Context, rdr io.Reader) E.Either[error, []byte] {
|
||||||
return E.TryCatchError(func() ([]byte, error) {
|
var buffer bytes.Buffer
|
||||||
var buffer bytes.Buffer
|
_, err := io.Copy(&buffer, MakeReader(ctx, rdr))
|
||||||
_, err := io.Copy(&buffer, MakeReader(ctx, rdr))
|
return E.TryCatchError(buffer.Bytes(), err)
|
||||||
return buffer.Bytes(), err
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Code generated by go generate; DO NOT EDIT.
|
// Code generated by go generate; DO NOT EDIT.
|
||||||
// This file was generated by robots at
|
// This file was generated by robots at
|
||||||
// 2023-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
|
package io
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Code generated by go generate; DO NOT EDIT.
|
// Code generated by go generate; DO NOT EDIT.
|
||||||
// This file was generated by robots at
|
// This file was generated by robots at
|
||||||
// 2023-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
|
package generic
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
@@ -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
|
// 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](
|
return RR.MonadTraverse[MA](
|
||||||
Of[GBS, MB],
|
Of[GBS, MB],
|
||||||
Map[GBS, func() func(B) MB, MB, func(B) 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
|
// 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](
|
return RR.Traverse[MA](
|
||||||
Of[GBS, MB],
|
Of[GBS, MB],
|
||||||
Map[GBS, func() func(B) MB, MB, func(B) 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 {
|
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])
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,10 +19,9 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
|
|
||||||
G "github.com/IBM/fp-go/io/generic"
|
G "github.com/IBM/fp-go/io/generic"
|
||||||
L "github.com/IBM/fp-go/lazy"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// WithLock executes the provided [IO] operation in the scope of a lock
|
// WithLock executes the provided [IO] operation in the scope of a lock
|
||||||
func WithLock[A any](lock L.Lazy[context.CancelFunc]) func(fa IO[A]) IO[A] {
|
func WithLock[A any](lock IO[context.CancelFunc]) func(fa IO[A]) IO[A] {
|
||||||
return G.WithLock[IO[A]](lock)
|
return G.WithLock[IO[A]](lock)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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] {
|
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
|
// TraverseRecord applies a function returning an [IO] to all elements in a record and the
|
||||||
// transforms this into an [IO] of that record
|
// 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] {
|
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
|
// TraverseRecordWithIndex applies a function returning an [IO] to all elements in a record and the
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Code generated by go generate; DO NOT EDIT.
|
// Code generated by go generate; DO NOT EDIT.
|
||||||
// This file was generated by robots at
|
// This file was generated by robots at
|
||||||
// 2023-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
|
package ioeither
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Code generated by go generate; DO NOT EDIT.
|
// Code generated by go generate; DO NOT EDIT.
|
||||||
// This file was generated by robots at
|
// This file was generated by robots at
|
||||||
// 2023-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
|
package generic
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
ET "github.com/IBM/fp-go/either"
|
ET "github.com/IBM/fp-go/either"
|
||||||
"github.com/IBM/fp-go/errors"
|
|
||||||
F "github.com/IBM/fp-go/function"
|
F "github.com/IBM/fp-go/function"
|
||||||
C "github.com/IBM/fp-go/internal/chain"
|
C "github.com/IBM/fp-go/internal/chain"
|
||||||
"github.com/IBM/fp-go/internal/eithert"
|
"github.com/IBM/fp-go/internal/eithert"
|
||||||
@@ -184,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 {
|
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 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 {
|
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
|
// Memoize computes the value of the provided IO monad lazily but exactly once
|
||||||
@@ -273,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
|
// 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 {
|
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])
|
return MonadFold(val, Right[GAE], Left[GAE])
|
||||||
@@ -313,3 +336,14 @@ func MonadFlap[GEAB ~func() ET.Either[E, func(A) B], GEB ~func() ET.Either[E, B]
|
|||||||
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 {
|
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)
|
return FC.Flap(MonadMap[GEAB, GEB], a)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ToIOOption[GA ~func() O.Option[A], GEA ~func() ET.Either[E, A], E, A any](ioe GEA) GA {
|
||||||
|
return F.Pipe1(
|
||||||
|
ioe,
|
||||||
|
IO.Map[GEA, GA](ET.ToOption[E, A]),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func FromIOOption[GEA ~func() ET.Either[E, A], GA ~func() O.Option[A], E, A any](onNone func() E) func(ioo GA) GEA {
|
||||||
|
return IO.Map[GA, GEA](ET.FromOption[A](onNone))
|
||||||
|
}
|
||||||
|
|||||||
@@ -20,54 +20,94 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
B "github.com/IBM/fp-go/bytes"
|
B "github.com/IBM/fp-go/bytes"
|
||||||
ER "github.com/IBM/fp-go/errors"
|
|
||||||
F "github.com/IBM/fp-go/function"
|
F "github.com/IBM/fp-go/function"
|
||||||
|
H "github.com/IBM/fp-go/http"
|
||||||
IOE "github.com/IBM/fp-go/ioeither"
|
IOE "github.com/IBM/fp-go/ioeither"
|
||||||
IOEF "github.com/IBM/fp-go/ioeither/file"
|
IOEF "github.com/IBM/fp-go/ioeither/file"
|
||||||
J "github.com/IBM/fp-go/json"
|
J "github.com/IBM/fp-go/json"
|
||||||
|
T "github.com/IBM/fp-go/tuple"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Client interface {
|
type (
|
||||||
Do(req *http.Request) IOE.IOEither[error, *http.Response]
|
// Requester is a reader that constructs a request
|
||||||
}
|
Requester = IOE.IOEither[error, *http.Request]
|
||||||
|
|
||||||
type client struct {
|
Client interface {
|
||||||
delegate *http.Client
|
Do(Requester) IOE.IOEither[error, *http.Response]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (client client) Do(req *http.Request) IOE.IOEither[error, *http.Response] {
|
client struct {
|
||||||
return IOE.TryCatch(func() (*http.Response, error) {
|
delegate *http.Client
|
||||||
return client.delegate.Do(req)
|
doIOE func(*http.Request) IOE.IOEither[error, *http.Response]
|
||||||
}, ER.IdentityError)
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
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 {
|
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] {
|
// ReadFullResponse sends a request, reads the response as a byte array and represents the result as a tuple
|
||||||
return func(req *http.Request) IOE.IOEither[error, []byte] {
|
func ReadFullResponse(client Client) func(Requester) IOE.IOEither[error, H.FullResponse] {
|
||||||
return IOEF.ReadAll(F.Pipe2(
|
return F.Flow3(
|
||||||
req,
|
client.Do,
|
||||||
client.Do,
|
IOE.ChainEitherK(H.ValidateResponse),
|
||||||
IOE.Map[error](func(resp *http.Response) io.ReadCloser {
|
IOE.Chain(func(resp *http.Response) IOE.IOEither[error, H.FullResponse] {
|
||||||
return resp.Body
|
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(
|
return F.Flow2(
|
||||||
ReadAll(client),
|
ReadAll(client),
|
||||||
IOE.Map[error](B.ToString),
|
IOE.Map[error](B.ToString),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ReadJson[A any](client Client) func(*http.Request) IOE.IOEither[error, A] {
|
// ReadJson sends a request, reads the response and parses the response as JSON
|
||||||
return F.Flow2(
|
func ReadJson[A any](client Client) func(Requester) IOE.IOEither[error, A] {
|
||||||
ReadAll(client),
|
return F.Flow3(
|
||||||
IOE.ChainEitherK(J.Unmarshal[A]),
|
ReadFullResponse(client),
|
||||||
|
IOE.ChainFirstEitherK(F.Flow2(
|
||||||
|
H.Response,
|
||||||
|
H.ValidateJsonResponse,
|
||||||
|
)),
|
||||||
|
IOE.ChainEitherK(F.Flow2(
|
||||||
|
H.Body,
|
||||||
|
J.Unmarshal[A],
|
||||||
|
)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ import (
|
|||||||
|
|
||||||
AR "github.com/IBM/fp-go/array"
|
AR "github.com/IBM/fp-go/array"
|
||||||
E "github.com/IBM/fp-go/either"
|
E "github.com/IBM/fp-go/either"
|
||||||
HE "github.com/IBM/fp-go/either/http"
|
|
||||||
"github.com/IBM/fp-go/errors"
|
"github.com/IBM/fp-go/errors"
|
||||||
F "github.com/IBM/fp-go/function"
|
F "github.com/IBM/fp-go/function"
|
||||||
IOE "github.com/IBM/fp-go/ioeither"
|
IOE "github.com/IBM/fp-go/ioeither"
|
||||||
@@ -53,17 +52,16 @@ func TestRetryHttp(t *testing.T) {
|
|||||||
client := MakeClient(&http.Client{})
|
client := MakeClient(&http.Client{})
|
||||||
|
|
||||||
action := func(status R.RetryStatus) IOE.IOEither[error, *PostItem] {
|
action := func(status R.RetryStatus) IOE.IOEither[error, *PostItem] {
|
||||||
return F.Pipe2(
|
return F.Pipe1(
|
||||||
HE.GetRequest(urls[status.IterNumber]),
|
MakeGetRequest(urls[status.IterNumber]),
|
||||||
IOE.FromEither[error, *http.Request],
|
ReadJson[*PostItem](client),
|
||||||
IOE.Chain(ReadJson[*PostItem](client)),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
check := E.Fold(
|
check := E.Fold(
|
||||||
F.Flow2(
|
F.Flow2(
|
||||||
errors.As[*net.DNSError](),
|
errors.As[*net.DNSError](),
|
||||||
O.Fold(F.Constant(false), F.Constant1[*net.DNSError](true)),
|
O.IsSome[*net.DNSError],
|
||||||
),
|
),
|
||||||
F.Constant1[*PostItem](false),
|
F.Constant1[*PostItem](false),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import (
|
|||||||
ET "github.com/IBM/fp-go/either"
|
ET "github.com/IBM/fp-go/either"
|
||||||
I "github.com/IBM/fp-go/io"
|
I "github.com/IBM/fp-go/io"
|
||||||
G "github.com/IBM/fp-go/ioeither/generic"
|
G "github.com/IBM/fp-go/ioeither/generic"
|
||||||
|
IOO "github.com/IBM/fp-go/iooption"
|
||||||
L "github.com/IBM/fp-go/lazy"
|
L "github.com/IBM/fp-go/lazy"
|
||||||
O "github.com/IBM/fp-go/option"
|
O "github.com/IBM/fp-go/option"
|
||||||
)
|
)
|
||||||
@@ -63,6 +64,10 @@ func FromOption[A, E any](onNone func() E) func(o O.Option[A]) IOEither[E, A] {
|
|||||||
return G.FromOption[IOEither[E, A]](onNone)
|
return G.FromOption[IOEither[E, A]](onNone)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func FromIOOption[A, E any](onNone func() E) func(o IOO.IOOption[A]) IOEither[E, A] {
|
||||||
|
return G.FromIOOption[IOEither[E, A], IOO.IOOption[A]](onNone)
|
||||||
|
}
|
||||||
|
|
||||||
func ChainOptionK[A, B, E any](onNone func() E) func(func(A) O.Option[B]) func(IOEither[E, A]) IOEither[E, B] {
|
func ChainOptionK[A, B, E any](onNone func() E) func(func(A) O.Option[B]) func(IOEither[E, A]) IOEither[E, B] {
|
||||||
return G.ChainOptionK[IOEither[E, A], IOEither[E, B]](onNone)
|
return G.ChainOptionK[IOEither[E, A], IOEither[E, B]](onNone)
|
||||||
}
|
}
|
||||||
@@ -125,10 +130,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)
|
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] {
|
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)
|
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] {
|
func Flatten[E, A any](mma IOEither[E, IOEither[E, A]]) IOEither[E, A] {
|
||||||
return G.Flatten(mma)
|
return G.Flatten(mma)
|
||||||
}
|
}
|
||||||
@@ -192,6 +216,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)
|
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
|
// 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] {
|
func MonadChainFirstIOK[E, A, B any](ma IOEither[E, A], f func(A) I.IO[B]) IOEither[E, A] {
|
||||||
return G.MonadChainFirstIOK(ma, f)
|
return G.MonadChainFirstIOK(ma, f)
|
||||||
@@ -239,3 +271,8 @@ func MonadFlap[E, B, A any](fab IOEither[E, func(A) B], a A) IOEither[E, B] {
|
|||||||
func Flap[E, B, A any](a A) func(IOEither[E, func(A) B]) IOEither[E, B] {
|
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)
|
return G.Flap[IOEither[E, func(A) B], IOEither[E, B]](a)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ToIOOption converts an [IOEither] to an [IOO.IOOption]
|
||||||
|
func ToIOOption[E, A any](ioe IOEither[E, A]) IOO.IOOption[A] {
|
||||||
|
return G.ToIOOption[IOO.IOOption[A]](ioe)
|
||||||
|
}
|
||||||
|
|||||||
@@ -18,11 +18,11 @@ package ioeither
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
|
IO "github.com/IBM/fp-go/io"
|
||||||
G "github.com/IBM/fp-go/ioeither/generic"
|
G "github.com/IBM/fp-go/ioeither/generic"
|
||||||
L "github.com/IBM/fp-go/lazy"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// WithLock executes the provided IO operation in the scope of a lock
|
// WithLock executes the provided IO operation in the scope of a lock
|
||||||
func WithLock[E, A any](lock L.Lazy[context.CancelFunc]) func(fa IOEither[E, A]) IOEither[E, A] {
|
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)
|
return G.WithLock[IOEither[E, A]](lock)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Code generated by go generate; DO NOT EDIT.
|
// Code generated by go generate; DO NOT EDIT.
|
||||||
// This file was generated by robots at
|
// This file was generated by robots at
|
||||||
// 2023-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
|
package iooption
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Code generated by go generate; DO NOT EDIT.
|
// Code generated by go generate; DO NOT EDIT.
|
||||||
// This file was generated by robots at
|
// This file was generated by robots at
|
||||||
// 2023-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
|
package generic
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
@@ -64,13 +64,6 @@ func FromEither[GA ~func() O.Option[A], E, A any](e ET.Either[E, A]) GA {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func FromIOEither[GA ~func() O.Option[A], GEA ~func() ET.Either[E, A], E, A any](ioe GEA) GA {
|
|
||||||
return F.Pipe1(
|
|
||||||
ioe,
|
|
||||||
IO.Map[GEA, GA](ET.ToOption[E, A]),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func MonadMap[GA ~func() O.Option[A], GB ~func() O.Option[B], A, B any](fa GA, f func(A) B) GB {
|
func MonadMap[GA ~func() O.Option[A], GB ~func() O.Option[B], A, B any](fa GA, f func(A) B) GB {
|
||||||
return optiont.MonadMap(IO.MonadMap[GA, GB, O.Option[A], O.Option[B]], fa, f)
|
return optiont.MonadMap(IO.MonadMap[GA, GB, O.Option[A], O.Option[B]], fa, f)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ package iooption
|
|||||||
import (
|
import (
|
||||||
ET "github.com/IBM/fp-go/either"
|
ET "github.com/IBM/fp-go/either"
|
||||||
I "github.com/IBM/fp-go/io"
|
I "github.com/IBM/fp-go/io"
|
||||||
IOE "github.com/IBM/fp-go/ioeither"
|
|
||||||
G "github.com/IBM/fp-go/iooption/generic"
|
G "github.com/IBM/fp-go/iooption/generic"
|
||||||
O "github.com/IBM/fp-go/option"
|
O "github.com/IBM/fp-go/option"
|
||||||
)
|
)
|
||||||
@@ -129,11 +128,6 @@ func Defer[A any](gen func() IOOption[A]) IOOption[A] {
|
|||||||
return G.Defer[IOOption[A]](gen)
|
return G.Defer[IOOption[A]](gen)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FromIOEither converts an [IOEither] into an [IOOption]
|
|
||||||
func FromIOEither[E, A any](ioe IOE.IOEither[E, A]) IOOption[A] {
|
|
||||||
return G.FromIOEither[IOOption[A]](ioe)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FromEither converts an [Either] into an [IOOption]
|
// FromEither converts an [Either] into an [IOOption]
|
||||||
func FromEither[E, A any](e ET.Either[E, A]) IOOption[A] {
|
func FromEither[E, A any](e ET.Either[E, A]) IOOption[A] {
|
||||||
return G.FromEither[IOOption[A]](e)
|
return G.FromEither[IOOption[A]](e)
|
||||||
|
|||||||
@@ -18,11 +18,11 @@ package iooption
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
|
IO "github.com/IBM/fp-go/io"
|
||||||
G "github.com/IBM/fp-go/iooption/generic"
|
G "github.com/IBM/fp-go/iooption/generic"
|
||||||
L "github.com/IBM/fp-go/lazy"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// WithLock executes the provided IO operation in the scope of a lock
|
// WithLock executes the provided IO operation in the scope of a lock
|
||||||
func WithLock[E, A any](lock L.Lazy[context.CancelFunc]) func(fa IOOption[A]) IOOption[A] {
|
func WithLock[E, A any](lock IO.IO[context.CancelFunc]) func(fa IOOption[A]) IOOption[A] {
|
||||||
return G.WithLock[IOOption[A]](lock)
|
return G.WithLock[IOOption[A]](lock)
|
||||||
}
|
}
|
||||||
|
|||||||
26
iterator/stateless/any.go
Normal file
26
iterator/stateless/any.go
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package stateless
|
||||||
|
|
||||||
|
import (
|
||||||
|
G "github.com/IBM/fp-go/iterator/stateless/generic"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Any returns `true` if any element of the iterable is `true`. If the iterable is empty, return `false`
|
||||||
|
// Similar to the [https://docs.python.org/3/library/functions.html#any] function
|
||||||
|
func Any[U any](pred func(U) bool) func(ma Iterator[U]) bool {
|
||||||
|
return G.Any[Iterator[U]](pred)
|
||||||
|
}
|
||||||
38
iterator/stateless/any_test.go
Normal file
38
iterator/stateless/any_test.go
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package stateless
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
A "github.com/IBM/fp-go/array"
|
||||||
|
F "github.com/IBM/fp-go/function"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAny(t *testing.T) {
|
||||||
|
|
||||||
|
anyBool := Any(F.Identity[bool])
|
||||||
|
|
||||||
|
i1 := FromArray(A.From(false, true, false))
|
||||||
|
assert.True(t, anyBool(i1))
|
||||||
|
|
||||||
|
i2 := FromArray(A.From(false, false, false))
|
||||||
|
assert.False(t, anyBool(i2))
|
||||||
|
|
||||||
|
i3 := Empty[bool]()
|
||||||
|
assert.False(t, anyBool(i3))
|
||||||
|
}
|
||||||
55
iterator/stateless/example_test.go
Normal file
55
iterator/stateless/example_test.go
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package stateless
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
A "github.com/IBM/fp-go/array"
|
||||||
|
F "github.com/IBM/fp-go/function"
|
||||||
|
O "github.com/IBM/fp-go/option"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Example_any() {
|
||||||
|
// `Any` function that simply returns the boolean identity
|
||||||
|
anyBool := Any(F.Identity[bool])
|
||||||
|
|
||||||
|
fmt.Println(anyBool(FromArray(A.From(true, false, false))))
|
||||||
|
fmt.Println(anyBool(FromArray(A.From(false, false, false))))
|
||||||
|
fmt.Println(anyBool(Empty[bool]()))
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// true
|
||||||
|
// false
|
||||||
|
// false
|
||||||
|
}
|
||||||
|
|
||||||
|
func Example_next() {
|
||||||
|
|
||||||
|
seq := MakeBy(F.Identity[int])
|
||||||
|
|
||||||
|
first := seq()
|
||||||
|
|
||||||
|
value := F.Pipe1(
|
||||||
|
first,
|
||||||
|
O.Map(Current[int]),
|
||||||
|
)
|
||||||
|
|
||||||
|
fmt.Println(value)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// Some[int](0)
|
||||||
|
}
|
||||||
31
iterator/stateless/generic/any.go
Normal file
31
iterator/stateless/generic/any.go
Normal 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 generic
|
||||||
|
|
||||||
|
import (
|
||||||
|
F "github.com/IBM/fp-go/function"
|
||||||
|
O "github.com/IBM/fp-go/option"
|
||||||
|
T "github.com/IBM/fp-go/tuple"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Any returns `true` if any element of the iterable is `true`. If the iterable is empty, return `false`
|
||||||
|
func Any[GU ~func() O.Option[T.Tuple2[GU, U]], FCT ~func(U) bool, U any](pred FCT) func(ma GU) bool {
|
||||||
|
return F.Flow3(
|
||||||
|
Filter[GU](pred),
|
||||||
|
First[GU],
|
||||||
|
O.IsSome[U],
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -41,7 +41,7 @@ func DropWhile[GU ~func() O.Option[T.Tuple2[GU, U]], U any](pred func(U) bool) f
|
|||||||
return F.Pipe2(
|
return F.Pipe2(
|
||||||
t,
|
t,
|
||||||
fromPred,
|
fromPred,
|
||||||
O.Fold(recurse(t.F1), O.Of[T.Tuple2[GU, U]]),
|
O.Fold(recurse(Next(t)), O.Of[T.Tuple2[GU, U]]),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,16 @@ import (
|
|||||||
T "github.com/IBM/fp-go/tuple"
|
T "github.com/IBM/fp-go/tuple"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Next returns the iterator for the next element in an iterator `T.Tuple2`
|
||||||
|
func Next[GU ~func() O.Option[T.Tuple2[GU, U]], U any](m T.Tuple2[GU, U]) GU {
|
||||||
|
return T.First(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Current returns the current element in an iterator `T.Tuple2`
|
||||||
|
func Current[GU ~func() O.Option[T.Tuple2[GU, U]], U any](m T.Tuple2[GU, U]) U {
|
||||||
|
return T.Second(m)
|
||||||
|
}
|
||||||
|
|
||||||
// From constructs an array from a set of variadic arguments
|
// From constructs an array from a set of variadic arguments
|
||||||
func From[GU ~func() O.Option[T.Tuple2[GU, U]], U any](data ...U) GU {
|
func From[GU ~func() O.Option[T.Tuple2[GU, U]], U any](data ...U) GU {
|
||||||
return FromArray[GU](data)
|
return FromArray[GU](data)
|
||||||
@@ -56,8 +66,8 @@ func reduce[GU ~func() O.Option[T.Tuple2[GU, U]], U, V any](as GU, f func(V, U)
|
|||||||
current := initial
|
current := initial
|
||||||
for ok {
|
for ok {
|
||||||
// next (with bad side effect)
|
// next (with bad side effect)
|
||||||
current = f(current, next.F2)
|
current = f(current, Current(next))
|
||||||
next, ok = O.Unwrap(next.F1())
|
next, ok = O.Unwrap(Next(next)())
|
||||||
}
|
}
|
||||||
return current
|
return current
|
||||||
}
|
}
|
||||||
@@ -199,8 +209,8 @@ func FilterMap[GV ~func() O.Option[T.Tuple2[GV, V]], GU ~func() O.Option[T.Tuple
|
|||||||
m = O.Fold(
|
m = O.Fold(
|
||||||
Empty[GV](),
|
Empty[GV](),
|
||||||
func(t T.Tuple2[GU, U]) O.Option[T.Tuple2[GV, V]] {
|
func(t T.Tuple2[GU, U]) O.Option[T.Tuple2[GV, V]] {
|
||||||
r := recurse(t.F1)
|
r := recurse(Next(t))
|
||||||
return O.MonadFold(f(t.F2), r, F.Flow2(
|
return O.MonadFold(f(Current(t)), r, F.Flow2(
|
||||||
F.Bind1st(T.MakeTuple2[GV, V], r),
|
F.Bind1st(T.MakeTuple2[GV, V], r),
|
||||||
O.Some[T.Tuple2[GV, V]],
|
O.Some[T.Tuple2[GV, V]],
|
||||||
))
|
))
|
||||||
|
|||||||
@@ -26,6 +26,16 @@ import (
|
|||||||
// Iterator represents a stateless, pure way to iterate over a sequence
|
// Iterator represents a stateless, pure way to iterate over a sequence
|
||||||
type Iterator[U any] L.Lazy[O.Option[T.Tuple2[Iterator[U], U]]]
|
type Iterator[U any] L.Lazy[O.Option[T.Tuple2[Iterator[U], U]]]
|
||||||
|
|
||||||
|
// Next returns the [Iterator] for the next element in an iterator `T.Tuple2`
|
||||||
|
func Next[U any](m T.Tuple2[Iterator[U], U]) Iterator[U] {
|
||||||
|
return G.Next(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Current returns the current element in an [Iterator] `T.Tuple2`
|
||||||
|
func Current[U any](m T.Tuple2[Iterator[U], U]) U {
|
||||||
|
return G.Current(m)
|
||||||
|
}
|
||||||
|
|
||||||
// Empty returns the empty iterator
|
// Empty returns the empty iterator
|
||||||
func Empty[U any]() Iterator[U] {
|
func Empty[U any]() Iterator[U] {
|
||||||
return G.Empty[Iterator[U]]()
|
return G.Empty[Iterator[U]]()
|
||||||
|
|||||||
12
json/json.go
12
json/json.go
@@ -23,16 +23,12 @@ import (
|
|||||||
|
|
||||||
// Unmarshal parses a JSON data structure from bytes
|
// Unmarshal parses a JSON data structure from bytes
|
||||||
func Unmarshal[A any](data []byte) E.Either[error, A] {
|
func Unmarshal[A any](data []byte) E.Either[error, A] {
|
||||||
return E.TryCatchError(func() (A, error) {
|
var result A
|
||||||
var result A
|
err := json.Unmarshal(data, &result)
|
||||||
err := json.Unmarshal(data, &result)
|
return E.TryCatchError(result, err)
|
||||||
return result, err
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Marshal converts a data structure to json
|
// Marshal converts a data structure to json
|
||||||
func Marshal[A any](a A) E.Either[error, []byte] {
|
func Marshal[A any](a A) E.Either[error, []byte] {
|
||||||
return E.TryCatchError(func() ([]byte, error) {
|
return E.TryCatchError(json.Marshal(a))
|
||||||
return json.Marshal(a)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,13 +41,13 @@ func SequenceArray[A any](tas []Lazy[A]) Lazy[[]A] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func MonadTraverseRecord[K comparable, A, B any](tas map[K]A, f func(A) Lazy[B]) Lazy[map[K]B] {
|
func MonadTraverseRecord[K comparable, A, B any](tas map[K]A, f func(A) Lazy[B]) Lazy[map[K]B] {
|
||||||
return G.MonadTraverseRecord[Lazy[B], Lazy[map[K]B]](tas, f)
|
return G.MonadTraverseRecord[Lazy[map[K]B]](tas, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TraverseRecord applies a function returning an [IO] to all elements in a record and the
|
// TraverseRecord applies a function returning an [IO] to all elements in a record and the
|
||||||
// transforms this into an [IO] of that record
|
// transforms this into an [IO] of that record
|
||||||
func TraverseRecord[K comparable, A, B any](f func(A) Lazy[B]) func(map[K]A) Lazy[map[K]B] {
|
func TraverseRecord[K comparable, A, B any](f func(A) Lazy[B]) func(map[K]A) Lazy[map[K]B] {
|
||||||
return G.TraverseRecord[Lazy[B], Lazy[map[K]B], map[K]A](f)
|
return G.TraverseRecord[Lazy[map[K]B], map[K]A, Lazy[B]](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TraverseRecord applies a function returning an [IO] to all elements in a record and the
|
// TraverseRecord applies a function returning an [IO] to all elements in a record and the
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ func MakeMagma[A any](c func(A, A) A) Magma[A] {
|
|||||||
|
|
||||||
func Reverse[A any](m Magma[A]) Magma[A] {
|
func Reverse[A any](m Magma[A]) Magma[A] {
|
||||||
return MakeMagma(func(x A, y A) A {
|
return MakeMagma(func(x A, y A) A {
|
||||||
return m.Concat(y, y)
|
return m.Concat(y, x)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import (
|
|||||||
G "github.com/IBM/fp-go/optics/traversal/generic"
|
G "github.com/IBM/fp-go/optics/traversal/generic"
|
||||||
)
|
)
|
||||||
|
|
||||||
// FromArray returns a traversal from an array for the identity monad
|
// FromArray returns a traversal from an array for the identity [Monoid]
|
||||||
func FromArray[E, A any](m M.Monoid[E]) G.Traversal[[]A, A, C.Const[E, []A], C.Const[E, A]] {
|
func FromArray[E, A any](m M.Monoid[E]) G.Traversal[[]A, A, C.Const[E, []A], C.Const[E, A]] {
|
||||||
return AR.FromArray[[]A, E, A](m)
|
return AR.FromArray[[]A, E, A](m)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Code generated by go generate; DO NOT EDIT.
|
// Code generated by go generate; DO NOT EDIT.
|
||||||
// This file was generated by robots at
|
// This file was generated by robots at
|
||||||
// 2023-09-12 13:44:34.2077257 +0200 CEST m=+0.023541001
|
// 2023-10-23 08:31:07.8687134 +0200 CEST m=+0.012222901
|
||||||
|
|
||||||
package option
|
package option
|
||||||
|
|
||||||
|
|||||||
@@ -82,6 +82,10 @@ func Fold[A, B any](onNone func() B, onSome func(a A) B) func(ma Option[A]) B {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func MonadGetOrElse[A any](fa Option[A], onNone func() A) A {
|
||||||
|
return MonadFold(fa, onNone, F.Identity[A])
|
||||||
|
}
|
||||||
|
|
||||||
func GetOrElse[A any](onNone func() A) func(Option[A]) A {
|
func GetOrElse[A any](onNone func() A) func(Option[A]) A {
|
||||||
return Fold(onNone, F.Identity[A])
|
return Fold(onNone, F.Identity[A])
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Code generated by go generate; DO NOT EDIT.
|
// Code generated by go generate; DO NOT EDIT.
|
||||||
// This file was generated by robots at
|
// This file was generated by robots at
|
||||||
// 2023-09-12 13:44:36.1655251 +0200 CEST m=+0.010000501
|
// 2023-10-23 08:31:13.0131834 +0200 CEST m=+0.010996501
|
||||||
|
|
||||||
package reader
|
package reader
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Code generated by go generate; DO NOT EDIT.
|
// Code generated by go generate; DO NOT EDIT.
|
||||||
// This file was generated by robots at
|
// This file was generated by robots at
|
||||||
// 2023-09-12 13:44:36.166526 +0200 CEST m=+0.011001401
|
// 2023-10-23 08:31:13.0131834 +0200 CEST m=+0.010996501
|
||||||
package generic
|
package generic
|
||||||
|
|
||||||
// From0 converts a function with 1 parameters returning a [R] into a function with 0 parameters returning a [GRA]
|
// From0 converts a function with 1 parameters returning a [R] into a function with 0 parameters returning a [GRA]
|
||||||
|
|||||||
@@ -25,58 +25,58 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
testError = fmt.Errorf("error")
|
errFoo = fmt.Errorf("error")
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSequenceT1(t *testing.T) {
|
func TestSequenceT1(t *testing.T) {
|
||||||
|
|
||||||
t1 := Of[MyContext, error]("s1")
|
t1 := Of[MyContext, error]("s1")
|
||||||
e1 := Left[MyContext, string](testError)
|
e1 := Left[MyContext, string](errFoo)
|
||||||
|
|
||||||
res1 := SequenceT1(t1)
|
res1 := SequenceT1(t1)
|
||||||
assert.Equal(t, E.Of[error](T.MakeTuple1("s1")), res1(defaultContext))
|
assert.Equal(t, E.Of[error](T.MakeTuple1("s1")), res1(defaultContext))
|
||||||
|
|
||||||
res2 := SequenceT1(e1)
|
res2 := SequenceT1(e1)
|
||||||
assert.Equal(t, E.Left[T.Tuple1[string]](testError), res2(defaultContext))
|
assert.Equal(t, E.Left[T.Tuple1[string]](errFoo), res2(defaultContext))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSequenceT2(t *testing.T) {
|
func TestSequenceT2(t *testing.T) {
|
||||||
|
|
||||||
t1 := Of[MyContext, error]("s1")
|
t1 := Of[MyContext, error]("s1")
|
||||||
e1 := Left[MyContext, string](testError)
|
e1 := Left[MyContext, string](errFoo)
|
||||||
t2 := Of[MyContext, error](2)
|
t2 := Of[MyContext, error](2)
|
||||||
e2 := Left[MyContext, int](testError)
|
e2 := Left[MyContext, int](errFoo)
|
||||||
|
|
||||||
res1 := SequenceT2(t1, t2)
|
res1 := SequenceT2(t1, t2)
|
||||||
assert.Equal(t, E.Of[error](T.MakeTuple2("s1", 2)), res1(defaultContext))
|
assert.Equal(t, E.Of[error](T.MakeTuple2("s1", 2)), res1(defaultContext))
|
||||||
|
|
||||||
res2 := SequenceT2(e1, t2)
|
res2 := SequenceT2(e1, t2)
|
||||||
assert.Equal(t, E.Left[T.Tuple2[string, int]](testError), res2(defaultContext))
|
assert.Equal(t, E.Left[T.Tuple2[string, int]](errFoo), res2(defaultContext))
|
||||||
|
|
||||||
res3 := SequenceT2(t1, e2)
|
res3 := SequenceT2(t1, e2)
|
||||||
assert.Equal(t, E.Left[T.Tuple2[string, int]](testError), res3(defaultContext))
|
assert.Equal(t, E.Left[T.Tuple2[string, int]](errFoo), res3(defaultContext))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSequenceT3(t *testing.T) {
|
func TestSequenceT3(t *testing.T) {
|
||||||
|
|
||||||
t1 := Of[MyContext, error]("s1")
|
t1 := Of[MyContext, error]("s1")
|
||||||
e1 := Left[MyContext, string](testError)
|
e1 := Left[MyContext, string](errFoo)
|
||||||
t2 := Of[MyContext, error](2)
|
t2 := Of[MyContext, error](2)
|
||||||
e2 := Left[MyContext, int](testError)
|
e2 := Left[MyContext, int](errFoo)
|
||||||
t3 := Of[MyContext, error](true)
|
t3 := Of[MyContext, error](true)
|
||||||
e3 := Left[MyContext, bool](testError)
|
e3 := Left[MyContext, bool](errFoo)
|
||||||
|
|
||||||
res1 := SequenceT3(t1, t2, t3)
|
res1 := SequenceT3(t1, t2, t3)
|
||||||
assert.Equal(t, E.Of[error](T.MakeTuple3("s1", 2, true)), res1(defaultContext))
|
assert.Equal(t, E.Of[error](T.MakeTuple3("s1", 2, true)), res1(defaultContext))
|
||||||
|
|
||||||
res2 := SequenceT3(e1, t2, t3)
|
res2 := SequenceT3(e1, t2, t3)
|
||||||
assert.Equal(t, E.Left[T.Tuple3[string, int, bool]](testError), res2(defaultContext))
|
assert.Equal(t, E.Left[T.Tuple3[string, int, bool]](errFoo), res2(defaultContext))
|
||||||
|
|
||||||
res3 := SequenceT3(t1, e2, t3)
|
res3 := SequenceT3(t1, e2, t3)
|
||||||
assert.Equal(t, E.Left[T.Tuple3[string, int, bool]](testError), res3(defaultContext))
|
assert.Equal(t, E.Left[T.Tuple3[string, int, bool]](errFoo), res3(defaultContext))
|
||||||
|
|
||||||
res4 := SequenceT3(t1, t2, e3)
|
res4 := SequenceT3(t1, t2, e3)
|
||||||
assert.Equal(t, E.Left[T.Tuple3[string, int, bool]](testError), res4(defaultContext))
|
assert.Equal(t, E.Left[T.Tuple3[string, int, bool]](errFoo), res4(defaultContext))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSequenceT4(t *testing.T) {
|
func TestSequenceT4(t *testing.T) {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Code generated by go generate; DO NOT EDIT.
|
// Code generated by go generate; DO NOT EDIT.
|
||||||
// This file was generated by robots at
|
// This file was generated by robots at
|
||||||
// 2023-09-12 13:44:37.5566477 +0200 CEST m=+0.009011201
|
// 2023-10-23 08:31:16.2414489 +0200 CEST m=+0.013052301
|
||||||
|
|
||||||
package readerioeither
|
package readerioeither
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Code generated by go generate; DO NOT EDIT.
|
// Code generated by go generate; DO NOT EDIT.
|
||||||
// This file was generated by robots at
|
// This file was generated by robots at
|
||||||
// 2023-09-12 13:44:37.5566477 +0200 CEST m=+0.009011201
|
// 2023-10-23 08:31:16.241979 +0200 CEST m=+0.013582401
|
||||||
package generic
|
package generic
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
@@ -99,6 +99,14 @@ func Collect[M ~map[K]V, GR ~[]R, K comparable, V, R any](f func(K, V) R) func(M
|
|||||||
return F.Bind2nd(collect[M, GR, K, V, R], f)
|
return F.Bind2nd(collect[M, GR, K, V, R], f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CollectOrd[M ~map[K]V, GR ~[]R, K comparable, V, R any](o ord.Ord[K]) func(f func(K, V) R) func(M) GR {
|
||||||
|
return func(f func(K, V) R) func(M) GR {
|
||||||
|
return func(r M) GR {
|
||||||
|
return collectOrd[M, GR](o, r, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func Reduce[M ~map[K]V, K comparable, V, R any](f func(R, V) R, initial R) func(M) R {
|
func Reduce[M ~map[K]V, K comparable, V, R any](f func(R, V) R, initial R) func(M) R {
|
||||||
return func(r M) R {
|
return func(r M) R {
|
||||||
return G.Reduce(r, f, initial)
|
return G.Reduce(r, f, initial)
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user