mirror of
https://github.com/IBM/fp-go.git
synced 2025-08-28 19:49:07 +02:00
Compare commits
107 Commits
cleue-add-
...
v1.0.53
Author | SHA1 | Date | |
---|---|---|---|
|
7766787cc1 | ||
|
6a9f38f990 | ||
|
e9584bc247 | ||
|
55252c5680 | ||
|
88bf35c4af | ||
|
da3c9683eb | ||
|
08d9fed9af | ||
|
83a0c6cdef | ||
|
9da484b79e | ||
|
e46cfd89d4 | ||
|
6a4cdfa891 | ||
|
93a7e9964b | ||
|
9ef98e1ec4 | ||
|
f1a730998d | ||
|
f129297045 | ||
|
b434f1fbf4 | ||
|
756e1336dc | ||
|
c629d18bb3 | ||
|
1eefc28ba6 | ||
|
af2915d98a | ||
|
e9f9c2777f | ||
|
895862a67e | ||
|
caf0574742 | ||
|
bace6f01eb | ||
|
254c63a16f | ||
|
655c8a9b1d | ||
|
c6d6be66e0 | ||
|
e313f95865 | ||
|
5f25317f97 | ||
|
f5aa2d6c15 | ||
|
7262820624 | ||
|
c8fc10358b | ||
|
1cd167541d | ||
|
ebe94d71dc | ||
|
7ef6eb524b | ||
|
7b82e87bf3 | ||
|
e8fdbe9f87 | ||
|
226c7039de | ||
|
943ae8e009 | ||
|
44c8441b07 | ||
|
600aeae770 | ||
|
f74a407294 | ||
|
b15ab38861 | ||
|
6532a83e82 | ||
|
7c12b72db1 | ||
|
dc894ad643 | ||
|
c902058320 | ||
|
bf33f4fb66 | ||
|
b4d2a5c6be | ||
|
705b71d95c | ||
|
34844bcfc2 | ||
|
9a9d13b066 | ||
|
da1449e680 | ||
|
865d9fe064 | ||
|
c5b1bae65a | ||
|
0a395f63ff | ||
|
26a7066de0 | ||
|
52823e2c8e | ||
|
503021c65e | ||
|
a2a6a41993 | ||
|
7da9de6f41 | ||
|
ff1b6faf84 | ||
|
38120764e7 | ||
|
a83c2aec49 | ||
|
03debd37c8 | ||
|
4f04344cda | ||
|
cf1c886f48 | ||
|
3e0eb99b88 | ||
|
cb15a3d9fc | ||
|
16535605f5 | ||
|
53f4e5ebd7 | ||
|
fb91fd5dc8 | ||
|
3ccafb5302 | ||
|
52b71ef4f3 | ||
|
5d77d5bb3d | ||
|
8ba8f852fa | ||
|
29d9882d2a | ||
|
f80ca31e14 | ||
|
8692078972 | ||
|
12a4f6801c | ||
|
8650a8a600 | ||
|
fb3b1f115c | ||
|
ce66cf2295 | ||
|
80e579dd0b | ||
|
ddafd1ee57 | ||
|
b5f077da71 | ||
|
1a0c40b419 | ||
|
d5d89b1853 | ||
|
0f061a5099 | ||
|
45e05f25ff | ||
|
a390d53451 | ||
|
1346b9378a | ||
|
befd4f471e | ||
|
db8d3da87a | ||
|
ee4e936183 | ||
|
0064ac1c75 | ||
|
8944a66c18 | ||
|
bd0c42db01 | ||
|
e9f03e2d26 | ||
|
bb630810fc | ||
|
9ba9eaacbe | ||
|
a9f6839acd | ||
|
1b1dccc551 | ||
|
39c6108bf5 | ||
|
83e1ff30c1 | ||
|
d9dda4cfa5 | ||
|
d9b2804a7e |
15
.github/workflows/build.yml
vendored
15
.github/workflows/build.yml
vendored
@@ -17,22 +17,25 @@ on:
|
||||
env:
|
||||
# Currently no way to detect automatically
|
||||
DEFAULT_BRANCH: main
|
||||
GO_VERSION: 1.20.5 # renovate: datasource=golang-version depName=golang
|
||||
GO_VERSION: 1.20.6 # renovate: datasource=golang-version depName=golang
|
||||
NODE_VERSION: 18
|
||||
DRY_RUN: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [ '1.20.x', '1.21.x' ]
|
||||
steps:
|
||||
# full checkout for semantic-release
|
||||
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Set up go ${{env.GO_VERSION}}
|
||||
- name: Set up go ${{ matrix.go-version }}
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: ${{env.GO_VERSION}}
|
||||
go-version: ${{ matrix.go-version }}
|
||||
-
|
||||
name: Tests
|
||||
run: |
|
||||
@@ -52,12 +55,12 @@ jobs:
|
||||
steps:
|
||||
# full checkout for semantic-release
|
||||
- name: Full checkout
|
||||
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Node.js ${{ env.NODE_VERSION }}
|
||||
uses: actions/setup-node@e33196f7422957bea03ed53f6fbb155025ffc7b8 # v3.7.0
|
||||
uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4.0.0
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
|
||||
|
17
README.md
17
README.md
@@ -14,6 +14,8 @@ go get github.com/IBM/fp-go
|
||||
|
||||
Refer to the [samples](./samples/).
|
||||
|
||||
Find API documentation [here](https://pkg.go.dev/github.com/IBM/fp-go)
|
||||
|
||||
## Design Goal
|
||||
|
||||
This library aims to provide a set of data types and functions that make it easy and fun to write maintainable and testable code in golang. It encourages the following patterns:
|
||||
@@ -73,7 +75,7 @@ This library aims to provide a set of data types and functions that make it easy
|
||||
|
||||
The library itself also comprises many small functions, but it's admittedly harder to maintain than code that uses it. However this asymmetry is intended because it offloads complexity from users into a central component.
|
||||
|
||||
## Comparation to Idiomatic Go
|
||||
## Comparison to Idiomatic Go
|
||||
|
||||
In this section we discuss how the functional APIs differ from idiomatic go function signatures and how to convert back and forth.
|
||||
|
||||
@@ -181,7 +183,7 @@ The `Map` operation for `ReaderIOEither` is defined as:
|
||||
func Map[R, E, A, B any](f func(A) B) func(fa ReaderIOEither[R, E, A]) ReaderIOEither[R, E, B]
|
||||
```
|
||||
|
||||
and in fact the equivalent operations for all other mondas follow the same pattern, we could try to introduce a new type for `ReaderIOEither` (without a parameter) as a HKT, e.g. like so (made-up syntax, does not work in go):
|
||||
and in fact the equivalent operations for all other monads follow the same pattern, we could try to introduce a new type for `ReaderIOEither` (without a parameter) as a HKT, e.g. like so (made-up syntax, does not work in go):
|
||||
|
||||
```go
|
||||
func Map[HKT, R, E, A, B any](f func(A) B) func(HKT[R, E, A]) HKT[R, E, B]
|
||||
@@ -192,3 +194,14 @@ this would be the completely generic method signature for all possible monads. I
|
||||
This FP library addresses this by introducing the HKTs as individual types, e.g. `HKT[A]` would be represented as a new generic type `HKTA`. This loses the correlation to the type `A` but allows to implement generic algorithms, at the price of readability.
|
||||
|
||||
For that reason these implementations are kept in the `internal` package. These are meant to be used by the library itself or by extensions, not by end users.
|
||||
|
||||
## Map/Ap/Flap
|
||||
|
||||
The following table lists the relationship between some selected operators
|
||||
|
||||
| Opertator | Parameter | Monad | Result |
|
||||
| -------- | ---------------- | --------------- | -------- |
|
||||
| Map | `func(A) B` | `HKT[A]` | `HKT[B]` |
|
||||
| Chain | `func(A) HKT[B]` | `HKT[A]` | `HKT[B]` |
|
||||
| Ap | `HKT[A]` | `HKT[func(A)B]` | `HKT[B]` |
|
||||
| Flap | `A` | `HKT[func(A)B]` | `HKT[B]` |
|
||||
|
@@ -294,3 +294,25 @@ func SliceRight[A any](start int) func([]A) []A {
|
||||
func Copy[A any](b []A) []A {
|
||||
return G.Copy(b)
|
||||
}
|
||||
|
||||
// FoldMap maps and folds an array. Map the Array passing each value to the iterating function. Then fold the results using the provided Monoid.
|
||||
func FoldMap[A, B any](m M.Monoid[B]) func(func(A) B) func([]A) B {
|
||||
return G.FoldMap[[]A](m)
|
||||
}
|
||||
|
||||
// Fold folds the array using the provided Monoid.
|
||||
func Fold[A any](m M.Monoid[A]) func([]A) A {
|
||||
return G.Fold[[]A](m)
|
||||
}
|
||||
|
||||
func Push[A any](a A) func([]A) []A {
|
||||
return G.Push[[]A](a)
|
||||
}
|
||||
|
||||
func MonadFlap[B, A any](fab []func(A) B, a A) []B {
|
||||
return G.MonadFlap[func(A) B, []func(A) B, []B, A, B](fab, a)
|
||||
}
|
||||
|
||||
func Flap[B, A any](a A) func([]func(A) B) []B {
|
||||
return G.Flap[func(A) B, []func(A) B, []B, A, B](a)
|
||||
}
|
||||
|
@@ -173,3 +173,22 @@ func TestFilterMap(t *testing.T) {
|
||||
|
||||
assert.Equal(t, From("a1", "a3"), res)
|
||||
}
|
||||
|
||||
func TestFoldMap(t *testing.T) {
|
||||
src := From("a", "b", "c")
|
||||
|
||||
fold := FoldMap[string](S.Monoid)(strings.ToUpper)
|
||||
|
||||
assert.Equal(t, "ABC", fold(src))
|
||||
}
|
||||
|
||||
func ExampleFoldMap() {
|
||||
src := From("a", "b", "c")
|
||||
|
||||
fold := FoldMap[string](S.Monoid)(strings.ToUpper)
|
||||
|
||||
fmt.Println(fold(src))
|
||||
|
||||
// Output: ABC
|
||||
|
||||
}
|
||||
|
59
array/examples_basic_test.go
Normal file
59
array/examples_basic_test.go
Normal file
@@ -0,0 +1,59 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package array
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
F "github.com/IBM/fp-go/function"
|
||||
O "github.com/IBM/fp-go/option"
|
||||
)
|
||||
|
||||
// Example_basic adapts examples from [https://github.com/inato/fp-ts-cheatsheet#basic-manipulation]
|
||||
func Example_basic() {
|
||||
|
||||
someArray := From(0, 1, 2, 3, 4, 5, 6, 7, 8, 9) // []int
|
||||
|
||||
isEven := func(num int) bool {
|
||||
return num%2 == 0
|
||||
}
|
||||
|
||||
square := func(num int) int {
|
||||
return num * num
|
||||
}
|
||||
|
||||
// filter and map
|
||||
result := F.Pipe2(
|
||||
someArray,
|
||||
Filter(isEven),
|
||||
Map(square),
|
||||
) // [0 4 16 36 64]
|
||||
|
||||
// or in one go with filterMap
|
||||
resultFilterMap := F.Pipe1(
|
||||
someArray,
|
||||
FilterMap(
|
||||
F.Flow2(O.FromPredicate(isEven), O.Map(square)),
|
||||
),
|
||||
)
|
||||
|
||||
fmt.Println(result)
|
||||
fmt.Println(resultFilterMap)
|
||||
|
||||
// Output:
|
||||
// [0 4 16 36 64]
|
||||
// [0 4 16 36 64]
|
||||
}
|
92
array/examples_sort_test.go
Normal file
92
array/examples_sort_test.go
Normal file
@@ -0,0 +1,92 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package array
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
F "github.com/IBM/fp-go/function"
|
||||
I "github.com/IBM/fp-go/number/integer"
|
||||
O "github.com/IBM/fp-go/option"
|
||||
"github.com/IBM/fp-go/ord"
|
||||
S "github.com/IBM/fp-go/string"
|
||||
)
|
||||
|
||||
type user struct {
|
||||
name string
|
||||
age O.Option[int]
|
||||
}
|
||||
|
||||
func (user user) GetName() string {
|
||||
return user.name
|
||||
}
|
||||
|
||||
func (user user) GetAge() O.Option[int] {
|
||||
return user.age
|
||||
}
|
||||
|
||||
// Example_sort adapts examples from [https://github.com/inato/fp-ts-cheatsheet#sort-elements-with-ord]
|
||||
func Example_sort() {
|
||||
|
||||
strings := From("zyx", "abc", "klm")
|
||||
|
||||
sortedStrings := F.Pipe1(
|
||||
strings,
|
||||
Sort(S.Ord),
|
||||
) // => ['abc', 'klm', 'zyx']
|
||||
|
||||
// reverse sort
|
||||
reverseSortedStrings := F.Pipe1(
|
||||
strings,
|
||||
Sort(ord.Reverse(S.Ord)),
|
||||
) // => ['zyx', 'klm', 'abc']
|
||||
|
||||
// sort Option
|
||||
optionalNumbers := From(O.Some(1337), O.None[int](), O.Some(42))
|
||||
|
||||
sortedNums := F.Pipe1(
|
||||
optionalNumbers,
|
||||
Sort(O.Ord(I.Ord)),
|
||||
)
|
||||
|
||||
// complex object with different rules
|
||||
byName := F.Pipe1(
|
||||
S.Ord,
|
||||
ord.Contramap(user.GetName),
|
||||
) // ord.Ord[user]
|
||||
|
||||
byAge := F.Pipe1(
|
||||
O.Ord(I.Ord),
|
||||
ord.Contramap(user.GetAge),
|
||||
) // ord.Ord[user]
|
||||
|
||||
sortedUsers := F.Pipe1(
|
||||
From(user{name: "a", age: O.Of(30)}, user{name: "d", age: O.Of(10)}, user{name: "c"}, user{name: "b", age: O.Of(10)}),
|
||||
SortBy(From(byAge, byName)),
|
||||
)
|
||||
|
||||
fmt.Println(sortedStrings)
|
||||
fmt.Println(reverseSortedStrings)
|
||||
fmt.Println(sortedNums)
|
||||
fmt.Println(sortedUsers)
|
||||
|
||||
// Output:
|
||||
// [abc klm zyx]
|
||||
// [zyx klm abc]
|
||||
// [None[int] Some[int](42) Some[int](1337)]
|
||||
// [{c {false 0}} {b {true 10}} {d {true 10}} {a {true 30}}]
|
||||
|
||||
}
|
@@ -18,6 +18,8 @@ package generic
|
||||
import (
|
||||
F "github.com/IBM/fp-go/function"
|
||||
"github.com/IBM/fp-go/internal/array"
|
||||
FC "github.com/IBM/fp-go/internal/functor"
|
||||
M "github.com/IBM/fp-go/monoid"
|
||||
O "github.com/IBM/fp-go/option"
|
||||
"github.com/IBM/fp-go/tuple"
|
||||
)
|
||||
@@ -27,6 +29,14 @@ func Of[GA ~[]A, A any](value A) GA {
|
||||
return GA{value}
|
||||
}
|
||||
|
||||
func Reduce[GA ~[]A, A, B any](fa GA, f func(B, A) B, initial B) B {
|
||||
return array.Reduce(fa, f, initial)
|
||||
}
|
||||
|
||||
func ReduceWithIndex[GA ~[]A, A, B any](fa GA, f func(int, B, A) B, initial B) B {
|
||||
return array.ReduceWithIndex(fa, f, initial)
|
||||
}
|
||||
|
||||
// From constructs an array from a set of variadic arguments
|
||||
func From[GA ~[]A, A any](data ...A) GA {
|
||||
return data
|
||||
@@ -104,6 +114,10 @@ func MonadMap[GA ~[]A, GB ~[]B, A, B any](as GA, f func(a A) B) GB {
|
||||
return array.MonadMap[GA, GB](as, f)
|
||||
}
|
||||
|
||||
func Map[GA ~[]A, GB ~[]B, A, B any](f func(a A) B) func(GA) GB {
|
||||
return F.Bind2nd(MonadMap[GA, GB, A, B], f)
|
||||
}
|
||||
|
||||
func Size[GA ~[]A, A any](as GA) int {
|
||||
return len(as)
|
||||
}
|
||||
@@ -174,6 +188,14 @@ func IsEmpty[AS ~[]A, A any](as AS) bool {
|
||||
return array.IsEmpty(as)
|
||||
}
|
||||
|
||||
func IsNil[GA ~[]A, A any](as GA) bool {
|
||||
return array.IsNil(as)
|
||||
}
|
||||
|
||||
func IsNonNil[GA ~[]A, A any](as GA) bool {
|
||||
return array.IsNonNil(as)
|
||||
}
|
||||
|
||||
func Match[AS ~[]A, A, B any](onEmpty func() B, onNonEmpty func(AS) B) func(AS) B {
|
||||
return func(as AS) B {
|
||||
if IsEmpty(as) {
|
||||
@@ -209,3 +231,31 @@ func Copy[AS ~[]A, A any](b AS) AS {
|
||||
copy(buf, b)
|
||||
return buf
|
||||
}
|
||||
|
||||
func FoldMap[AS ~[]A, A, B any](m M.Monoid[B]) func(func(A) B) func(AS) B {
|
||||
return func(f func(A) B) func(AS) B {
|
||||
return func(as AS) B {
|
||||
return array.Reduce(as, func(cur B, a A) B {
|
||||
return m.Concat(cur, f(a))
|
||||
}, m.Empty())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Fold[AS ~[]A, A any](m M.Monoid[A]) func(AS) A {
|
||||
return func(as AS) A {
|
||||
return array.Reduce(as, m.Concat, m.Empty())
|
||||
}
|
||||
}
|
||||
|
||||
func Push[GA ~[]A, A any](a A) func(GA) GA {
|
||||
return F.Bind2nd(array.Push[GA, A], a)
|
||||
}
|
||||
|
||||
func MonadFlap[FAB ~func(A) B, GFAB ~[]FAB, GB ~[]B, A, B any](fab GFAB, a A) GB {
|
||||
return FC.MonadFlap(MonadMap[GFAB, GB], fab, a)
|
||||
}
|
||||
|
||||
func Flap[FAB ~func(A) B, GFAB ~[]FAB, GB ~[]B, A, B any](a A) func(GFAB) GB {
|
||||
return F.Bind2nd(MonadFlap[FAB, GFAB, GB, A, B], a)
|
||||
}
|
||||
|
@@ -18,11 +18,17 @@ package generic
|
||||
import (
|
||||
"sort"
|
||||
|
||||
F "github.com/IBM/fp-go/function"
|
||||
O "github.com/IBM/fp-go/ord"
|
||||
)
|
||||
|
||||
// Sort implements a stable sort on the array given the provided ordering
|
||||
func Sort[GA ~[]T, T any](ord O.Ord[T]) func(ma GA) GA {
|
||||
return SortByKey[GA](ord, F.Identity[T])
|
||||
}
|
||||
|
||||
// SortByKey implements a stable sort on the array given the provided ordering on an extracted key
|
||||
func SortByKey[GA ~[]T, K, T any](ord O.Ord[K], f func(T) K) func(ma GA) GA {
|
||||
|
||||
return func(ma GA) GA {
|
||||
// nothing to sort
|
||||
@@ -34,8 +40,17 @@ func Sort[GA ~[]T, T any](ord O.Ord[T]) func(ma GA) GA {
|
||||
cpy := make(GA, l)
|
||||
copy(cpy, ma)
|
||||
sort.Slice(cpy, func(i, j int) bool {
|
||||
return ord.Compare(cpy[i], cpy[j]) < 0
|
||||
return ord.Compare(f(cpy[i]), f(cpy[j])) < 0
|
||||
})
|
||||
return cpy
|
||||
}
|
||||
}
|
||||
|
||||
// SortBy implements a stable sort on the array given the provided ordering
|
||||
func SortBy[GA ~[]T, GO ~[]O.Ord[T], T any](ord GO) func(ma GA) GA {
|
||||
return F.Pipe2(
|
||||
ord,
|
||||
Fold[GO](O.Monoid[T]()),
|
||||
Sort[GA, T],
|
||||
)
|
||||
}
|
||||
|
124
array/nonempty/array.go
Normal file
124
array/nonempty/array.go
Normal file
@@ -0,0 +1,124 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package nonempty
|
||||
|
||||
import (
|
||||
G "github.com/IBM/fp-go/array/generic"
|
||||
F "github.com/IBM/fp-go/function"
|
||||
"github.com/IBM/fp-go/internal/array"
|
||||
S "github.com/IBM/fp-go/semigroup"
|
||||
)
|
||||
|
||||
// NonEmptyArray represents an array with at least one element
|
||||
type NonEmptyArray[A any] []A
|
||||
|
||||
// Of constructs a single element array
|
||||
func Of[A any](first A) NonEmptyArray[A] {
|
||||
return G.Of[NonEmptyArray[A]](first)
|
||||
}
|
||||
|
||||
// From constructs a [NonEmptyArray] from a set of variadic arguments
|
||||
func From[A any](first A, data ...A) NonEmptyArray[A] {
|
||||
count := len(data)
|
||||
if count == 0 {
|
||||
return Of(first)
|
||||
}
|
||||
// allocate the requested buffer
|
||||
buffer := make(NonEmptyArray[A], count+1)
|
||||
buffer[0] = first
|
||||
copy(buffer[1:], data)
|
||||
return buffer
|
||||
}
|
||||
|
||||
func IsEmpty[A any](as NonEmptyArray[A]) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func IsNonEmpty[A any](as NonEmptyArray[A]) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func MonadMap[A, B any](as NonEmptyArray[A], f func(a A) B) NonEmptyArray[B] {
|
||||
return G.MonadMap[NonEmptyArray[A], NonEmptyArray[B]](as, f)
|
||||
}
|
||||
|
||||
func Map[A, B any](f func(a A) B) func(NonEmptyArray[A]) NonEmptyArray[B] {
|
||||
return F.Bind2nd(MonadMap[A, B], f)
|
||||
}
|
||||
|
||||
func Reduce[A, B any](f func(B, A) B, initial B) func(NonEmptyArray[A]) B {
|
||||
return func(as NonEmptyArray[A]) B {
|
||||
return array.Reduce(as, f, initial)
|
||||
}
|
||||
}
|
||||
|
||||
func Tail[A any](as NonEmptyArray[A]) []A {
|
||||
return as[1:]
|
||||
}
|
||||
|
||||
func Head[A any](as NonEmptyArray[A]) A {
|
||||
return as[0]
|
||||
}
|
||||
|
||||
func First[A any](as NonEmptyArray[A]) A {
|
||||
return as[0]
|
||||
}
|
||||
|
||||
func Last[A any](as NonEmptyArray[A]) A {
|
||||
return as[len(as)-1]
|
||||
}
|
||||
|
||||
func Size[A any](as NonEmptyArray[A]) int {
|
||||
return G.Size(as)
|
||||
}
|
||||
|
||||
func Flatten[A any](mma NonEmptyArray[NonEmptyArray[A]]) NonEmptyArray[A] {
|
||||
return G.Flatten(mma)
|
||||
}
|
||||
|
||||
func MonadChain[A, B any](fa NonEmptyArray[A], f func(a A) NonEmptyArray[B]) NonEmptyArray[B] {
|
||||
return G.MonadChain[NonEmptyArray[A], NonEmptyArray[B]](fa, f)
|
||||
}
|
||||
|
||||
func Chain[A, B any](f func(A) NonEmptyArray[B]) func(NonEmptyArray[A]) NonEmptyArray[B] {
|
||||
return G.Chain[NonEmptyArray[A], NonEmptyArray[B]](f)
|
||||
}
|
||||
|
||||
func MonadAp[B, A any](fab NonEmptyArray[func(A) B], fa NonEmptyArray[A]) NonEmptyArray[B] {
|
||||
return G.MonadAp[NonEmptyArray[B]](fab, fa)
|
||||
}
|
||||
|
||||
func Ap[B, A any](fa NonEmptyArray[A]) func(NonEmptyArray[func(A) B]) NonEmptyArray[B] {
|
||||
return G.Ap[NonEmptyArray[B], NonEmptyArray[func(A) B]](fa)
|
||||
}
|
||||
|
||||
// FoldMap maps and folds a [NonEmptyArray]. Map the [NonEmptyArray] passing each value to the iterating function. Then fold the results using the provided [Semigroup].
|
||||
func FoldMap[A, B any](s S.Semigroup[B]) func(func(A) B) func(NonEmptyArray[A]) B {
|
||||
return func(f func(A) B) func(NonEmptyArray[A]) B {
|
||||
return func(as NonEmptyArray[A]) B {
|
||||
return array.Reduce(Tail(as), func(cur B, a A) B {
|
||||
return s.Concat(cur, f(a))
|
||||
}, f(Head(as)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fold folds the [NonEmptyArray] using the provided [Semigroup].
|
||||
func Fold[A any](s S.Semigroup[A]) func(NonEmptyArray[A]) A {
|
||||
return func(as NonEmptyArray[A]) A {
|
||||
return array.Reduce(Tail(as), s.Concat, Head(as))
|
||||
}
|
||||
}
|
@@ -24,3 +24,13 @@ import (
|
||||
func Sort[T any](ord O.Ord[T]) func(ma []T) []T {
|
||||
return G.Sort[[]T](ord)
|
||||
}
|
||||
|
||||
// SortByKey implements a stable sort on the array given the provided ordering on an extracted key
|
||||
func SortByKey[K, T any](ord O.Ord[K], f func(T) K) func(ma []T) []T {
|
||||
return G.SortByKey[[]T](ord, f)
|
||||
}
|
||||
|
||||
// SortBy implements a stable sort on the array given the provided ordering
|
||||
func SortBy[T any](ord []O.Ord[T]) func(ma []T) []T {
|
||||
return G.SortBy[[]T, []O.Ord[T]](ord)
|
||||
}
|
||||
|
109
assert/assert_test.go
Normal file
109
assert/assert_test.go
Normal file
@@ -0,0 +1,109 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package assert
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
E "github.com/IBM/fp-go/either"
|
||||
EQ "github.com/IBM/fp-go/eq"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var (
|
||||
errTest = fmt.Errorf("test failure")
|
||||
|
||||
// Eq is the equal predicate checking if objects are equal
|
||||
Eq = EQ.FromEquals(assert.ObjectsAreEqual)
|
||||
)
|
||||
|
||||
func wrap1[T any](wrapped func(t assert.TestingT, expected, actual any, msgAndArgs ...any) bool, t *testing.T, expected T) func(actual T) E.Either[error, T] {
|
||||
return func(actual T) E.Either[error, T] {
|
||||
ok := wrapped(t, expected, actual)
|
||||
if ok {
|
||||
return E.Of[error](actual)
|
||||
}
|
||||
return E.Left[T](errTest)
|
||||
}
|
||||
}
|
||||
|
||||
// NotEqual tests if the expected and the actual values are not equal
|
||||
func NotEqual[T any](t *testing.T, expected T) func(actual T) E.Either[error, T] {
|
||||
return wrap1(assert.NotEqual, t, expected)
|
||||
}
|
||||
|
||||
// Equal tests if the expected and the actual values are equal
|
||||
func Equal[T any](t *testing.T, expected T) func(actual T) E.Either[error, T] {
|
||||
return wrap1(assert.Equal, t, expected)
|
||||
}
|
||||
|
||||
// Length tests if an array has the expected length
|
||||
func Length[T any](t *testing.T, expected int) func(actual []T) E.Either[error, []T] {
|
||||
return func(actual []T) E.Either[error, []T] {
|
||||
ok := assert.Len(t, actual, expected)
|
||||
if ok {
|
||||
return E.Of[error](actual)
|
||||
}
|
||||
return E.Left[[]T](errTest)
|
||||
}
|
||||
}
|
||||
|
||||
// NoError validates that there is no error
|
||||
func NoError[T any](t *testing.T) func(actual E.Either[error, T]) E.Either[error, T] {
|
||||
return func(actual E.Either[error, T]) E.Either[error, T] {
|
||||
return E.MonadFold(actual, func(e error) E.Either[error, T] {
|
||||
assert.NoError(t, e)
|
||||
return E.Left[T](e)
|
||||
}, func(value T) E.Either[error, T] {
|
||||
assert.NoError(t, nil)
|
||||
return E.Right[error](value)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// ArrayContains tests if a value is contained in an array
|
||||
func ArrayContains[T any](t *testing.T, expected T) func(actual []T) E.Either[error, []T] {
|
||||
return func(actual []T) E.Either[error, []T] {
|
||||
ok := assert.Contains(t, actual, expected)
|
||||
if ok {
|
||||
return E.Of[error](actual)
|
||||
}
|
||||
return E.Left[[]T](errTest)
|
||||
}
|
||||
}
|
||||
|
||||
// ContainsKey tests if a key is contained in a map
|
||||
func ContainsKey[T any, K comparable](t *testing.T, expected K) func(actual map[K]T) E.Either[error, map[K]T] {
|
||||
return func(actual map[K]T) E.Either[error, map[K]T] {
|
||||
ok := assert.Contains(t, actual, expected)
|
||||
if ok {
|
||||
return E.Of[error](actual)
|
||||
}
|
||||
return E.Left[map[K]T](errTest)
|
||||
}
|
||||
}
|
||||
|
||||
// NotContainsKey tests if a key is not contained in a map
|
||||
func NotContainsKey[T any, K comparable](t *testing.T, expected K) func(actual map[K]T) E.Either[error, map[K]T] {
|
||||
return func(actual map[K]T) E.Either[error, map[K]T] {
|
||||
ok := assert.NotContains(t, actual, expected)
|
||||
if ok {
|
||||
return E.Of[error](actual)
|
||||
}
|
||||
return E.Left[map[K]T](errTest)
|
||||
}
|
||||
}
|
59
boolean/boolean.go
Normal file
59
boolean/boolean.go
Normal file
@@ -0,0 +1,59 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package boolean
|
||||
|
||||
import (
|
||||
EQ "github.com/IBM/fp-go/eq"
|
||||
M "github.com/IBM/fp-go/monoid"
|
||||
O "github.com/IBM/fp-go/ord"
|
||||
)
|
||||
|
||||
var (
|
||||
// MonoidAny is the boolean [M.Monoid] under disjunction
|
||||
MonoidAny = M.MakeMonoid(
|
||||
func(l, r bool) bool {
|
||||
return l || r
|
||||
},
|
||||
false,
|
||||
)
|
||||
|
||||
// MonoidAll is the boolean [M.Monoid] under conjuction
|
||||
MonoidAll = M.MakeMonoid(
|
||||
func(l, r bool) bool {
|
||||
return l && r
|
||||
},
|
||||
true,
|
||||
)
|
||||
|
||||
// Eq is the equals predicate for boolean
|
||||
Eq = EQ.FromStrictEquals[bool]()
|
||||
|
||||
// Ord is the strict ordering for boolean
|
||||
Ord = O.MakeOrd(func(l, r bool) int {
|
||||
if l {
|
||||
if r {
|
||||
return 0
|
||||
}
|
||||
return +1
|
||||
}
|
||||
if r {
|
||||
return -1
|
||||
}
|
||||
return 0
|
||||
}, func(l, r bool) bool {
|
||||
return l == r
|
||||
})
|
||||
)
|
@@ -388,7 +388,7 @@ func generateApplyHelpers(filename string, count int) error {
|
||||
// some header
|
||||
fmt.Fprintln(f, "// Code generated by go generate; DO NOT EDIT.")
|
||||
fmt.Fprintln(f, "// This file was generated by robots at")
|
||||
fmt.Fprintf(f, "// %s\n", time.Now())
|
||||
fmt.Fprintf(f, "// %s\n\n", time.Now())
|
||||
|
||||
fmt.Fprintf(f, "package %s\n\n", pkg)
|
||||
|
||||
|
@@ -266,7 +266,7 @@ func generateBindHelpers(filename string, count int) error {
|
||||
// some header
|
||||
fmt.Fprintln(f, "// Code generated by go generate; DO NOT EDIT.")
|
||||
fmt.Fprintln(f, "// This file was generated by robots at")
|
||||
fmt.Fprintf(f, "// %s\n", time.Now())
|
||||
fmt.Fprintf(f, "// %s\n\n", time.Now())
|
||||
|
||||
fmt.Fprintf(f, "package %s\n", pkg)
|
||||
|
||||
|
@@ -115,16 +115,14 @@ func generateEitherize(f *os.File, i int) {
|
||||
fmt.Fprintf(f, "t%d T%d", j, j)
|
||||
}
|
||||
fmt.Fprintf(f, ") Either[error, R] {\n")
|
||||
fmt.Fprintf(f, " return TryCatchError(func() (R, error) {\n")
|
||||
fmt.Fprintf(f, " return f(")
|
||||
fmt.Fprintf(f, " return TryCatchError(f(")
|
||||
for j := 0; j < i; j++ {
|
||||
if j > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "t%d", j)
|
||||
}
|
||||
fmt.Fprintln(f, ")")
|
||||
fmt.Fprintln(f, " })")
|
||||
fmt.Fprintln(f, "))")
|
||||
fmt.Fprintln(f, " }")
|
||||
fmt.Fprintln(f, "}")
|
||||
}
|
||||
@@ -150,7 +148,7 @@ func generateEitherHelpers(filename string, count int) error {
|
||||
// some header
|
||||
fmt.Fprintln(f, "// Code generated by go generate; DO NOT EDIT.")
|
||||
fmt.Fprintln(f, "// This file was generated by robots at")
|
||||
fmt.Fprintf(f, "// %s\n", time.Now())
|
||||
fmt.Fprintf(f, "// %s\n\n", time.Now())
|
||||
|
||||
fmt.Fprintf(f, "package %s\n\n", pkg)
|
||||
|
||||
|
@@ -27,5 +27,5 @@ func writePackage(f *os.File, pkg string) {
|
||||
// some header
|
||||
fmt.Fprintln(f, "// Code generated by go generate; DO NOT EDIT.")
|
||||
fmt.Fprintln(f, "// This file was generated by robots at")
|
||||
fmt.Fprintf(f, "// %s\n", time.Now())
|
||||
fmt.Fprintf(f, "// %s\n\n", time.Now())
|
||||
}
|
||||
|
@@ -62,7 +62,7 @@ func generateIdentityHelpers(filename string, count int) error {
|
||||
// some header
|
||||
fmt.Fprintln(f, "// Code generated by go generate; DO NOT EDIT.")
|
||||
fmt.Fprintln(f, "// This file was generated by robots at")
|
||||
fmt.Fprintf(f, "// %s\n", time.Now())
|
||||
fmt.Fprintf(f, "// %s\n\n", time.Now())
|
||||
|
||||
fmt.Fprintf(f, "package %s\n\n", pkg)
|
||||
|
||||
|
@@ -81,7 +81,7 @@ func generateIOHelpers(filename string, count int) error {
|
||||
// some header
|
||||
fmt.Fprintln(f, "// Code generated by go generate; DO NOT EDIT.")
|
||||
fmt.Fprintln(f, "// This file was generated by robots at")
|
||||
fmt.Fprintf(f, "// %s\n", time.Now())
|
||||
fmt.Fprintf(f, "// %s\n\n", time.Now())
|
||||
|
||||
fmt.Fprintf(f, "package %s\n\n", pkg)
|
||||
|
||||
|
@@ -217,7 +217,7 @@ func generateIOEitherHelpers(filename string, count int) error {
|
||||
// some header
|
||||
fmt.Fprintln(f, "// Code generated by go generate; DO NOT EDIT.")
|
||||
fmt.Fprintln(f, "// This file was generated by robots at")
|
||||
fmt.Fprintf(f, "// %s\n", time.Now())
|
||||
fmt.Fprintf(f, "// %s\n\n", time.Now())
|
||||
|
||||
fmt.Fprintf(f, "package %s\n\n", pkg)
|
||||
|
||||
|
@@ -81,7 +81,7 @@ func generateIOOptionHelpers(filename string, count int) error {
|
||||
// some header
|
||||
fmt.Fprintln(f, "// Code generated by go generate; DO NOT EDIT.")
|
||||
fmt.Fprintln(f, "// This file was generated by robots at")
|
||||
fmt.Fprintf(f, "// %s\n", time.Now())
|
||||
fmt.Fprintf(f, "// %s\n\n", time.Now())
|
||||
|
||||
fmt.Fprintf(f, "package %s\n\n", pkg)
|
||||
|
||||
|
@@ -148,7 +148,7 @@ func generateOptionHelpers(filename string, count int) error {
|
||||
// some header
|
||||
fmt.Fprintln(f, "// Code generated by go generate; DO NOT EDIT.")
|
||||
fmt.Fprintln(f, "// This file was generated by robots at")
|
||||
fmt.Fprintf(f, "// %s\n", time.Now())
|
||||
fmt.Fprintf(f, "// %s\n\n", time.Now())
|
||||
|
||||
fmt.Fprintf(f, "package %s\n\n", pkg)
|
||||
|
||||
|
44
cli/pipe.go
44
cli/pipe.go
@@ -25,6 +25,40 @@ import (
|
||||
C "github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func generateUnsliced(f *os.File, i int) {
|
||||
// Create the optionize version
|
||||
fmt.Fprintf(f, "\n// Unsliced%d converts a function taking a slice parameter into a function with %d parameters\n", i, i)
|
||||
fmt.Fprintf(f, "func Unsliced%d[F ~func([]T) R, T, R any](f F) func(", i)
|
||||
for j := 0; j < i; j++ {
|
||||
if j > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "T")
|
||||
}
|
||||
fmt.Fprintf(f, ") R {\n")
|
||||
fmt.Fprintf(f, " return func(")
|
||||
for j := 0; j < i; j++ {
|
||||
if j > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "t%d", j+1)
|
||||
}
|
||||
if i > 0 {
|
||||
fmt.Fprintf(f, " T")
|
||||
}
|
||||
fmt.Fprintf(f, ") R {\n")
|
||||
fmt.Fprintf(f, " return f([]T{")
|
||||
for j := 0; j < i; j++ {
|
||||
if j > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "t%d", j+1)
|
||||
}
|
||||
fmt.Fprintln(f, "})")
|
||||
fmt.Fprintln(f, " }")
|
||||
fmt.Fprintln(f, "}")
|
||||
}
|
||||
|
||||
func generateVariadic(f *os.File, i int) {
|
||||
// Create the nullary version
|
||||
fmt.Fprintf(f, "\n// Variadic%d converts a function taking %d parameters and a final slice into a function with %d parameters but a final variadic argument\n", i, i, i)
|
||||
@@ -83,7 +117,7 @@ func generateVariadic(f *os.File, i int) {
|
||||
fmt.Fprintf(f, "v)\n")
|
||||
fmt.Fprintf(f, " }\n")
|
||||
|
||||
fmt.Fprintf(f, "}")
|
||||
fmt.Fprintf(f, "}\n")
|
||||
}
|
||||
|
||||
func generateUnvariadic(f *os.File, i int) {
|
||||
@@ -144,7 +178,7 @@ func generateUnvariadic(f *os.File, i int) {
|
||||
fmt.Fprintf(f, "v...)\n")
|
||||
fmt.Fprintf(f, " }\n")
|
||||
|
||||
fmt.Fprintf(f, "}")
|
||||
fmt.Fprintf(f, "}\n")
|
||||
}
|
||||
|
||||
func generateNullary(f *os.File, i int) {
|
||||
@@ -337,7 +371,7 @@ func generatePipeHelpers(filename string, count int) error {
|
||||
// some header
|
||||
fmt.Fprintln(f, "// Code generated by go generate; DO NOT EDIT.")
|
||||
fmt.Fprintln(f, "// This file was generated by robots at")
|
||||
fmt.Fprintf(f, "// %s\n", time.Now())
|
||||
fmt.Fprintf(f, "// %s\n\n", time.Now())
|
||||
|
||||
fmt.Fprintf(f, "package %s\n", pkg)
|
||||
|
||||
@@ -347,6 +381,8 @@ func generatePipeHelpers(filename string, count int) error {
|
||||
generateVariadic(f, 0)
|
||||
// unvariadic
|
||||
generateUnvariadic(f, 0)
|
||||
// unsliced
|
||||
generateUnsliced(f, 0)
|
||||
|
||||
for i := 1; i <= count; i++ {
|
||||
|
||||
@@ -364,6 +400,8 @@ func generatePipeHelpers(filename string, count int) error {
|
||||
generateVariadic(f, i)
|
||||
// unvariadic
|
||||
generateUnvariadic(f, i)
|
||||
// unsliced
|
||||
generateUnsliced(f, i)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@@ -12,6 +12,7 @@
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package cli
|
||||
|
||||
import (
|
||||
@@ -117,7 +118,7 @@ func generateReaderHelpers(filename string, count int) error {
|
||||
// some header
|
||||
fmt.Fprintln(f, "// Code generated by go generate; DO NOT EDIT.")
|
||||
fmt.Fprintln(f, "// This file was generated by robots at")
|
||||
fmt.Fprintf(f, "// %s\n", time.Now())
|
||||
fmt.Fprintf(f, "// %s\n\n", time.Now())
|
||||
|
||||
fmt.Fprintf(f, "package %s\n\n", pkg)
|
||||
|
||||
|
@@ -169,7 +169,7 @@ func generateReaderIOEitherHelpers(filename string, count int) error {
|
||||
// some header
|
||||
fmt.Fprintln(f, "// Code generated by go generate; DO NOT EDIT.")
|
||||
fmt.Fprintln(f, "// This file was generated by robots at")
|
||||
fmt.Fprintf(f, "// %s\n", time.Now())
|
||||
fmt.Fprintf(f, "// %s\n\n", time.Now())
|
||||
|
||||
fmt.Fprintf(f, "package %s\n\n", pkg)
|
||||
|
||||
|
54
cli/tuple.go
54
cli/tuple.go
@@ -20,6 +20,7 @@ import (
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
C "github.com/urfave/cli/v2"
|
||||
@@ -36,8 +37,51 @@ func writeTupleType(f *os.File, symbol string, i int) {
|
||||
fmt.Fprintf(f, "]")
|
||||
}
|
||||
|
||||
func makeTupleType(name string) func(i int) string {
|
||||
return func(i int) string {
|
||||
var buf strings.Builder
|
||||
buf.WriteString(fmt.Sprintf("Tuple%d[", i))
|
||||
for j := 0; j < i; j++ {
|
||||
if j > 0 {
|
||||
buf.WriteString(", ")
|
||||
}
|
||||
buf.WriteString(fmt.Sprintf("%s%d", name, j+1))
|
||||
}
|
||||
buf.WriteString("]")
|
||||
|
||||
return buf.String()
|
||||
}
|
||||
}
|
||||
|
||||
func generatePush(f *os.File, i int) {
|
||||
tuple1 := makeTupleType("T")(i)
|
||||
tuple2 := makeTupleType("T")(i + 1)
|
||||
// Create the replicate version
|
||||
fmt.Fprintf(f, "\n// Push%d creates a [Tuple%d] from a [Tuple%d] by appending a constant value\n", i, i+1, i)
|
||||
fmt.Fprintf(f, "func Push%d[", i)
|
||||
// function prototypes
|
||||
for j := 0; j <= i; j++ {
|
||||
if j > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "T%d", j+1)
|
||||
}
|
||||
fmt.Fprintf(f, " any](value T%d) func(%s) %s {\n", i+1, tuple1, tuple2)
|
||||
fmt.Fprintf(f, " return func(t %s) %s {\n", tuple1, tuple2)
|
||||
fmt.Fprintf(f, " return MakeTuple%d(", i+1)
|
||||
for j := 0; j < i; j++ {
|
||||
if j > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "t.F%d", j+1)
|
||||
}
|
||||
fmt.Fprintf(f, ", value)\n")
|
||||
fmt.Fprintf(f, " }\n")
|
||||
fmt.Fprintf(f, "}\n")
|
||||
}
|
||||
|
||||
func generateReplicate(f *os.File, i int) {
|
||||
// Create the optionize version
|
||||
// Create the replicate version
|
||||
fmt.Fprintf(f, "\n// Replicate%d creates a [Tuple%d] with all fields set to the input value `t`\n", i, i)
|
||||
fmt.Fprintf(f, "func Replicate%d[T any](t T) Tuple%d[", i, i)
|
||||
for j := 1; j <= i; j++ {
|
||||
@@ -294,7 +338,7 @@ func generateUntupled(f *os.File, i int) {
|
||||
|
||||
func generateTupled(f *os.File, i int) {
|
||||
// Create the optionize version
|
||||
fmt.Fprintf(f, "\n// Tupled%d converts a function with %d parameters returning into a function taking a Tuple%d\n// The inverse function is [Untupled%d]\n", i, i, i, i)
|
||||
fmt.Fprintf(f, "\n// Tupled%d converts a function with %d parameters into a function taking a Tuple%d\n// The inverse function is [Untupled%d]\n", i, i, i, i)
|
||||
fmt.Fprintf(f, "func Tupled%d[F ~func(", i)
|
||||
for j := 0; j < i; j++ {
|
||||
if j > 0 {
|
||||
@@ -355,7 +399,7 @@ func generateTupleHelpers(filename string, count int) error {
|
||||
// some header
|
||||
fmt.Fprintln(f, "// Code generated by go generate; DO NOT EDIT.")
|
||||
fmt.Fprintln(f, "// This file was generated by robots at")
|
||||
fmt.Fprintf(f, "// %s\n", time.Now())
|
||||
fmt.Fprintf(f, "// %s\n\n", time.Now())
|
||||
|
||||
fmt.Fprintf(f, "package %s\n\n", pkg)
|
||||
|
||||
@@ -398,6 +442,10 @@ import (
|
||||
generateToArray(f, i)
|
||||
// generate fromArray
|
||||
generateFromArray(f, i)
|
||||
// generate push
|
||||
if i < count {
|
||||
generatePush(f, i)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@@ -19,15 +19,14 @@ import (
|
||||
"context"
|
||||
|
||||
E "github.com/IBM/fp-go/either"
|
||||
ET "github.com/IBM/fp-go/either"
|
||||
IOE "github.com/IBM/fp-go/ioeither/generic"
|
||||
)
|
||||
|
||||
// withContext wraps an existing IOEither and performs a context check for cancellation before delegating
|
||||
// WithContext wraps an existing IOEither and performs a context check for cancellation before delegating
|
||||
func WithContext[GIO ~func() E.Either[error, A], A any](ctx context.Context, ma GIO) GIO {
|
||||
return IOE.MakeIO[GIO](func() E.Either[error, A] {
|
||||
if err := context.Cause(ctx); err != nil {
|
||||
return ET.Left[A](err)
|
||||
return E.Left[A](err)
|
||||
}
|
||||
return ma()
|
||||
})
|
||||
|
@@ -1,53 +0,0 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package reader
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
R "github.com/IBM/fp-go/reader/generic"
|
||||
)
|
||||
|
||||
// these functions curry a golang function with the context as the firsr parameter into a either reader with the context as the last parameter
|
||||
// this goes back to the advice in https://pkg.go.dev/context to put the context as a first parameter as a convention
|
||||
|
||||
func Curry0[A any](f func(context.Context) A) Reader[A] {
|
||||
return R.Curry0[Reader[A]](f)
|
||||
}
|
||||
|
||||
func Curry1[T1, A any](f func(context.Context, T1) A) func(T1) Reader[A] {
|
||||
return R.Curry1[Reader[A]](f)
|
||||
}
|
||||
|
||||
func Curry2[T1, T2, A any](f func(context.Context, T1, T2) A) func(T1) func(T2) Reader[A] {
|
||||
return R.Curry2[Reader[A]](f)
|
||||
}
|
||||
|
||||
func Curry3[T1, T2, T3, A any](f func(context.Context, T1, T2, T3) A) func(T1) func(T2) func(T3) Reader[A] {
|
||||
return R.Curry3[Reader[A]](f)
|
||||
}
|
||||
|
||||
func Uncurry1[T1, A any](f func(T1) Reader[A]) func(context.Context, T1) A {
|
||||
return R.Uncurry1(f)
|
||||
}
|
||||
|
||||
func Uncurry2[T1, T2, A any](f func(T1) func(T2) Reader[A]) func(context.Context, T1, T2) A {
|
||||
return R.Uncurry2(f)
|
||||
}
|
||||
|
||||
func Uncurry3[T1, T2, T3, A any](f func(T1) func(T2) func(T3) Reader[A]) func(context.Context, T1, T2, T3) A {
|
||||
return R.Uncurry3(f)
|
||||
}
|
@@ -1,41 +0,0 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package reader
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
R "github.com/IBM/fp-go/reader/generic"
|
||||
)
|
||||
|
||||
// these functions curry a golang function with the context as the firsr parameter into a either reader with the context as the last parameter
|
||||
// this goes back to the advice in https://pkg.go.dev/context to put the context as a first parameter as a convention
|
||||
|
||||
func From0[A any](f func(context.Context) A) func() Reader[A] {
|
||||
return R.From0[Reader[A]](f)
|
||||
}
|
||||
|
||||
func From1[T1, A any](f func(context.Context, T1) A) func(T1) Reader[A] {
|
||||
return R.From1[Reader[A]](f)
|
||||
}
|
||||
|
||||
func From2[T1, T2, A any](f func(context.Context, T1, T2) A) func(T1, T2) Reader[A] {
|
||||
return R.From2[Reader[A]](f)
|
||||
}
|
||||
|
||||
func From3[T1, T2, T3, A any](f func(context.Context, T1, T2, T3) A) func(T1, T2, T3) Reader[A] {
|
||||
return R.From3[Reader[A]](f)
|
||||
}
|
@@ -1,58 +0,0 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package reader
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
R "github.com/IBM/fp-go/reader/generic"
|
||||
)
|
||||
|
||||
func MonadMap[A, B any](fa Reader[A], f func(A) B) Reader[B] {
|
||||
return R.MonadMap[Reader[A], Reader[B]](fa, f)
|
||||
}
|
||||
|
||||
func Map[A, B any](f func(A) B) func(Reader[A]) Reader[B] {
|
||||
return R.Map[Reader[A], Reader[B]](f)
|
||||
}
|
||||
|
||||
func MonadChain[A, B any](ma Reader[A], f func(A) Reader[B]) Reader[B] {
|
||||
return R.MonadChain(ma, f)
|
||||
}
|
||||
|
||||
func Chain[A, B any](f func(A) Reader[B]) func(Reader[A]) Reader[B] {
|
||||
return R.Chain[Reader[A]](f)
|
||||
}
|
||||
|
||||
func Of[A any](a A) Reader[A] {
|
||||
return R.Of[Reader[A]](a)
|
||||
}
|
||||
|
||||
func MonadAp[A, B any](fab Reader[func(A) B], fa Reader[A]) Reader[B] {
|
||||
return R.MonadAp[Reader[A], Reader[B]](fab, fa)
|
||||
}
|
||||
|
||||
func Ap[A, B any](fa Reader[A]) func(Reader[func(A) B]) Reader[B] {
|
||||
return R.Ap[Reader[A], Reader[B], Reader[func(A) B]](fa)
|
||||
}
|
||||
|
||||
func Ask() Reader[context.Context] {
|
||||
return R.Ask[Reader[context.Context]]()
|
||||
}
|
||||
|
||||
func Asks[A any](r Reader[A]) Reader[A] {
|
||||
return R.Asks(r)
|
||||
}
|
@@ -1,75 +0,0 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package reader
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
F "github.com/IBM/fp-go/function"
|
||||
T "github.com/IBM/fp-go/tuple"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func GoFunction(ctx context.Context, data string) string {
|
||||
return strings.ToUpper(data)
|
||||
}
|
||||
|
||||
func GoIntFunction(ctx context.Context, data string, number int) string {
|
||||
return fmt.Sprintf("%s: %d", data, number)
|
||||
}
|
||||
|
||||
func TestReaderFrom(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
f := From1(GoFunction)
|
||||
|
||||
result := f("input")(ctx)
|
||||
|
||||
assert.Equal(t, result, "INPUT")
|
||||
|
||||
}
|
||||
|
||||
func MyFinalResult(left, right string) string {
|
||||
return fmt.Sprintf("%s-%s", left, right)
|
||||
}
|
||||
|
||||
func TestReadersFrom(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
f1 := From1(GoFunction)
|
||||
f2 := From2(GoIntFunction)
|
||||
|
||||
result1 := f1("input")(ctx)
|
||||
result2 := f2("input", 10)(ctx)
|
||||
|
||||
result3 := MyFinalResult(result1, result2)
|
||||
|
||||
h := F.Pipe1(
|
||||
SequenceT2(f1("input"), f2("input", 10)),
|
||||
Map(T.Tupled2(MyFinalResult)),
|
||||
)
|
||||
|
||||
composedResult := h(ctx)
|
||||
|
||||
assert.Equal(t, result1, "INPUT")
|
||||
assert.Equal(t, result2, "input: 10")
|
||||
assert.Equal(t, result3, "INPUT-input: 10")
|
||||
|
||||
assert.Equal(t, composedResult, "INPUT-input: 10")
|
||||
|
||||
}
|
@@ -1,57 +0,0 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package reader
|
||||
|
||||
import (
|
||||
R "github.com/IBM/fp-go/reader/generic"
|
||||
T "github.com/IBM/fp-go/tuple"
|
||||
)
|
||||
|
||||
// SequenceT converts n inputs of higher kinded types into a higher kinded types of n strongly typed values, represented as a tuple
|
||||
|
||||
func SequenceT1[A any](a Reader[A]) Reader[T.Tuple1[A]] {
|
||||
return R.SequenceT1[
|
||||
Reader[A],
|
||||
Reader[T.Tuple1[A]],
|
||||
](a)
|
||||
}
|
||||
|
||||
func SequenceT2[A, B any](a Reader[A], b Reader[B]) Reader[T.Tuple2[A, B]] {
|
||||
return R.SequenceT2[
|
||||
Reader[A],
|
||||
Reader[B],
|
||||
Reader[T.Tuple2[A, B]],
|
||||
](a, b)
|
||||
}
|
||||
|
||||
func SequenceT3[A, B, C any](a Reader[A], b Reader[B], c Reader[C]) Reader[T.Tuple3[A, B, C]] {
|
||||
return R.SequenceT3[
|
||||
Reader[A],
|
||||
Reader[B],
|
||||
Reader[C],
|
||||
Reader[T.Tuple3[A, B, C]],
|
||||
](a, b, c)
|
||||
}
|
||||
|
||||
func SequenceT4[A, B, C, D any](a Reader[A], b Reader[B], c Reader[C], d Reader[D]) Reader[T.Tuple4[A, B, C, D]] {
|
||||
return R.SequenceT4[
|
||||
Reader[A],
|
||||
Reader[B],
|
||||
Reader[C],
|
||||
Reader[D],
|
||||
Reader[T.Tuple4[A, B, C, D]],
|
||||
](a, b, c, d)
|
||||
}
|
@@ -24,6 +24,11 @@ func TraverseArray[A, B any](f func(A) ReaderEither[B]) func([]A) ReaderEither[[
|
||||
return RE.TraverseArray[ReaderEither[B], ReaderEither[[]B], []A](f)
|
||||
}
|
||||
|
||||
// TraverseArrayWithIndex transforms an array
|
||||
func TraverseArrayWithIndex[A, B any](f func(int, A) ReaderEither[B]) func([]A) ReaderEither[[]B] {
|
||||
return RE.TraverseArrayWithIndex[ReaderEither[B], ReaderEither[[]B], []A](f)
|
||||
}
|
||||
|
||||
// SequenceArray converts a homogeneous sequence of either into an either of sequence
|
||||
func SequenceArray[A any](ma []ReaderEither[A]) ReaderEither[[]A] {
|
||||
return RE.SequenceArray[ReaderEither[A], ReaderEither[[]A]](ma)
|
||||
|
@@ -34,8 +34,6 @@ var (
|
||||
|
||||
func command(name string, args []string, in []byte) RE.ReaderEither[exec.CommandOutput] {
|
||||
return func(ctx context.Context) E.Either[error, exec.CommandOutput] {
|
||||
return E.TryCatchError(func() (exec.CommandOutput, error) {
|
||||
return GE.Exec(ctx, name, args, in)
|
||||
})
|
||||
return E.TryCatchError(GE.Exec(ctx, name, args, in))
|
||||
}
|
||||
}
|
||||
|
@@ -18,7 +18,6 @@ package readereither
|
||||
import (
|
||||
"context"
|
||||
|
||||
R "github.com/IBM/fp-go/context/reader"
|
||||
ET "github.com/IBM/fp-go/either"
|
||||
O "github.com/IBM/fp-go/option"
|
||||
RE "github.com/IBM/fp-go/readereither/generic"
|
||||
@@ -32,14 +31,6 @@ func FromEither[A any](e ET.Either[error, A]) ReaderEither[A] {
|
||||
return RE.FromEither[ReaderEither[A]](e)
|
||||
}
|
||||
|
||||
func RightReader[A any](r R.Reader[A]) ReaderEither[A] {
|
||||
return RE.RightReader[R.Reader[A], ReaderEither[A]](r)
|
||||
}
|
||||
|
||||
func LeftReader[A any](l R.Reader[error]) ReaderEither[A] {
|
||||
return RE.LeftReader[R.Reader[error], ReaderEither[A]](l)
|
||||
}
|
||||
|
||||
func Left[A any](l error) ReaderEither[A] {
|
||||
return RE.Left[ReaderEither[A]](l)
|
||||
}
|
||||
@@ -48,10 +39,6 @@ func Right[A any](r A) ReaderEither[A] {
|
||||
return RE.Right[ReaderEither[A]](r)
|
||||
}
|
||||
|
||||
func FromReader[A any](r R.Reader[A]) ReaderEither[A] {
|
||||
return RE.FromReader[R.Reader[A], ReaderEither[A]](r)
|
||||
}
|
||||
|
||||
func MonadMap[A, B any](fa ReaderEither[A], f func(A) B) ReaderEither[B] {
|
||||
return RE.MonadMap[ReaderEither[A], ReaderEither[B]](fa, f)
|
||||
}
|
||||
@@ -84,30 +71,14 @@ func FromPredicate[A any](pred func(A) bool, onFalse func(A) error) func(A) Read
|
||||
return RE.FromPredicate[ReaderEither[A]](pred, onFalse)
|
||||
}
|
||||
|
||||
func Fold[A, B any](onLeft func(error) R.Reader[B], onRight func(A) R.Reader[B]) func(ReaderEither[A]) R.Reader[B] {
|
||||
return RE.Fold[ReaderEither[A]](onLeft, onRight)
|
||||
}
|
||||
|
||||
func GetOrElse[A any](onLeft func(error) R.Reader[A]) func(ReaderEither[A]) R.Reader[A] {
|
||||
return RE.GetOrElse[ReaderEither[A]](onLeft)
|
||||
}
|
||||
|
||||
func OrElse[A any](onLeft func(error) ReaderEither[A]) func(ReaderEither[A]) ReaderEither[A] {
|
||||
return RE.OrElse[ReaderEither[A]](onLeft)
|
||||
}
|
||||
|
||||
func OrLeft[A any](onLeft func(error) R.Reader[error]) func(ReaderEither[A]) ReaderEither[A] {
|
||||
return RE.OrLeft[ReaderEither[A], ReaderEither[A]](onLeft)
|
||||
}
|
||||
|
||||
func Ask() ReaderEither[context.Context] {
|
||||
return RE.Ask[ReaderEither[context.Context]]()
|
||||
}
|
||||
|
||||
func Asks[A any](r R.Reader[A]) ReaderEither[A] {
|
||||
return RE.Asks[R.Reader[A], ReaderEither[A]](r)
|
||||
}
|
||||
|
||||
func MonadChainEitherK[A, B any](ma ReaderEither[A], f func(A) ET.Either[error, B]) ReaderEither[B] {
|
||||
return RE.MonadChainEitherK[ReaderEither[A], ReaderEither[B]](ma, f)
|
||||
}
|
||||
@@ -119,3 +90,11 @@ func ChainEitherK[A, B any](f func(A) ET.Either[error, B]) func(ma ReaderEither[
|
||||
func ChainOptionK[A, B any](onNone func() error) func(func(A) O.Option[B]) func(ReaderEither[A]) ReaderEither[B] {
|
||||
return RE.ChainOptionK[ReaderEither[A], ReaderEither[B]](onNone)
|
||||
}
|
||||
|
||||
func MonadFlap[B, A any](fab ReaderEither[func(A) B], a A) ReaderEither[B] {
|
||||
return RE.MonadFlap[ReaderEither[func(A) B], ReaderEither[B]](fab, a)
|
||||
}
|
||||
|
||||
func Flap[B, A any](a A) func(ReaderEither[func(A) B]) ReaderEither[B] {
|
||||
return RE.Flap[ReaderEither[func(A) B], ReaderEither[B]](a)
|
||||
}
|
||||
|
@@ -1,42 +0,0 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package readerio
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
IO "github.com/IBM/fp-go/io"
|
||||
R "github.com/IBM/fp-go/readerio/generic"
|
||||
)
|
||||
|
||||
// these functions curry a golang function with the context as the firsr parameter into a either reader with the context as the last parameter
|
||||
// this goes back to the advice in https://pkg.go.dev/context to put the context as a first parameter as a convention
|
||||
|
||||
func From0[A any](f func(context.Context) IO.IO[A]) func() ReaderIO[A] {
|
||||
return R.From0[ReaderIO[A]](f)
|
||||
}
|
||||
|
||||
func From1[T1, A any](f func(context.Context, T1) IO.IO[A]) func(T1) ReaderIO[A] {
|
||||
return R.From1[ReaderIO[A]](f)
|
||||
}
|
||||
|
||||
func From2[T1, T2, A any](f func(context.Context, T1, T2) IO.IO[A]) func(T1, T2) ReaderIO[A] {
|
||||
return R.From2[ReaderIO[A]](f)
|
||||
}
|
||||
|
||||
func From3[T1, T2, T3, A any](f func(context.Context, T1, T2, T3) IO.IO[A]) func(T1, T2, T3) ReaderIO[A] {
|
||||
return R.From3[ReaderIO[A]](f)
|
||||
}
|
@@ -1,59 +0,0 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package readerio
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
R "github.com/IBM/fp-go/readerio/generic"
|
||||
)
|
||||
|
||||
func MonadMap[A, B any](fa ReaderIO[A], f func(A) B) ReaderIO[B] {
|
||||
return R.MonadMap[ReaderIO[A], ReaderIO[B]](fa, f)
|
||||
}
|
||||
|
||||
func Map[A, B any](f func(A) B) func(ReaderIO[A]) ReaderIO[B] {
|
||||
return R.Map[ReaderIO[A], ReaderIO[B]](f)
|
||||
}
|
||||
|
||||
func MonadChain[A, B any](ma ReaderIO[A], f func(A) ReaderIO[B]) ReaderIO[B] {
|
||||
return R.MonadChain(ma, f)
|
||||
}
|
||||
|
||||
func Chain[A, B any](f func(A) ReaderIO[B]) func(ReaderIO[A]) ReaderIO[B] {
|
||||
return R.Chain[ReaderIO[A]](f)
|
||||
}
|
||||
|
||||
func Of[A any](a A) ReaderIO[A] {
|
||||
return R.Of[ReaderIO[A]](a)
|
||||
}
|
||||
|
||||
func MonadAp[A, B any](fab ReaderIO[func(A) B], fa ReaderIO[A]) ReaderIO[B] {
|
||||
return R.MonadAp[ReaderIO[A], ReaderIO[B]](fab, fa)
|
||||
}
|
||||
|
||||
func Ap[A, B any](fa ReaderIO[A]) func(ReaderIO[func(A) B]) ReaderIO[B] {
|
||||
return R.Ap[ReaderIO[A], ReaderIO[B], ReaderIO[func(A) B]](fa)
|
||||
}
|
||||
|
||||
func Ask() ReaderIO[context.Context] {
|
||||
return R.Ask[ReaderIO[context.Context]]()
|
||||
}
|
||||
|
||||
// Defer creates an IO by creating a brand new IO via a generator function, each time
|
||||
func Defer[A any](gen func() ReaderIO[A]) ReaderIO[A] {
|
||||
return R.Defer[ReaderIO[A]](gen)
|
||||
}
|
@@ -1,80 +0,0 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package readerio
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
F "github.com/IBM/fp-go/function"
|
||||
IO "github.com/IBM/fp-go/io"
|
||||
T "github.com/IBM/fp-go/tuple"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func GoFunction(ctx context.Context, data string) IO.IO[string] {
|
||||
return func() string {
|
||||
return strings.ToUpper(data)
|
||||
}
|
||||
}
|
||||
|
||||
func GoIntFunction(ctx context.Context, data string, number int) IO.IO[string] {
|
||||
return func() string {
|
||||
return fmt.Sprintf("%s: %d", data, number)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReaderFrom(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
f := From1(GoFunction)
|
||||
|
||||
result := f("input")(ctx)
|
||||
|
||||
assert.Equal(t, result(), "INPUT")
|
||||
|
||||
}
|
||||
|
||||
func MyFinalResult(left, right string) string {
|
||||
return fmt.Sprintf("%s-%s", left, right)
|
||||
}
|
||||
|
||||
func TestReadersFrom(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
f1 := From1(GoFunction)
|
||||
f2 := From2(GoIntFunction)
|
||||
|
||||
result1 := f1("input")(ctx)
|
||||
result2 := f2("input", 10)(ctx)
|
||||
|
||||
result3 := MyFinalResult(result1(), result2())
|
||||
|
||||
h := F.Pipe1(
|
||||
SequenceT2(f1("input"), f2("input", 10)),
|
||||
Map(T.Tupled2(MyFinalResult)),
|
||||
)
|
||||
|
||||
composedResult := h(ctx)
|
||||
|
||||
assert.Equal(t, result1(), "INPUT")
|
||||
assert.Equal(t, result2(), "input: 10")
|
||||
assert.Equal(t, result3, "INPUT-input: 10")
|
||||
|
||||
assert.Equal(t, composedResult(), "INPUT-input: 10")
|
||||
|
||||
}
|
@@ -1,57 +0,0 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package readerio
|
||||
|
||||
import (
|
||||
R "github.com/IBM/fp-go/readerio/generic"
|
||||
T "github.com/IBM/fp-go/tuple"
|
||||
)
|
||||
|
||||
// SequenceT converts n inputs of higher kinded types into a higher kinded types of n strongly typed values, represented as a tuple
|
||||
|
||||
func SequenceT1[A any](a ReaderIO[A]) ReaderIO[T.Tuple1[A]] {
|
||||
return R.SequenceT1[
|
||||
ReaderIO[A],
|
||||
ReaderIO[T.Tuple1[A]],
|
||||
](a)
|
||||
}
|
||||
|
||||
func SequenceT2[A, B any](a ReaderIO[A], b ReaderIO[B]) ReaderIO[T.Tuple2[A, B]] {
|
||||
return R.SequenceT2[
|
||||
ReaderIO[A],
|
||||
ReaderIO[B],
|
||||
ReaderIO[T.Tuple2[A, B]],
|
||||
](a, b)
|
||||
}
|
||||
|
||||
func SequenceT3[A, B, C any](a ReaderIO[A], b ReaderIO[B], c ReaderIO[C]) ReaderIO[T.Tuple3[A, B, C]] {
|
||||
return R.SequenceT3[
|
||||
ReaderIO[A],
|
||||
ReaderIO[B],
|
||||
ReaderIO[C],
|
||||
ReaderIO[T.Tuple3[A, B, C]],
|
||||
](a, b, c)
|
||||
}
|
||||
|
||||
func SequenceT4[A, B, C, D any](a ReaderIO[A], b ReaderIO[B], c ReaderIO[C], d ReaderIO[D]) ReaderIO[T.Tuple4[A, B, C, D]] {
|
||||
return R.SequenceT4[
|
||||
ReaderIO[A],
|
||||
ReaderIO[B],
|
||||
ReaderIO[C],
|
||||
ReaderIO[D],
|
||||
ReaderIO[T.Tuple4[A, B, C, D]],
|
||||
](a, b, c, d)
|
||||
}
|
37
context/readerioeither/bracket.go
Normal file
37
context/readerioeither/bracket.go
Normal file
@@ -0,0 +1,37 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package readerioeither
|
||||
|
||||
import (
|
||||
G "github.com/IBM/fp-go/context/readerioeither/generic"
|
||||
E "github.com/IBM/fp-go/either"
|
||||
)
|
||||
|
||||
// Bracket makes sure that a resource is cleaned up in the event of an error. The release action is called regardless of
|
||||
// whether the body action returns and error or not.
|
||||
func Bracket[
|
||||
A, B, ANY any](
|
||||
|
||||
acquire ReaderIOEither[A],
|
||||
use func(A) ReaderIOEither[B],
|
||||
release func(A, E.Either[error, B]) ReaderIOEither[ANY],
|
||||
) ReaderIOEither[B] {
|
||||
return G.Bracket[ReaderIOEither[A], ReaderIOEither[B], ReaderIOEither[ANY]](
|
||||
acquire,
|
||||
use,
|
||||
release,
|
||||
)
|
||||
}
|
@@ -25,25 +25,31 @@ import (
|
||||
F "github.com/IBM/fp-go/function"
|
||||
"github.com/IBM/fp-go/internal/file"
|
||||
IOE "github.com/IBM/fp-go/ioeither"
|
||||
IOEF "github.com/IBM/fp-go/ioeither/file"
|
||||
)
|
||||
|
||||
var (
|
||||
openIOE = IOE.Eitherize1(os.Open)
|
||||
// Open opens a file for reading within the given context
|
||||
Open = F.Flow3(
|
||||
openIOE,
|
||||
IOEF.Open,
|
||||
RIOE.FromIOEither[*os.File],
|
||||
RIOE.WithContext[*os.File],
|
||||
)
|
||||
|
||||
// Remove removes a file by name
|
||||
Remove = F.Flow2(
|
||||
IOEF.Remove,
|
||||
RIOE.FromIOEither[string],
|
||||
)
|
||||
)
|
||||
|
||||
// Close closes an object
|
||||
func Close[C io.Closer](c C) RIOE.ReaderIOEither[any] {
|
||||
return RIOE.FromIOEither(func() ET.Either[error, any] {
|
||||
return ET.TryCatchError(func() (any, error) {
|
||||
return c, c.Close()
|
||||
})
|
||||
})
|
||||
return F.Pipe2(
|
||||
c,
|
||||
IOEF.Close[C],
|
||||
RIOE.FromIOEither[any],
|
||||
)
|
||||
}
|
||||
|
||||
// ReadFile reads a file in the scope of a context
|
||||
|
@@ -19,9 +19,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
RIO "github.com/IBM/fp-go/context/readerio"
|
||||
R "github.com/IBM/fp-go/context/readerioeither"
|
||||
"github.com/IBM/fp-go/errors"
|
||||
F "github.com/IBM/fp-go/function"
|
||||
IO "github.com/IBM/fp-go/io"
|
||||
J "github.com/IBM/fp-go/json"
|
||||
@@ -37,20 +35,17 @@ func getData(r RecordType) string {
|
||||
|
||||
func ExampleReadFile() {
|
||||
|
||||
data := F.Pipe4(
|
||||
data := F.Pipe3(
|
||||
ReadFile("./data/file.json"),
|
||||
R.ChainEitherK(J.Unmarshal[RecordType]),
|
||||
R.ChainFirstIOK(IO.Logf[RecordType]("Log: %v")),
|
||||
R.Map(getData),
|
||||
R.GetOrElse(F.Flow2(
|
||||
errors.ToString,
|
||||
RIO.Of[string],
|
||||
)),
|
||||
)
|
||||
|
||||
result := data(context.Background())
|
||||
|
||||
fmt.Println(result())
|
||||
|
||||
// Output: Carsten
|
||||
// Output:
|
||||
// Right[<nil>, string](Carsten)
|
||||
}
|
||||
|
52
context/readerioeither/file/tempfile.go
Normal file
52
context/readerioeither/file/tempfile.go
Normal file
@@ -0,0 +1,52 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package file
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
RIOE "github.com/IBM/fp-go/context/readerioeither"
|
||||
F "github.com/IBM/fp-go/function"
|
||||
IO "github.com/IBM/fp-go/io"
|
||||
IOF "github.com/IBM/fp-go/io/file"
|
||||
IOEF "github.com/IBM/fp-go/ioeither/file"
|
||||
)
|
||||
|
||||
var (
|
||||
// onCreateTempFile creates a temp file with sensible defaults
|
||||
onCreateTempFile = CreateTemp("", "*")
|
||||
// destroy handler
|
||||
onReleaseTempFile = F.Flow4(
|
||||
IOF.Close[*os.File],
|
||||
IO.Map((*os.File).Name),
|
||||
RIOE.FromIO[string],
|
||||
RIOE.Chain(Remove),
|
||||
)
|
||||
)
|
||||
|
||||
// CreateTemp created a temp file with proper parametrization
|
||||
func CreateTemp(dir, pattern string) RIOE.ReaderIOEither[*os.File] {
|
||||
return F.Pipe2(
|
||||
IOEF.CreateTemp(dir, pattern),
|
||||
RIOE.FromIOEither[*os.File],
|
||||
RIOE.WithContext[*os.File],
|
||||
)
|
||||
}
|
||||
|
||||
// WithTempFile creates a temporary file, then invokes a callback to create a resource based on the file, then close and remove the temp file
|
||||
func WithTempFile[A any](f func(*os.File) RIOE.ReaderIOEither[A]) RIOE.ReaderIOEither[A] {
|
||||
return RIOE.WithResource[A](onCreateTempFile, onReleaseTempFile)(f)
|
||||
}
|
47
context/readerioeither/file/tempfile_test.go
Normal file
47
context/readerioeither/file/tempfile_test.go
Normal file
@@ -0,0 +1,47 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package file
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
RIOE "github.com/IBM/fp-go/context/readerioeither"
|
||||
E "github.com/IBM/fp-go/either"
|
||||
F "github.com/IBM/fp-go/function"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestWithTempFile(t *testing.T) {
|
||||
|
||||
res := WithTempFile(onWriteAll[*os.File]([]byte("Carsten")))
|
||||
|
||||
assert.Equal(t, E.Of[error]([]byte("Carsten")), res(context.Background())())
|
||||
}
|
||||
|
||||
func TestWithTempFileOnClosedFile(t *testing.T) {
|
||||
|
||||
res := WithTempFile(func(f *os.File) RIOE.ReaderIOEither[[]byte] {
|
||||
return F.Pipe2(
|
||||
f,
|
||||
onWriteAll[*os.File]([]byte("Carsten")),
|
||||
RIOE.ChainFirst(F.Constant1[[]byte](Close(f))),
|
||||
)
|
||||
})
|
||||
|
||||
assert.Equal(t, E.Of[error]([]byte("Carsten")), res(context.Background())())
|
||||
}
|
57
context/readerioeither/file/write.go
Normal file
57
context/readerioeither/file/write.go
Normal file
@@ -0,0 +1,57 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package file
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
|
||||
RIOE "github.com/IBM/fp-go/context/readerioeither"
|
||||
F "github.com/IBM/fp-go/function"
|
||||
)
|
||||
|
||||
func onWriteAll[W io.Writer](data []byte) func(w W) RIOE.ReaderIOEither[[]byte] {
|
||||
return func(w W) RIOE.ReaderIOEither[[]byte] {
|
||||
return F.Pipe1(
|
||||
RIOE.TryCatch(func(ctx context.Context) func() ([]byte, error) {
|
||||
return func() ([]byte, error) {
|
||||
_, err := w.Write(data)
|
||||
return data, err
|
||||
}
|
||||
}),
|
||||
RIOE.WithContext[[]byte],
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// WriteAll uses a generator function to create a stream, writes data to it and closes it
|
||||
func WriteAll[W io.WriteCloser](data []byte) func(acquire RIOE.ReaderIOEither[W]) RIOE.ReaderIOEither[[]byte] {
|
||||
onWrite := onWriteAll[W](data)
|
||||
return func(onCreate RIOE.ReaderIOEither[W]) RIOE.ReaderIOEither[[]byte] {
|
||||
return RIOE.WithResource[[]byte](
|
||||
onCreate,
|
||||
Close[W])(
|
||||
onWrite,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Write uses a generator function to create a stream, writes data to it and closes it
|
||||
func Write[R any, W io.WriteCloser](acquire RIOE.ReaderIOEither[W]) func(use func(W) RIOE.ReaderIOEither[R]) RIOE.ReaderIOEither[R] {
|
||||
return RIOE.WithResource[R](
|
||||
acquire,
|
||||
Close[W])
|
||||
}
|
@@ -1,23 +1,8 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package readerioeither
|
||||
|
||||
// Code generated by go generate; DO NOT EDIT.
|
||||
// This file was generated by robots at
|
||||
// 2023-07-28 22:48:20.012425 +0200 CEST m=+0.019517901
|
||||
// 2023-10-23 08:30:39.012572 +0200 CEST m=+0.008846101
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
53
context/readerioeither/generic/bracket.go
Normal file
53
context/readerioeither/generic/bracket.go
Normal file
@@ -0,0 +1,53 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package generic
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
E "github.com/IBM/fp-go/either"
|
||||
G "github.com/IBM/fp-go/internal/bracket"
|
||||
I "github.com/IBM/fp-go/readerio/generic"
|
||||
)
|
||||
|
||||
// Bracket makes sure that a resource is cleaned up in the event of an error. The release action is called regardless of
|
||||
// whether the body action returns and error or not.
|
||||
func Bracket[
|
||||
GA ~func(context.Context) TA,
|
||||
GB ~func(context.Context) TB,
|
||||
GANY ~func(context.Context) TANY,
|
||||
|
||||
TA ~func() E.Either[error, A],
|
||||
TB ~func() E.Either[error, B],
|
||||
TANY ~func() E.Either[error, ANY],
|
||||
|
||||
A, B, ANY any](
|
||||
|
||||
acquire GA,
|
||||
use func(A) GB,
|
||||
release func(A, E.Either[error, B]) GANY,
|
||||
) GB {
|
||||
return G.Bracket[GA, GB, GANY, E.Either[error, B], A, B](
|
||||
I.Of[GB, TB, context.Context, E.Either[error, B]],
|
||||
MonadChain[GA, GB, TA, TB, A, B],
|
||||
I.MonadChain[GB, GB, TB, TB, context.Context, E.Either[error, B], E.Either[error, B]],
|
||||
MonadChain[GANY, GB, TANY, TB, ANY, B],
|
||||
|
||||
acquire,
|
||||
use,
|
||||
release,
|
||||
)
|
||||
}
|
@@ -1,23 +1,8 @@
|
||||
// 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
|
||||
|
||||
// Code generated by go generate; DO NOT EDIT.
|
||||
// This file was generated by robots at
|
||||
// 2023-07-28 22:48:20.012425 +0200 CEST m=+0.019517901
|
||||
// 2023-10-23 08:30:39.012572 +0200 CEST m=+0.008846101
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
56
context/readerioeither/generic/monoid.go
Normal file
56
context/readerioeither/generic/monoid.go
Normal file
@@ -0,0 +1,56 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package generic
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
ET "github.com/IBM/fp-go/either"
|
||||
M "github.com/IBM/fp-go/monoid"
|
||||
)
|
||||
|
||||
func ApplicativeMonoid[GRA ~func(context.Context) GIOA, GRFA ~func(context.Context) GIOFA, GIOA ~func() ET.Either[error, A], GIOFA ~func() ET.Either[error, func(A) A], A any](
|
||||
m M.Monoid[A],
|
||||
) M.Monoid[GRA] {
|
||||
return M.ApplicativeMonoid(
|
||||
Of[GRA],
|
||||
MonadMap[GRA, GRFA],
|
||||
MonadAp[GRA, GRA, GRFA],
|
||||
m,
|
||||
)
|
||||
}
|
||||
|
||||
func ApplicativeMonoidSeq[GRA ~func(context.Context) GIOA, GRFA ~func(context.Context) GIOFA, GIOA ~func() ET.Either[error, A], GIOFA ~func() ET.Either[error, func(A) A], A any](
|
||||
m M.Monoid[A],
|
||||
) M.Monoid[GRA] {
|
||||
return M.ApplicativeMonoid(
|
||||
Of[GRA],
|
||||
MonadMap[GRA, GRFA],
|
||||
MonadApSeq[GRA, GRA, GRFA],
|
||||
m,
|
||||
)
|
||||
}
|
||||
|
||||
func ApplicativeMonoidPar[GRA ~func(context.Context) GIOA, GRFA ~func(context.Context) GIOFA, GIOA ~func() ET.Either[error, A], GIOFA ~func() ET.Either[error, func(A) A], A any](
|
||||
m M.Monoid[A],
|
||||
) M.Monoid[GRA] {
|
||||
return M.ApplicativeMonoid(
|
||||
Of[GRA],
|
||||
MonadMap[GRA, GRFA],
|
||||
MonadApPar[GRA, GRA, GRFA],
|
||||
m,
|
||||
)
|
||||
}
|
@@ -100,6 +100,28 @@ func Map[
|
||||
return RIE.Map[GRA, GRB](f)
|
||||
}
|
||||
|
||||
func MonadMapTo[
|
||||
GRA ~func(context.Context) GIOA,
|
||||
GRB ~func(context.Context) GIOB,
|
||||
|
||||
GIOA ~func() E.Either[error, A],
|
||||
GIOB ~func() E.Either[error, B],
|
||||
|
||||
A, B any](fa GRA, b B) GRB {
|
||||
return RIE.MonadMapTo[GRA, GRB](fa, b)
|
||||
}
|
||||
|
||||
func MapTo[
|
||||
GRA ~func(context.Context) GIOA,
|
||||
GRB ~func(context.Context) GIOB,
|
||||
|
||||
GIOA ~func() E.Either[error, A],
|
||||
GIOB ~func() E.Either[error, B],
|
||||
|
||||
A, B any](b B) func(GRA) GRB {
|
||||
return RIE.MapTo[GRA, GRB](b)
|
||||
}
|
||||
|
||||
func MonadChain[
|
||||
GRA ~func(context.Context) GIOA,
|
||||
GRB ~func(context.Context) GIOB,
|
||||
@@ -410,10 +432,10 @@ func FromIOEither[
|
||||
|
||||
func FromIO[
|
||||
GRA ~func(context.Context) GIOA,
|
||||
GIOA ~func() E.Either[error, A],
|
||||
|
||||
GIOB ~func() A,
|
||||
|
||||
GIOA ~func() E.Either[error, A],
|
||||
|
||||
A any](t GIOB) GRA {
|
||||
return RIE.FromIO[GRA](t)
|
||||
}
|
||||
@@ -458,6 +480,34 @@ func ChainIOK[
|
||||
return RIE.ChainIOK[GRA, GRB](f)
|
||||
}
|
||||
|
||||
func MonadChainReaderIOK[
|
||||
GRB ~func(context.Context) GIOB,
|
||||
GRA ~func(context.Context) GIOA,
|
||||
GRIO ~func(context.Context) GIO,
|
||||
|
||||
GIOA ~func() E.Either[error, A],
|
||||
GIOB ~func() E.Either[error, B],
|
||||
|
||||
GIO ~func() B,
|
||||
|
||||
A, B any](ma GRA, f func(A) GRIO) GRB {
|
||||
return RIE.MonadChainReaderIOK[GRA, GRB](ma, f)
|
||||
}
|
||||
|
||||
func ChainReaderIOK[
|
||||
GRB ~func(context.Context) GIOB,
|
||||
GRA ~func(context.Context) GIOA,
|
||||
GRIO ~func(context.Context) GIO,
|
||||
|
||||
GIOA ~func() E.Either[error, A],
|
||||
GIOB ~func() E.Either[error, B],
|
||||
|
||||
GIO ~func() B,
|
||||
|
||||
A, B any](f func(A) GRIO) func(ma GRA) GRB {
|
||||
return RIE.ChainReaderIOK[GRA, GRB](f)
|
||||
}
|
||||
|
||||
func MonadChainFirstIOK[
|
||||
GRA ~func(context.Context) GIOA,
|
||||
GIOA ~func() E.Either[error, A],
|
||||
@@ -521,7 +571,7 @@ func Timer[
|
||||
](delay time.Duration) GRA {
|
||||
return F.Pipe2(
|
||||
IO.Now[func() time.Time](),
|
||||
FromIO[GRA, GIOA, func() time.Time],
|
||||
FromIO[GRA, func() time.Time],
|
||||
Delay[GRA](delay),
|
||||
)
|
||||
}
|
||||
@@ -543,3 +593,82 @@ func TryCatch[
|
||||
A any](f func(context.Context) func() (A, error)) GRA {
|
||||
return RIE.TryCatch[GRA](f, ER.IdentityError)
|
||||
}
|
||||
|
||||
func MonadAlt[LAZY ~func() GEA, GEA ~func(context.Context) GIOA, GIOA ~func() E.Either[error, A], A any](first GEA, second LAZY) GEA {
|
||||
return RIE.MonadAlt(first, second)
|
||||
}
|
||||
|
||||
func Alt[LAZY ~func() GEA, GEA ~func(context.Context) GIOA, GIOA ~func() E.Either[error, A], A any](second LAZY) func(GEA) GEA {
|
||||
return RIE.Alt(second)
|
||||
}
|
||||
|
||||
// Memoize computes the value of the provided monad lazily but exactly once
|
||||
// The context used to compute the value is the context of the first call, so do not use this
|
||||
// method if the value has a functional dependency on the content of the context
|
||||
func Memoize[
|
||||
GRA ~func(context.Context) GIOA,
|
||||
GIOA ~func() E.Either[error, A],
|
||||
A any](rdr GRA) GRA {
|
||||
return RIE.Memoize[GRA](rdr)
|
||||
}
|
||||
|
||||
func Flatten[
|
||||
GGRA ~func(context.Context) GGIOA,
|
||||
GGIOA ~func() E.Either[error, GRA],
|
||||
GRA ~func(context.Context) GIOA,
|
||||
GIOA ~func() E.Either[error, A],
|
||||
A any](rdr GGRA) GRA {
|
||||
return RIE.Flatten[GRA](rdr)
|
||||
}
|
||||
|
||||
func MonadFromReaderIO[
|
||||
GRIOEA ~func(context.Context) GIOEA,
|
||||
GIOEA ~func() E.Either[error, A],
|
||||
|
||||
GRIOA ~func(context.Context) GIOA,
|
||||
GIOA ~func() A,
|
||||
|
||||
A any](a A, f func(A) GRIOA) GRIOEA {
|
||||
return RIE.MonadFromReaderIO[GRIOEA](a, f)
|
||||
}
|
||||
|
||||
func FromReaderIO[
|
||||
GRIOEA ~func(context.Context) GIOEA,
|
||||
GIOEA ~func() E.Either[error, A],
|
||||
|
||||
GRIOA ~func(context.Context) GIOA,
|
||||
GIOA ~func() A,
|
||||
|
||||
A any](f func(A) GRIOA) func(A) GRIOEA {
|
||||
return RIE.FromReaderIO[GRIOEA](f)
|
||||
}
|
||||
|
||||
func RightReaderIO[
|
||||
GRIOEA ~func(context.Context) GIOEA,
|
||||
GIOEA ~func() E.Either[error, A],
|
||||
|
||||
GRIOA ~func(context.Context) GIOA,
|
||||
GIOA ~func() A,
|
||||
|
||||
A any](ma GRIOA) GRIOEA {
|
||||
return RIE.RightReaderIO[GRIOEA](ma)
|
||||
}
|
||||
|
||||
func LeftReaderIO[
|
||||
GRIOEA ~func(context.Context) GIOEA,
|
||||
GIOEA ~func() E.Either[error, A],
|
||||
|
||||
GRIOE ~func(context.Context) GIOE,
|
||||
GIOE ~func() error,
|
||||
|
||||
A any](ma GRIOE) GRIOEA {
|
||||
return RIE.LeftReaderIO[GRIOEA](ma)
|
||||
}
|
||||
|
||||
func MonadFlap[GREAB ~func(context.Context) GEAB, GREB ~func(context.Context) GEB, GEAB ~func() E.Either[error, func(A) B], GEB ~func() E.Either[error, B], B, A any](fab GREAB, a A) GREB {
|
||||
return RIE.MonadFlap[GREAB, GREB](fab, a)
|
||||
}
|
||||
|
||||
func Flap[GREAB ~func(context.Context) GEAB, GREB ~func(context.Context) GEB, GEAB ~func() E.Either[error, func(A) B], GEB ~func() E.Either[error, B], B, A any](a A) func(GREAB) GREB {
|
||||
return RIE.Flap[GREAB, GREB](a)
|
||||
}
|
||||
|
@@ -13,18 +13,17 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package reader
|
||||
package generic
|
||||
|
||||
import (
|
||||
R "github.com/IBM/fp-go/reader/generic"
|
||||
"context"
|
||||
|
||||
ET "github.com/IBM/fp-go/either"
|
||||
S "github.com/IBM/fp-go/semigroup"
|
||||
)
|
||||
|
||||
// TraverseArray transforms an array
|
||||
func TraverseArray[A, B any](f func(A) Reader[B]) func([]A) Reader[[]B] {
|
||||
return R.TraverseArray[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)
|
||||
func AltSemigroup[GRA ~func(context.Context) GIOA, GIOA ~func() ET.Either[error, A], A any]() S.Semigroup[GRA] {
|
||||
return S.AltSemigroup(
|
||||
MonadAlt[func() GRA],
|
||||
)
|
||||
}
|
44
context/readerioeither/generic/sync.go
Normal file
44
context/readerioeither/generic/sync.go
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package generic
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
E "github.com/IBM/fp-go/either"
|
||||
F "github.com/IBM/fp-go/function"
|
||||
IO "github.com/IBM/fp-go/io/generic"
|
||||
)
|
||||
|
||||
// WithLock executes the provided IO operation in the scope of a lock
|
||||
func WithLock[
|
||||
GRA ~func(context.Context) GIOA,
|
||||
GIOA ~func() E.Either[error, A],
|
||||
GRCANCEL ~func(context.Context) GIOCANCEL,
|
||||
GIOCANCEL ~func() E.Either[error, context.CancelFunc],
|
||||
A any](lock GRCANCEL) func(fa GRA) GRA {
|
||||
|
||||
type GRANY func(ctx context.Context) func() E.Either[error, any]
|
||||
type IOANY func() any
|
||||
|
||||
return F.Flow2(
|
||||
F.Constant1[context.CancelFunc, GRA],
|
||||
WithResource[GRA, GRCANCEL, GRANY](lock, F.Flow2(
|
||||
IO.FromImpure[IOANY, context.CancelFunc],
|
||||
FromIO[GRANY, IOANY],
|
||||
)),
|
||||
)
|
||||
}
|
@@ -62,6 +62,25 @@ func TraverseArray[
|
||||
)
|
||||
}
|
||||
|
||||
// TraverseArrayWithIndex transforms an array
|
||||
func TraverseArrayWithIndex[
|
||||
AS ~[]A,
|
||||
GRBS ~func(context.Context) GIOBS,
|
||||
GRB ~func(context.Context) GIOB,
|
||||
GIOBS ~func() E.Either[error, BS],
|
||||
GIOB ~func() E.Either[error, B],
|
||||
BS ~[]B,
|
||||
A, B any](f func(int, A) GRB) func(AS) GRBS {
|
||||
|
||||
return RA.TraverseWithIndex[AS](
|
||||
Of[GRBS, GIOBS, BS],
|
||||
Map[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GIOBS, func() E.Either[error, func(B) BS], BS, func(B) BS],
|
||||
Ap[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GRB],
|
||||
|
||||
f,
|
||||
)
|
||||
}
|
||||
|
||||
// SequenceArray converts a homogeneous sequence of either into an either of sequence
|
||||
func SequenceArray[
|
||||
AS ~[]A,
|
||||
@@ -115,6 +134,26 @@ func TraverseRecord[K comparable,
|
||||
)
|
||||
}
|
||||
|
||||
// TraverseRecordWithIndex transforms a record
|
||||
func TraverseRecordWithIndex[K comparable,
|
||||
AS ~map[K]A,
|
||||
GRBS ~func(context.Context) GIOBS,
|
||||
GRB ~func(context.Context) GIOB,
|
||||
GIOBS ~func() E.Either[error, BS],
|
||||
GIOB ~func() E.Either[error, B],
|
||||
BS ~map[K]B,
|
||||
|
||||
A, B any](f func(K, A) GRB) func(AS) GRBS {
|
||||
|
||||
return RR.TraverseWithIndex[AS](
|
||||
Of[GRBS, GIOBS, BS],
|
||||
Map[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GIOBS, func() E.Either[error, func(B) BS], BS, func(B) BS],
|
||||
Ap[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GRB],
|
||||
|
||||
f,
|
||||
)
|
||||
}
|
||||
|
||||
// SequenceRecord converts a homogeneous sequence of either into an either of sequence
|
||||
func SequenceRecord[K comparable,
|
||||
AS ~map[K]A,
|
||||
|
@@ -21,6 +21,13 @@ import (
|
||||
"testing"
|
||||
|
||||
H "net/http"
|
||||
|
||||
R "github.com/IBM/fp-go/context/readerioeither"
|
||||
E "github.com/IBM/fp-go/either"
|
||||
"github.com/IBM/fp-go/errors"
|
||||
F "github.com/IBM/fp-go/function"
|
||||
IOE "github.com/IBM/fp-go/ioeither"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
type PostItem struct {
|
||||
@@ -30,6 +37,47 @@ type PostItem struct {
|
||||
Body string `json:"body"`
|
||||
}
|
||||
|
||||
func getTitle(item PostItem) string {
|
||||
return item.Title
|
||||
}
|
||||
|
||||
type simpleRequestBuilder struct {
|
||||
method string
|
||||
url string
|
||||
headers H.Header
|
||||
}
|
||||
|
||||
func requestBuilder() simpleRequestBuilder {
|
||||
return simpleRequestBuilder{method: "GET"}
|
||||
}
|
||||
|
||||
func (b simpleRequestBuilder) WithURL(url string) simpleRequestBuilder {
|
||||
b.url = url
|
||||
return b
|
||||
}
|
||||
|
||||
func (b simpleRequestBuilder) WithHeader(key, value string) simpleRequestBuilder {
|
||||
if b.headers == nil {
|
||||
b.headers = make(H.Header)
|
||||
} else {
|
||||
b.headers = b.headers.Clone()
|
||||
}
|
||||
b.headers.Set(key, value)
|
||||
return b
|
||||
}
|
||||
|
||||
func (b simpleRequestBuilder) Build() R.ReaderIOEither[*H.Request] {
|
||||
return func(ctx context.Context) IOE.IOEither[error, *H.Request] {
|
||||
return IOE.TryCatchError(func() (*H.Request, error) {
|
||||
req, err := H.NewRequestWithContext(ctx, b.method, b.url, nil)
|
||||
if err == nil {
|
||||
req.Header = b.headers
|
||||
}
|
||||
return req, err
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSendSingleRequest(t *testing.T) {
|
||||
|
||||
client := MakeClient(H.DefaultClient)
|
||||
@@ -40,7 +88,70 @@ func TestSendSingleRequest(t *testing.T) {
|
||||
|
||||
resp1 := readItem(req1)
|
||||
|
||||
resE := resp1(context.Background())()
|
||||
resE := resp1(context.TODO())()
|
||||
|
||||
fmt.Println(resE)
|
||||
}
|
||||
|
||||
// setHeaderUnsafe updates a header value in a request object by mutating the request object
|
||||
func setHeaderUnsafe(key, value string) func(*H.Request) *H.Request {
|
||||
return func(req *H.Request) *H.Request {
|
||||
req.Header.Set(key, value)
|
||||
return req
|
||||
}
|
||||
}
|
||||
|
||||
func TestSendSingleRequestWithHeaderUnsafe(t *testing.T) {
|
||||
|
||||
client := MakeClient(H.DefaultClient)
|
||||
|
||||
// this is not safe from a puristic perspective, because the map call mutates the request object
|
||||
req1 := F.Pipe2(
|
||||
"https://jsonplaceholder.typicode.com/posts/1",
|
||||
MakeGetRequest,
|
||||
R.Map(setHeaderUnsafe("Content-Type", "text/html")),
|
||||
)
|
||||
|
||||
readItem := ReadJson[PostItem](client)
|
||||
|
||||
resp1 := F.Pipe2(
|
||||
req1,
|
||||
readItem,
|
||||
R.Map(getTitle),
|
||||
)
|
||||
|
||||
res := F.Pipe1(
|
||||
resp1(context.TODO())(),
|
||||
E.GetOrElse(errors.ToString),
|
||||
)
|
||||
|
||||
assert.Equal(t, "sunt aut facere repellat provident occaecati excepturi optio reprehenderit", res)
|
||||
}
|
||||
|
||||
func TestSendSingleRequestWithHeaderSafe(t *testing.T) {
|
||||
|
||||
client := MakeClient(H.DefaultClient)
|
||||
|
||||
// the request builder assembles config values to construct
|
||||
// the final http request. Each `With` step creates a copy of the settings
|
||||
// so the flow is pure
|
||||
request := requestBuilder().
|
||||
WithURL("https://jsonplaceholder.typicode.com/posts/1").
|
||||
WithHeader("Content-Type", "text/html").
|
||||
Build()
|
||||
|
||||
readItem := ReadJson[PostItem](client)
|
||||
|
||||
response := F.Pipe2(
|
||||
request,
|
||||
readItem,
|
||||
R.Map(getTitle),
|
||||
)
|
||||
|
||||
res := F.Pipe1(
|
||||
response(context.TODO())(),
|
||||
E.GetOrElse(errors.ToString),
|
||||
)
|
||||
|
||||
assert.Equal(t, "sunt aut facere repellat provident occaecati excepturi optio reprehenderit", res)
|
||||
}
|
||||
|
56
context/readerioeither/monoid.go
Normal file
56
context/readerioeither/monoid.go
Normal file
@@ -0,0 +1,56 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package readerioeither
|
||||
|
||||
import (
|
||||
G "github.com/IBM/fp-go/context/readerioeither/generic"
|
||||
L "github.com/IBM/fp-go/lazy"
|
||||
M "github.com/IBM/fp-go/monoid"
|
||||
)
|
||||
|
||||
// ApplicativeMonoid returns a [Monoid] that concatenates [ReaderIOEither] instances via their applicative
|
||||
func ApplicativeMonoid[A any](m M.Monoid[A]) M.Monoid[ReaderIOEither[A]] {
|
||||
return G.ApplicativeMonoid[ReaderIOEither[A], ReaderIOEither[func(A) A]](m)
|
||||
}
|
||||
|
||||
// ApplicativeMonoidSeq returns a [Monoid] that concatenates [ReaderIOEither] instances via their applicative
|
||||
func ApplicativeMonoidSeq[A any](m M.Monoid[A]) M.Monoid[ReaderIOEither[A]] {
|
||||
return G.ApplicativeMonoidSeq[ReaderIOEither[A], ReaderIOEither[func(A) A]](m)
|
||||
}
|
||||
|
||||
// ApplicativeMonoidPar returns a [Monoid] that concatenates [ReaderIOEither] instances via their applicative
|
||||
func ApplicativeMonoidPar[A any](m M.Monoid[A]) M.Monoid[ReaderIOEither[A]] {
|
||||
return G.ApplicativeMonoidPar[ReaderIOEither[A], ReaderIOEither[func(A) A]](m)
|
||||
}
|
||||
|
||||
// AlternativeMonoid is the alternative [Monoid] for [ReaderIOEither]
|
||||
func AlternativeMonoid[A any](m M.Monoid[A]) M.Monoid[ReaderIOEither[A]] {
|
||||
return M.AlternativeMonoid(
|
||||
Of[A],
|
||||
MonadMap[A, func(A) A],
|
||||
MonadAp[A, A],
|
||||
MonadAlt[A],
|
||||
m,
|
||||
)
|
||||
}
|
||||
|
||||
// AltMonoid is the alternative [Monoid] for an [ReaderIOEither]
|
||||
func AltMonoid[A any](zero L.Lazy[ReaderIOEither[A]]) M.Monoid[ReaderIOEither[A]] {
|
||||
return M.AltMonoid(
|
||||
zero,
|
||||
MonadAlt[A],
|
||||
)
|
||||
}
|
@@ -19,12 +19,11 @@ import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
R "github.com/IBM/fp-go/context/reader"
|
||||
RIO "github.com/IBM/fp-go/context/readerio"
|
||||
G "github.com/IBM/fp-go/context/readerioeither/generic"
|
||||
ET "github.com/IBM/fp-go/either"
|
||||
IO "github.com/IBM/fp-go/io"
|
||||
IOE "github.com/IBM/fp-go/ioeither"
|
||||
L "github.com/IBM/fp-go/lazy"
|
||||
O "github.com/IBM/fp-go/option"
|
||||
)
|
||||
|
||||
@@ -32,14 +31,6 @@ func FromEither[A any](e ET.Either[error, A]) ReaderIOEither[A] {
|
||||
return G.FromEither[ReaderIOEither[A]](e)
|
||||
}
|
||||
|
||||
func RightReader[A any](r R.Reader[A]) ReaderIOEither[A] {
|
||||
return G.RightReader[ReaderIOEither[A]](r)
|
||||
}
|
||||
|
||||
func LeftReader[A any](l R.Reader[error]) ReaderIOEither[A] {
|
||||
return G.LeftReader[ReaderIOEither[A]](l)
|
||||
}
|
||||
|
||||
func Left[A any](l error) ReaderIOEither[A] {
|
||||
return G.Left[ReaderIOEither[A]](l)
|
||||
}
|
||||
@@ -48,10 +39,6 @@ func Right[A any](r A) ReaderIOEither[A] {
|
||||
return G.Right[ReaderIOEither[A]](r)
|
||||
}
|
||||
|
||||
func FromReader[A any](r R.Reader[A]) ReaderIOEither[A] {
|
||||
return G.FromReader[ReaderIOEither[A]](r)
|
||||
}
|
||||
|
||||
func MonadMap[A, B any](fa ReaderIOEither[A], f func(A) B) ReaderIOEither[B] {
|
||||
return G.MonadMap[ReaderIOEither[A], ReaderIOEither[B]](fa, f)
|
||||
}
|
||||
@@ -60,6 +47,14 @@ func Map[A, B any](f func(A) B) func(ReaderIOEither[A]) ReaderIOEither[B] {
|
||||
return G.Map[ReaderIOEither[A], ReaderIOEither[B]](f)
|
||||
}
|
||||
|
||||
func MonadMapTo[A, B any](fa ReaderIOEither[A], b B) ReaderIOEither[B] {
|
||||
return G.MonadMapTo[ReaderIOEither[A], ReaderIOEither[B]](fa, b)
|
||||
}
|
||||
|
||||
func MapTo[A, B any](b B) func(ReaderIOEither[A]) ReaderIOEither[B] {
|
||||
return G.MapTo[ReaderIOEither[A], ReaderIOEither[B]](b)
|
||||
}
|
||||
|
||||
func MonadChain[A, B any](ma ReaderIOEither[A], f func(A) ReaderIOEither[B]) ReaderIOEither[B] {
|
||||
return G.MonadChain(ma, f)
|
||||
}
|
||||
@@ -94,30 +89,14 @@ func FromPredicate[A any](pred func(A) bool, onFalse func(A) error) func(A) Read
|
||||
return G.FromPredicate[ReaderIOEither[A]](pred, onFalse)
|
||||
}
|
||||
|
||||
func Fold[A, B any](onLeft func(error) RIO.ReaderIO[B], onRight func(A) RIO.ReaderIO[B]) func(ReaderIOEither[A]) RIO.ReaderIO[B] {
|
||||
return G.Fold[RIO.ReaderIO[B], ReaderIOEither[A]](onLeft, onRight)
|
||||
}
|
||||
|
||||
func GetOrElse[A any](onLeft func(error) RIO.ReaderIO[A]) func(ReaderIOEither[A]) RIO.ReaderIO[A] {
|
||||
return G.GetOrElse[RIO.ReaderIO[A], ReaderIOEither[A]](onLeft)
|
||||
}
|
||||
|
||||
func OrElse[A any](onLeft func(error) ReaderIOEither[A]) func(ReaderIOEither[A]) ReaderIOEither[A] {
|
||||
return G.OrElse[ReaderIOEither[A]](onLeft)
|
||||
}
|
||||
|
||||
func OrLeft[A any](onLeft func(error) RIO.ReaderIO[error]) func(ReaderIOEither[A]) ReaderIOEither[A] {
|
||||
return G.OrLeft[ReaderIOEither[A], RIO.ReaderIO[error]](onLeft)
|
||||
}
|
||||
|
||||
func Ask() ReaderIOEither[context.Context] {
|
||||
return G.Ask[ReaderIOEither[context.Context]]()
|
||||
}
|
||||
|
||||
func Asks[A any](r R.Reader[A]) ReaderIOEither[A] {
|
||||
return G.Asks[ReaderIOEither[A]](r)
|
||||
}
|
||||
|
||||
func MonadChainEitherK[A, B any](ma ReaderIOEither[A], f func(A) ET.Either[error, B]) ReaderIOEither[B] {
|
||||
return G.MonadChainEitherK[ReaderIOEither[A], ReaderIOEither[B]](ma, f)
|
||||
}
|
||||
@@ -146,6 +125,10 @@ func FromIO[A any](t IO.IO[A]) ReaderIOEither[A] {
|
||||
return G.FromIO[ReaderIOEither[A]](t)
|
||||
}
|
||||
|
||||
func FromLazy[A any](t L.Lazy[A]) ReaderIOEither[A] {
|
||||
return G.FromIO[ReaderIOEither[A]](t)
|
||||
}
|
||||
|
||||
// Never returns a 'ReaderIOEither' that never returns, except if its context gets canceled
|
||||
func Never[A any]() ReaderIOEither[A] {
|
||||
return G.Never[ReaderIOEither[A]]()
|
||||
@@ -182,7 +165,7 @@ func Timer(delay time.Duration) ReaderIOEither[time.Time] {
|
||||
}
|
||||
|
||||
// Defer creates an IO by creating a brand new IO via a generator function, each time
|
||||
func Defer[A any](gen func() ReaderIOEither[A]) ReaderIOEither[A] {
|
||||
func Defer[A any](gen L.Lazy[ReaderIOEither[A]]) ReaderIOEither[A] {
|
||||
return G.Defer[ReaderIOEither[A]](gen)
|
||||
}
|
||||
|
||||
@@ -190,3 +173,34 @@ func Defer[A any](gen func() ReaderIOEither[A]) ReaderIOEither[A] {
|
||||
func TryCatch[A any](f func(context.Context) func() (A, error)) ReaderIOEither[A] {
|
||||
return G.TryCatch[ReaderIOEither[A]](f)
|
||||
}
|
||||
|
||||
// MonadAlt identifies an associative operation on a type constructor
|
||||
func MonadAlt[A any](first ReaderIOEither[A], second L.Lazy[ReaderIOEither[A]]) ReaderIOEither[A] {
|
||||
return G.MonadAlt(first, second)
|
||||
}
|
||||
|
||||
// Alt identifies an associative operation on a type constructor
|
||||
func Alt[A any](second L.Lazy[ReaderIOEither[A]]) func(ReaderIOEither[A]) ReaderIOEither[A] {
|
||||
return G.Alt(second)
|
||||
}
|
||||
|
||||
// Memoize computes the value of the provided [ReaderIOEither] monad lazily but exactly once
|
||||
// The context used to compute the value is the context of the first call, so do not use this
|
||||
// method if the value has a functional dependency on the content of the context
|
||||
func Memoize[A any](rdr ReaderIOEither[A]) ReaderIOEither[A] {
|
||||
return G.Memoize[ReaderIOEither[A]](rdr)
|
||||
}
|
||||
|
||||
// Flatten converts a nested [ReaderIOEither] into a [ReaderIOEither]
|
||||
func Flatten[
|
||||
A any](rdr ReaderIOEither[ReaderIOEither[A]]) ReaderIOEither[A] {
|
||||
return G.Flatten[ReaderIOEither[ReaderIOEither[A]]](rdr)
|
||||
}
|
||||
|
||||
func MonadFlap[B, A any](fab ReaderIOEither[func(A) B], a A) ReaderIOEither[B] {
|
||||
return G.MonadFlap[ReaderIOEither[func(A) B], ReaderIOEither[B]](fab, a)
|
||||
}
|
||||
|
||||
func Flap[B, A any](a A) func(ReaderIOEither[func(A) B]) ReaderIOEither[B] {
|
||||
return G.Flap[ReaderIOEither[func(A) B], ReaderIOEither[B]](a)
|
||||
}
|
||||
|
@@ -162,3 +162,125 @@ func TestRegularApply(t *testing.T) {
|
||||
res := applied(context.Background())()
|
||||
assert.Equal(t, E.Of[error]("CARSTEN"), res)
|
||||
}
|
||||
|
||||
func TestWithResourceNoErrors(t *testing.T) {
|
||||
var countAcquire, countBody, countRelease int
|
||||
|
||||
acquire := FromLazy(func() int {
|
||||
countAcquire++
|
||||
return countAcquire
|
||||
})
|
||||
|
||||
release := func(int) ReaderIOEither[int] {
|
||||
return FromLazy(func() int {
|
||||
countRelease++
|
||||
return countRelease
|
||||
})
|
||||
}
|
||||
|
||||
body := func(int) ReaderIOEither[int] {
|
||||
return FromLazy(func() int {
|
||||
countBody++
|
||||
return countBody
|
||||
})
|
||||
}
|
||||
|
||||
resRIOE := WithResource[int](acquire, release)(body)
|
||||
|
||||
res := resRIOE(context.Background())()
|
||||
|
||||
assert.Equal(t, 1, countAcquire)
|
||||
assert.Equal(t, 1, countBody)
|
||||
assert.Equal(t, 1, countRelease)
|
||||
assert.Equal(t, E.Of[error](1), res)
|
||||
}
|
||||
|
||||
func TestWithResourceErrorInBody(t *testing.T) {
|
||||
var countAcquire, countBody, countRelease int
|
||||
|
||||
acquire := FromLazy(func() int {
|
||||
countAcquire++
|
||||
return countAcquire
|
||||
})
|
||||
|
||||
release := func(int) ReaderIOEither[int] {
|
||||
return FromLazy(func() int {
|
||||
countRelease++
|
||||
return countRelease
|
||||
})
|
||||
}
|
||||
|
||||
err := fmt.Errorf("error in body")
|
||||
body := func(int) ReaderIOEither[int] {
|
||||
return Left[int](err)
|
||||
}
|
||||
|
||||
resRIOE := WithResource[int](acquire, release)(body)
|
||||
|
||||
res := resRIOE(context.Background())()
|
||||
|
||||
assert.Equal(t, 1, countAcquire)
|
||||
assert.Equal(t, 0, countBody)
|
||||
assert.Equal(t, 1, countRelease)
|
||||
assert.Equal(t, E.Left[int](err), res)
|
||||
}
|
||||
|
||||
func TestWithResourceErrorInAcquire(t *testing.T) {
|
||||
var countAcquire, countBody, countRelease int
|
||||
|
||||
err := fmt.Errorf("error in acquire")
|
||||
acquire := Left[int](err)
|
||||
|
||||
release := func(int) ReaderIOEither[int] {
|
||||
return FromLazy(func() int {
|
||||
countRelease++
|
||||
return countRelease
|
||||
})
|
||||
}
|
||||
|
||||
body := func(int) ReaderIOEither[int] {
|
||||
return FromLazy(func() int {
|
||||
countBody++
|
||||
return countBody
|
||||
})
|
||||
}
|
||||
|
||||
resRIOE := WithResource[int](acquire, release)(body)
|
||||
|
||||
res := resRIOE(context.Background())()
|
||||
|
||||
assert.Equal(t, 0, countAcquire)
|
||||
assert.Equal(t, 0, countBody)
|
||||
assert.Equal(t, 0, countRelease)
|
||||
assert.Equal(t, E.Left[int](err), res)
|
||||
}
|
||||
|
||||
func TestWithResourceErrorInRelease(t *testing.T) {
|
||||
var countAcquire, countBody, countRelease int
|
||||
|
||||
acquire := FromLazy(func() int {
|
||||
countAcquire++
|
||||
return countAcquire
|
||||
})
|
||||
|
||||
err := fmt.Errorf("error in release")
|
||||
release := func(int) ReaderIOEither[int] {
|
||||
return Left[int](err)
|
||||
}
|
||||
|
||||
body := func(int) ReaderIOEither[int] {
|
||||
return FromLazy(func() int {
|
||||
countBody++
|
||||
return countBody
|
||||
})
|
||||
}
|
||||
|
||||
resRIOE := WithResource[int](acquire, release)(body)
|
||||
|
||||
res := resRIOE(context.Background())()
|
||||
|
||||
assert.Equal(t, 1, countAcquire)
|
||||
assert.Equal(t, 1, countBody)
|
||||
assert.Equal(t, 0, countRelease)
|
||||
assert.Equal(t, E.Left[int](err), res)
|
||||
}
|
||||
|
26
context/readerioeither/semigroup.go
Normal file
26
context/readerioeither/semigroup.go
Normal file
@@ -0,0 +1,26 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package readerioeither
|
||||
|
||||
import (
|
||||
G "github.com/IBM/fp-go/context/readerioeither/generic"
|
||||
S "github.com/IBM/fp-go/semigroup"
|
||||
)
|
||||
|
||||
// AltSemigroup is a [Semigroup] that tries the first item and then the second one using an alternative
|
||||
func AltSemigroup[A any]() S.Semigroup[ReaderIOEither[A]] {
|
||||
return G.AltSemigroup[ReaderIOEither[A]]()
|
||||
}
|
@@ -13,14 +13,15 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package reader implements a specialization of the Reader monad assuming a golang context as the context of the monad
|
||||
package reader
|
||||
package readerioeither
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
R "github.com/IBM/fp-go/reader"
|
||||
G "github.com/IBM/fp-go/context/readerioeither/generic"
|
||||
)
|
||||
|
||||
// Reader is a specialization of the Reader monad assuming a golang context as the context of the monad
|
||||
type Reader[A any] R.Reader[context.Context, A]
|
||||
// WithLock executes the provided IO operation in the scope of a lock
|
||||
func WithLock[A any](lock ReaderIOEither[context.CancelFunc]) func(fa ReaderIOEither[A]) ReaderIOEither[A] {
|
||||
return G.WithLock[ReaderIOEither[A]](lock)
|
||||
}
|
@@ -24,6 +24,11 @@ func TraverseArray[A, B any](f func(A) ReaderIOEither[B]) func([]A) ReaderIOEith
|
||||
return G.TraverseArray[[]A, ReaderIOEither[[]B]](f)
|
||||
}
|
||||
|
||||
// TraverseArrayWithIndex uses transforms an array [[]A] into [[]ReaderIOEither[B]] and then resolves that into a [ReaderIOEither[[]B]]
|
||||
func TraverseArrayWithIndex[A, B any](f func(int, A) ReaderIOEither[B]) func([]A) ReaderIOEither[[]B] {
|
||||
return G.TraverseArrayWithIndex[[]A, ReaderIOEither[[]B]](f)
|
||||
}
|
||||
|
||||
// SequenceArray converts a homogeneous sequence of either into an either of sequence
|
||||
func SequenceArray[A any](ma []ReaderIOEither[A]) ReaderIOEither[[]A] {
|
||||
return G.SequenceArray[[]A, []ReaderIOEither[A], ReaderIOEither[[]A]](ma)
|
||||
@@ -34,6 +39,11 @@ func TraverseRecord[K comparable, A, B any](f func(A) ReaderIOEither[B]) func(ma
|
||||
return G.TraverseRecord[K, map[K]A, ReaderIOEither[map[K]B]](f)
|
||||
}
|
||||
|
||||
// TraverseRecordWithIndex uses transforms a record [map[K]A] into [map[K]ReaderIOEither[B]] and then resolves that into a [ReaderIOEither[map[K]B]]
|
||||
func TraverseRecordWithIndex[K comparable, A, B any](f func(K, A) ReaderIOEither[B]) func(map[K]A) ReaderIOEither[map[K]B] {
|
||||
return G.TraverseRecordWithIndex[K, map[K]A, ReaderIOEither[map[K]B]](f)
|
||||
}
|
||||
|
||||
// SequenceRecord converts a homogeneous sequence of either into an either of sequence
|
||||
func SequenceRecord[K comparable, A any](ma map[K]ReaderIOEither[A]) ReaderIOEither[map[K]A] {
|
||||
return G.SequenceRecord[K, map[K]A, map[K]ReaderIOEither[A], ReaderIOEither[map[K]A]](ma)
|
||||
|
2
coverage.bat
Normal file
2
coverage.bat
Normal file
@@ -0,0 +1,2 @@
|
||||
@echo off
|
||||
go tool cover -html=build/cover.out -o build/cover.html
|
@@ -24,6 +24,7 @@ func ApplySemigroup[E, A any](s S.Semigroup[A]) S.Semigroup[Either[E, A]] {
|
||||
return S.ApplySemigroup(MonadMap[E, A, func(A) A], MonadAp[A, E, A], s)
|
||||
}
|
||||
|
||||
// ApplicativeMonoid returns a [Monoid] that concatenates [Either] instances via their applicative
|
||||
func ApplicativeMonoid[E, A any](m M.Monoid[A]) M.Monoid[Either[E, A]] {
|
||||
return M.ApplicativeMonoid(Of[E, A], MonadMap[E, A, func(A) A], MonadAp[A, E, A], m)
|
||||
}
|
||||
|
@@ -20,7 +20,7 @@ import (
|
||||
RA "github.com/IBM/fp-go/internal/array"
|
||||
)
|
||||
|
||||
// TraverseArray transforms an array
|
||||
// TraverseArrayG transforms an array
|
||||
func TraverseArrayG[GA ~[]A, GB ~[]B, E, A, B any](f func(A) Either[E, B]) func(GA) Either[E, GB] {
|
||||
return RA.Traverse[GA](
|
||||
Of[E, GB],
|
||||
@@ -36,6 +36,22 @@ func TraverseArray[E, A, B any](f func(A) Either[E, B]) func([]A) Either[E, []B]
|
||||
return TraverseArrayG[[]A, []B](f)
|
||||
}
|
||||
|
||||
// TraverseArrayWithIndexG transforms an array
|
||||
func TraverseArrayWithIndexG[GA ~[]A, GB ~[]B, E, A, B any](f func(int, A) Either[E, B]) func(GA) Either[E, GB] {
|
||||
return RA.TraverseWithIndex[GA](
|
||||
Of[E, GB],
|
||||
Map[E, GB, func(B) GB],
|
||||
Ap[GB, E, B],
|
||||
|
||||
f,
|
||||
)
|
||||
}
|
||||
|
||||
// TraverseArrayWithIndex transforms an array
|
||||
func TraverseArrayWithIndex[E, A, B any](f func(int, A) Either[E, B]) func([]A) Either[E, []B] {
|
||||
return TraverseArrayWithIndexG[[]A, []B](f)
|
||||
}
|
||||
|
||||
func SequenceArrayG[GA ~[]A, GOA ~[]Either[E, A], E, A any](ma GOA) Either[E, GA] {
|
||||
return TraverseArrayG[GOA, GA](F.Identity[Either[E, A]])(ma)
|
||||
}
|
||||
@@ -44,3 +60,15 @@ func SequenceArrayG[GA ~[]A, GOA ~[]Either[E, A], E, A any](ma GOA) Either[E, GA
|
||||
func SequenceArray[E, A any](ma []Either[E, A]) Either[E, []A] {
|
||||
return SequenceArrayG[[]A](ma)
|
||||
}
|
||||
|
||||
// CompactArrayG discards the none values and keeps the some values
|
||||
func CompactArrayG[A1 ~[]Either[E, A], A2 ~[]A, E, A any](fa A1) A2 {
|
||||
return RA.Reduce(fa, func(out A2, value Either[E, A]) A2 {
|
||||
return MonadFold(value, F.Constant1[E](out), F.Bind1st(RA.Append[A2, A], out))
|
||||
}, make(A2, len(fa)))
|
||||
}
|
||||
|
||||
// CompactArray discards the none values and keeps the some values
|
||||
func CompactArray[E, A any](fa []Either[E, A]) []A {
|
||||
return CompactArrayG[[]Either[E, A], []A](fa)
|
||||
}
|
||||
|
@@ -13,6 +13,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package option defines the [Either] datastructure and its monadic operations
|
||||
package either
|
||||
|
||||
//go:generate go run .. either --count 10 --filename gen.go
|
||||
|
@@ -22,6 +22,8 @@ package either
|
||||
import (
|
||||
E "github.com/IBM/fp-go/errors"
|
||||
F "github.com/IBM/fp-go/function"
|
||||
FC "github.com/IBM/fp-go/internal/functor"
|
||||
L "github.com/IBM/fp-go/lazy"
|
||||
O "github.com/IBM/fp-go/option"
|
||||
)
|
||||
|
||||
@@ -91,12 +93,12 @@ func MonadChainTo[E, A, B any](ma Either[E, A], mb Either[E, B]) Either[E, B] {
|
||||
return mb
|
||||
}
|
||||
|
||||
func MonadChainOptionK[E, A, B any](onNone func() E, ma Either[E, A], f func(A) O.Option[B]) Either[E, B] {
|
||||
return MonadChain(ma, F.Flow2(f, FromOption[E, B](onNone)))
|
||||
func MonadChainOptionK[A, B, E any](onNone func() E, ma Either[E, A], f func(A) O.Option[B]) Either[E, B] {
|
||||
return MonadChain(ma, F.Flow2(f, FromOption[B](onNone)))
|
||||
}
|
||||
|
||||
func ChainOptionK[E, A, B any](onNone func() E) func(func(A) O.Option[B]) func(Either[E, A]) Either[E, B] {
|
||||
from := FromOption[E, B](onNone)
|
||||
func ChainOptionK[A, B, E any](onNone func() E) func(func(A) O.Option[B]) func(Either[E, A]) Either[E, B] {
|
||||
from := FromOption[B](onNone)
|
||||
return func(f func(A) O.Option[B]) func(Either[E, A]) Either[E, B] {
|
||||
return Chain(F.Flow2(f, from))
|
||||
}
|
||||
@@ -118,16 +120,15 @@ func Flatten[E, A any](mma Either[E, Either[E, A]]) Either[E, A] {
|
||||
return MonadChain(mma, F.Identity[Either[E, A]])
|
||||
}
|
||||
|
||||
func TryCatch[FA ~func() (A, error), FE func(error) E, E, A any](f FA, onThrow FE) Either[E, A] {
|
||||
val, err := f()
|
||||
func TryCatch[FE func(error) E, E, A any](val A, err error, onThrow FE) Either[E, A] {
|
||||
if err != nil {
|
||||
return F.Pipe2(err, onThrow, Left[A, E])
|
||||
}
|
||||
return F.Pipe1(val, Right[E, A])
|
||||
}
|
||||
|
||||
func TryCatchError[F ~func() (A, error), A any](f F) Either[error, A] {
|
||||
return TryCatch(f, E.IdentityError)
|
||||
func TryCatchError[A any](val A, err error) Either[error, A] {
|
||||
return TryCatch(val, err, E.IdentityError)
|
||||
}
|
||||
|
||||
func Sequence2[E, T1, T2, R any](f func(T1, T2) Either[E, R]) func(Either[E, T1], Either[E, T2]) Either[E, R] {
|
||||
@@ -142,19 +143,17 @@ func Sequence3[E, T1, T2, T3, R any](f func(T1, T2, T3) Either[E, R]) func(Eithe
|
||||
}
|
||||
}
|
||||
|
||||
func FromOption[E, A any](onNone func() E) func(O.Option[A]) Either[E, A] {
|
||||
func FromOption[A, E any](onNone func() E) func(O.Option[A]) Either[E, A] {
|
||||
return O.Fold(F.Nullary2(onNone, Left[A, E]), Right[E, A])
|
||||
}
|
||||
|
||||
func ToOption[E, A any]() func(Either[E, A]) O.Option[A] {
|
||||
return Fold(F.Ignore1of1[E](O.None[A]), O.Some[A])
|
||||
func ToOption[E, A any](ma Either[E, A]) O.Option[A] {
|
||||
return MonadFold(ma, F.Ignore1of1[E](O.None[A]), O.Some[A])
|
||||
}
|
||||
|
||||
func FromError[A any](f func(a A) error) func(A) Either[error, A] {
|
||||
return func(a A) Either[error, A] {
|
||||
return TryCatchError(func() (A, error) {
|
||||
return a, f(a)
|
||||
})
|
||||
return TryCatchError(a, f(a))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,7 +181,7 @@ func FromPredicate[E, A any](pred func(A) bool, onFalse func(A) E) func(A) Eithe
|
||||
}
|
||||
}
|
||||
|
||||
func FromNillable[E, A any](e E) func(*A) Either[E, *A] {
|
||||
func FromNillable[A, E any](e E) func(*A) Either[E, *A] {
|
||||
return FromPredicate(F.IsNonNil[A], F.Constant1[*A](e))
|
||||
}
|
||||
|
||||
@@ -197,11 +196,11 @@ func Reduce[E, A, B any](f func(B, A) B, initial B) func(Either[E, A]) B {
|
||||
)
|
||||
}
|
||||
|
||||
func AltW[E, E1, A any](that func() Either[E1, A]) func(Either[E, A]) Either[E1, A] {
|
||||
func AltW[E, E1, A any](that L.Lazy[Either[E1, A]]) func(Either[E, A]) Either[E1, A] {
|
||||
return Fold(F.Ignore1of1[E](that), Right[E1, A])
|
||||
}
|
||||
|
||||
func Alt[E, A any](that func() Either[E, A]) func(Either[E, A]) Either[E, A] {
|
||||
func Alt[E, A any](that L.Lazy[Either[E, A]]) func(Either[E, A]) Either[E, A] {
|
||||
return AltW[E](that)
|
||||
}
|
||||
|
||||
@@ -209,7 +208,7 @@ func OrElse[E, A any](onLeft func(e E) Either[E, A]) func(Either[E, A]) Either[E
|
||||
return Fold(onLeft, Of[E, A])
|
||||
}
|
||||
|
||||
func ToType[E, A any](onError func(any) E) func(any) Either[E, A] {
|
||||
func ToType[A, E any](onError func(any) E) func(any) Either[E, A] {
|
||||
return func(value any) Either[E, A] {
|
||||
return F.Pipe2(
|
||||
value,
|
||||
@@ -245,3 +244,15 @@ func MonadSequence3[E, T1, T2, T3, R any](e1 Either[E, T1], e2 Either[E, T2], e3
|
||||
func Swap[E, A any](val Either[E, A]) Either[A, E] {
|
||||
return MonadFold(val, Right[A, E], Left[E, A])
|
||||
}
|
||||
|
||||
func MonadFlap[E, B, A any](fab Either[E, func(A) B], a A) Either[E, B] {
|
||||
return FC.MonadFlap(MonadMap[E, func(A) B, B], fab, a)
|
||||
}
|
||||
|
||||
func Flap[E, B, A any](a A) func(Either[E, func(A) B]) Either[E, B] {
|
||||
return F.Bind2nd(MonadFlap[E, B, A], a)
|
||||
}
|
||||
|
||||
func MonadAlt[E, A any](fa Either[E, A], that L.Lazy[Either[E, A]]) Either[E, A] {
|
||||
return MonadFold(fa, F.Ignore1of1[E](that), Of[E, A])
|
||||
}
|
||||
|
@@ -100,7 +100,7 @@ func TestChainFirst(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestChainOptionK(t *testing.T) {
|
||||
f := ChainOptionK[string, int, int](F.Constant("a"))(func(n int) O.Option[int] {
|
||||
f := ChainOptionK[int, int](F.Constant("a"))(func(n int) O.Option[int] {
|
||||
if n > 0 {
|
||||
return O.Some(n)
|
||||
}
|
||||
@@ -112,6 +112,6 @@ func TestChainOptionK(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestFromOption(t *testing.T) {
|
||||
assert.Equal(t, Left[int]("none"), FromOption[string, int](F.Constant("none"))(O.None[int]()))
|
||||
assert.Equal(t, Right[string](1), FromOption[string, int](F.Constant("none"))(O.Some(1)))
|
||||
assert.Equal(t, Left[int]("none"), FromOption[int](F.Constant("none"))(O.None[int]()))
|
||||
assert.Equal(t, Right[string](1), FromOption[int](F.Constant("none"))(O.Some(1)))
|
||||
}
|
||||
|
58
either/examples_create_test.go
Normal file
58
either/examples_create_test.go
Normal file
@@ -0,0 +1,58 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package either
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/IBM/fp-go/errors"
|
||||
)
|
||||
|
||||
func ExampleEither_creation() {
|
||||
// Build an Either
|
||||
leftValue := Left[string](fmt.Errorf("some error"))
|
||||
rightValue := Right[error]("value")
|
||||
|
||||
// Build from a value
|
||||
fromNillable := FromNillable[string](fmt.Errorf("value was nil"))
|
||||
leftFromNil := fromNillable(nil)
|
||||
value := "value"
|
||||
rightFromPointer := fromNillable(&value)
|
||||
|
||||
// some predicate
|
||||
isEven := func(num int) bool {
|
||||
return num%2 == 0
|
||||
}
|
||||
fromEven := FromPredicate(isEven, errors.OnSome[int]("%d is an odd number"))
|
||||
leftFromPred := fromEven(3)
|
||||
rightFromPred := fromEven(4)
|
||||
|
||||
fmt.Println(leftValue)
|
||||
fmt.Println(rightValue)
|
||||
fmt.Println(leftFromNil)
|
||||
fmt.Println(IsRight(rightFromPointer))
|
||||
fmt.Println(leftFromPred)
|
||||
fmt.Println(rightFromPred)
|
||||
|
||||
// Output:
|
||||
// Left[*errors.errorString, string](some error)
|
||||
// Right[<nil>, string](value)
|
||||
// Left[*errors.errorString, *string](value was nil)
|
||||
// true
|
||||
// Left[*errors.errorString, int](3 is an odd number)
|
||||
// Right[<nil>, int](4)
|
||||
|
||||
}
|
64
either/examples_extract_test.go
Normal file
64
either/examples_extract_test.go
Normal file
@@ -0,0 +1,64 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package either
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
F "github.com/IBM/fp-go/function"
|
||||
N "github.com/IBM/fp-go/number"
|
||||
)
|
||||
|
||||
func ExampleEither_extraction() {
|
||||
leftValue := Left[int](fmt.Errorf("Division by Zero!"))
|
||||
rightValue := Right[error](10)
|
||||
|
||||
// Convert Either[E, A] to A with a default value
|
||||
leftWithDefault := GetOrElse(F.Constant1[error](0))(leftValue) // 0
|
||||
rightWithDefault := GetOrElse(F.Constant1[error](0))(rightValue) // 10
|
||||
|
||||
// Apply a different function on Left(...)/Right(...)
|
||||
doubleOrZero := Fold(F.Constant1[error](0), N.Mul(2)) // func(Either[error, int]) int
|
||||
doubleFromLeft := doubleOrZero(leftValue) // 0
|
||||
doubleFromRight := doubleOrZero(rightValue) // 20
|
||||
|
||||
// Pro-tip: Fold is short for the following:
|
||||
doubleOrZeroBis := F.Flow2(
|
||||
Map[error](N.Mul(2)),
|
||||
GetOrElse(F.Constant1[error](0)),
|
||||
)
|
||||
doubleFromLeftBis := doubleOrZeroBis(leftValue) // 0
|
||||
doubleFromRightBis := doubleOrZeroBis(rightValue) // 20
|
||||
|
||||
fmt.Println(leftValue)
|
||||
fmt.Println(rightValue)
|
||||
fmt.Println(leftWithDefault)
|
||||
fmt.Println(rightWithDefault)
|
||||
fmt.Println(doubleFromLeft)
|
||||
fmt.Println(doubleFromRight)
|
||||
fmt.Println(doubleFromLeftBis)
|
||||
fmt.Println(doubleFromRightBis)
|
||||
|
||||
// Output:
|
||||
// Left[*errors.errorString, int](Division by Zero!)
|
||||
// Right[<nil>, int](10)
|
||||
// 0
|
||||
// 10
|
||||
// 0
|
||||
// 20
|
||||
// 0
|
||||
// 20
|
||||
}
|
@@ -27,12 +27,10 @@ import (
|
||||
var (
|
||||
// Command executes a command
|
||||
// use this version if the command does not produce any side effect, i.e. if the output is uniquely determined by by the input
|
||||
// typically you'd rather use the IOEither version of the command
|
||||
// typically you'd rather use the [IOEither] version of the command
|
||||
Command = F.Curry3(command)
|
||||
)
|
||||
|
||||
func command(name string, args []string, in []byte) E.Either[error, exec.CommandOutput] {
|
||||
return E.TryCatchError(func() (exec.CommandOutput, error) {
|
||||
return GE.Exec(context.Background(), name, args, in)
|
||||
})
|
||||
return E.TryCatchError(GE.Exec(context.Background(), name, args, in))
|
||||
}
|
||||
|
@@ -1,21 +1,7 @@
|
||||
// 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.
|
||||
|
||||
// Code generated by go generate; DO NOT EDIT.
|
||||
// This file was generated by robots at
|
||||
// 2023-07-28 22:48:23.165015 +0200 CEST m=+0.038998801
|
||||
// 2023-10-23 08:30:40.410373 +0200 CEST m=+0.010337601
|
||||
|
||||
package either
|
||||
|
||||
import (
|
||||
@@ -27,9 +13,7 @@ import (
|
||||
// The inverse function is [Uneitherize0]
|
||||
func Eitherize0[F ~func() (R, error), R any](f F) func() Either[error, R] {
|
||||
return func() Either[error, R] {
|
||||
return TryCatchError(func() (R, error) {
|
||||
return f()
|
||||
})
|
||||
return TryCatchError(f())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,9 +29,7 @@ func Uneitherize0[F ~func() Either[error, R], R any](f F) func() (R, error) {
|
||||
// The inverse function is [Uneitherize1]
|
||||
func Eitherize1[F ~func(T0) (R, error), T0, R any](f F) func(T0) Either[error, R] {
|
||||
return func(t0 T0) Either[error, R] {
|
||||
return TryCatchError(func() (R, error) {
|
||||
return f(t0)
|
||||
})
|
||||
return TryCatchError(f(t0))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,9 +72,7 @@ func TraverseTuple1[F1 ~func(A1) Either[E, T1], E, A1, T1 any](f1 F1) func(T.Tup
|
||||
// The inverse function is [Uneitherize2]
|
||||
func Eitherize2[F ~func(T0, T1) (R, error), T0, T1, R any](f F) func(T0, T1) Either[error, R] {
|
||||
return func(t0 T0, t1 T1) Either[error, R] {
|
||||
return TryCatchError(func() (R, error) {
|
||||
return f(t0, t1)
|
||||
})
|
||||
return TryCatchError(f(t0, t1))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,9 +120,7 @@ func TraverseTuple2[F1 ~func(A1) Either[E, T1], F2 ~func(A2) Either[E, T2], E, A
|
||||
// The inverse function is [Uneitherize3]
|
||||
func Eitherize3[F ~func(T0, T1, T2) (R, error), T0, T1, T2, R any](f F) func(T0, T1, T2) Either[error, R] {
|
||||
return func(t0 T0, t1 T1, t2 T2) Either[error, R] {
|
||||
return TryCatchError(func() (R, error) {
|
||||
return f(t0, t1, t2)
|
||||
})
|
||||
return TryCatchError(f(t0, t1, t2))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,9 +173,7 @@ func TraverseTuple3[F1 ~func(A1) Either[E, T1], F2 ~func(A2) Either[E, T2], F3 ~
|
||||
// The inverse function is [Uneitherize4]
|
||||
func Eitherize4[F ~func(T0, T1, T2, T3) (R, error), T0, T1, T2, T3, R any](f F) func(T0, T1, T2, T3) Either[error, R] {
|
||||
return func(t0 T0, t1 T1, t2 T2, t3 T3) Either[error, R] {
|
||||
return TryCatchError(func() (R, error) {
|
||||
return f(t0, t1, t2, t3)
|
||||
})
|
||||
return TryCatchError(f(t0, t1, t2, t3))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -255,9 +231,7 @@ func TraverseTuple4[F1 ~func(A1) Either[E, T1], F2 ~func(A2) Either[E, T2], F3 ~
|
||||
// The inverse function is [Uneitherize5]
|
||||
func Eitherize5[F ~func(T0, T1, T2, T3, T4) (R, error), T0, T1, T2, T3, T4, R any](f F) func(T0, T1, T2, T3, T4) Either[error, R] {
|
||||
return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4) Either[error, R] {
|
||||
return TryCatchError(func() (R, error) {
|
||||
return f(t0, t1, t2, t3, t4)
|
||||
})
|
||||
return TryCatchError(f(t0, t1, t2, t3, t4))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -320,9 +294,7 @@ func TraverseTuple5[F1 ~func(A1) Either[E, T1], F2 ~func(A2) Either[E, T2], F3 ~
|
||||
// The inverse function is [Uneitherize6]
|
||||
func Eitherize6[F ~func(T0, T1, T2, T3, T4, T5) (R, error), T0, T1, T2, T3, T4, T5, R any](f F) func(T0, T1, T2, T3, T4, T5) Either[error, R] {
|
||||
return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5) Either[error, R] {
|
||||
return TryCatchError(func() (R, error) {
|
||||
return f(t0, t1, t2, t3, t4, t5)
|
||||
})
|
||||
return TryCatchError(f(t0, t1, t2, t3, t4, t5))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -390,9 +362,7 @@ func TraverseTuple6[F1 ~func(A1) Either[E, T1], F2 ~func(A2) Either[E, T2], F3 ~
|
||||
// The inverse function is [Uneitherize7]
|
||||
func Eitherize7[F ~func(T0, T1, T2, T3, T4, T5, T6) (R, error), T0, T1, T2, T3, T4, T5, T6, R any](f F) func(T0, T1, T2, T3, T4, T5, T6) Either[error, R] {
|
||||
return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6) Either[error, R] {
|
||||
return TryCatchError(func() (R, error) {
|
||||
return f(t0, t1, t2, t3, t4, t5, t6)
|
||||
})
|
||||
return TryCatchError(f(t0, t1, t2, t3, t4, t5, t6))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -465,9 +435,7 @@ func TraverseTuple7[F1 ~func(A1) Either[E, T1], F2 ~func(A2) Either[E, T2], F3 ~
|
||||
// The inverse function is [Uneitherize8]
|
||||
func Eitherize8[F ~func(T0, T1, T2, T3, T4, T5, T6, T7) (R, error), T0, T1, T2, T3, T4, T5, T6, T7, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7) Either[error, R] {
|
||||
return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7) Either[error, R] {
|
||||
return TryCatchError(func() (R, error) {
|
||||
return f(t0, t1, t2, t3, t4, t5, t6, t7)
|
||||
})
|
||||
return TryCatchError(f(t0, t1, t2, t3, t4, t5, t6, t7))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -545,9 +513,7 @@ func TraverseTuple8[F1 ~func(A1) Either[E, T1], F2 ~func(A2) Either[E, T2], F3 ~
|
||||
// The inverse function is [Uneitherize9]
|
||||
func Eitherize9[F ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8) (R, error), T0, T1, T2, T3, T4, T5, T6, T7, T8, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7, T8) Either[error, R] {
|
||||
return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8) Either[error, R] {
|
||||
return TryCatchError(func() (R, error) {
|
||||
return f(t0, t1, t2, t3, t4, t5, t6, t7, t8)
|
||||
})
|
||||
return TryCatchError(f(t0, t1, t2, t3, t4, t5, t6, t7, t8))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -630,9 +596,7 @@ func TraverseTuple9[F1 ~func(A1) Either[E, T1], F2 ~func(A2) Either[E, T2], F3 ~
|
||||
// The inverse function is [Uneitherize10]
|
||||
func Eitherize10[F ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) (R, error), T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) Either[error, R] {
|
||||
return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9) Either[error, R] {
|
||||
return TryCatchError(func() (R, error) {
|
||||
return f(t0, t1, t2, t3, t4, t5, t6, t7, t8, t9)
|
||||
})
|
||||
return TryCatchError(f(t0, t1, t2, t3, t4, t5, t6, t7, t8, t9))
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -35,17 +35,13 @@ var (
|
||||
func bodyRequest(method string) func(string) func([]byte) E.Either[error, *http.Request] {
|
||||
return func(url string) func([]byte) E.Either[error, *http.Request] {
|
||||
return func(body []byte) E.Either[error, *http.Request] {
|
||||
return E.TryCatchError(func() (*http.Request, error) {
|
||||
return http.NewRequest(method, url, bytes.NewReader(body))
|
||||
})
|
||||
return E.TryCatchError(http.NewRequest(method, url, bytes.NewReader(body)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func noBodyRequest(method string) func(string) E.Either[error, *http.Request] {
|
||||
return func(url string) E.Either[error, *http.Request] {
|
||||
return E.TryCatchError(func() (*http.Request, error) {
|
||||
return http.NewRequest(method, url, nil)
|
||||
})
|
||||
return E.TryCatchError(http.NewRequest(method, url, nil))
|
||||
}
|
||||
}
|
||||
|
40
either/monoid.go
Normal file
40
either/monoid.go
Normal file
@@ -0,0 +1,40 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package either
|
||||
|
||||
import (
|
||||
L "github.com/IBM/fp-go/lazy"
|
||||
M "github.com/IBM/fp-go/monoid"
|
||||
)
|
||||
|
||||
// AlternativeMonoid is the alternative [Monoid] for an [Either]
|
||||
func AlternativeMonoid[E, A any](m M.Monoid[A]) M.Monoid[Either[E, A]] {
|
||||
return M.AlternativeMonoid(
|
||||
Of[E, A],
|
||||
MonadMap[E, A, func(A) A],
|
||||
MonadAp[A, E, A],
|
||||
MonadAlt[E, A],
|
||||
m,
|
||||
)
|
||||
}
|
||||
|
||||
// AltMonoid is the alternative [Monoid] for an [Either]
|
||||
func AltMonoid[E, A any](zero L.Lazy[Either[E, A]]) M.Monoid[Either[E, A]] {
|
||||
return M.AltMonoid(
|
||||
zero,
|
||||
MonadAlt[E, A],
|
||||
)
|
||||
}
|
@@ -20,7 +20,7 @@ import (
|
||||
RR "github.com/IBM/fp-go/internal/record"
|
||||
)
|
||||
|
||||
// TraverseRecord transforms a record of options into an option of a record
|
||||
// TraverseRecordG transforms a record of options into an option of a record
|
||||
func TraverseRecordG[GA ~map[K]A, GB ~map[K]B, K comparable, E, A, B any](f func(A) Either[E, B]) func(GA) Either[E, GB] {
|
||||
return RR.Traverse[GA](
|
||||
Of[E, GB],
|
||||
@@ -35,6 +35,21 @@ func TraverseRecord[K comparable, E, A, B any](f func(A) Either[E, B]) func(map[
|
||||
return TraverseRecordG[map[K]A, map[K]B](f)
|
||||
}
|
||||
|
||||
// TraverseRecordWithIndexG transforms a record of options into an option of a record
|
||||
func TraverseRecordWithIndexG[GA ~map[K]A, GB ~map[K]B, K comparable, E, A, B any](f func(K, A) Either[E, B]) func(GA) Either[E, GB] {
|
||||
return RR.TraverseWithIndex[GA](
|
||||
Of[E, GB],
|
||||
Map[E, GB, func(B) GB],
|
||||
Ap[GB, E, B],
|
||||
f,
|
||||
)
|
||||
}
|
||||
|
||||
// TraverseRecordWithIndex transforms a record of eithers into an either of a record
|
||||
func TraverseRecordWithIndex[K comparable, E, A, B any](f func(K, A) Either[E, B]) func(map[K]A) Either[E, map[K]B] {
|
||||
return TraverseRecordWithIndexG[map[K]A, map[K]B](f)
|
||||
}
|
||||
|
||||
func SequenceRecordG[GA ~map[K]A, GOA ~map[K]Either[E, A], K comparable, E, A any](ma GOA) Either[E, GA] {
|
||||
return TraverseRecordG[GOA, GA](F.Identity[Either[E, A]])(ma)
|
||||
}
|
||||
@@ -43,3 +58,24 @@ func SequenceRecordG[GA ~map[K]A, GOA ~map[K]Either[E, A], K comparable, E, A an
|
||||
func SequenceRecord[K comparable, E, A any](ma map[K]Either[E, A]) Either[E, map[K]A] {
|
||||
return SequenceRecordG[map[K]A](ma)
|
||||
}
|
||||
|
||||
func upsertAtReadWrite[M ~map[K]V, K comparable, V any](r M, k K, v V) M {
|
||||
r[k] = v
|
||||
return r
|
||||
}
|
||||
|
||||
// CompactRecordG discards the noe values and keeps the some values
|
||||
func CompactRecordG[M1 ~map[K]Either[E, A], M2 ~map[K]A, K comparable, E, A any](m M1) M2 {
|
||||
out := make(M2)
|
||||
onLeft := F.Constant1[E](out)
|
||||
return RR.ReduceWithIndex(m, func(key K, _ M2, value Either[E, A]) M2 {
|
||||
return MonadFold(value, onLeft, func(v A) M2 {
|
||||
return upsertAtReadWrite(out, key, v)
|
||||
})
|
||||
}, out)
|
||||
}
|
||||
|
||||
// CompactRecord discards all none values and keeps the somes
|
||||
func CompactRecord[K comparable, E, A any](m map[K]Either[E, A]) map[K]A {
|
||||
return CompactRecordG[map[K]Either[E, A], map[K]A](m)
|
||||
}
|
||||
|
37
either/record_test.go
Normal file
37
either/record_test.go
Normal file
@@ -0,0 +1,37 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package either
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestCompactRecord(t *testing.T) {
|
||||
// make the map
|
||||
m := make(map[string]Either[string, int])
|
||||
m["foo"] = Left[int]("error")
|
||||
m["bar"] = Right[string](1)
|
||||
// compact it
|
||||
m1 := CompactRecord(m)
|
||||
// check expected
|
||||
exp := map[string]int{
|
||||
"bar": 1,
|
||||
}
|
||||
|
||||
assert.Equal(t, exp, m1)
|
||||
}
|
@@ -25,20 +25,14 @@ import (
|
||||
|
||||
func TestWithResource(t *testing.T) {
|
||||
onCreate := func() Either[error, *os.File] {
|
||||
return TryCatchError(func() (*os.File, error) {
|
||||
return os.CreateTemp("", "*")
|
||||
})
|
||||
return TryCatchError(os.CreateTemp("", "*"))
|
||||
}
|
||||
onDelete := F.Flow2(
|
||||
func(f *os.File) Either[error, string] {
|
||||
return TryCatchError(func() (string, error) {
|
||||
return f.Name(), f.Close()
|
||||
})
|
||||
return TryCatchError(f.Name(), f.Close())
|
||||
},
|
||||
Chain(func(name string) Either[error, any] {
|
||||
return TryCatchError(func() (any, error) {
|
||||
return name, os.Remove(name)
|
||||
})
|
||||
return TryCatchError(any(name), os.Remove(name))
|
||||
}),
|
||||
)
|
||||
|
||||
|
@@ -13,17 +13,15 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package file
|
||||
package either
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
IOE "github.com/IBM/fp-go/ioeither"
|
||||
S "github.com/IBM/fp-go/semigroup"
|
||||
)
|
||||
|
||||
// onClose closes a closeable resource
|
||||
func onClose[R io.Closer](r R) IOE.IOEither[error, R] {
|
||||
return IOE.TryCatchError(func() (R, error) {
|
||||
return r, r.Close()
|
||||
})
|
||||
// AltSemigroup is the alternative [Semigroup] for an [Either]
|
||||
func AltSemigroup[E, A any]() S.Semigroup[Either[E, A]] {
|
||||
return S.AltSemigroup(
|
||||
MonadAlt[E, A],
|
||||
)
|
||||
}
|
@@ -27,13 +27,13 @@ HKTRB = HKT<Either[B]>
|
||||
HKTA = HKT<A>
|
||||
HKTB = HKT<B>
|
||||
*/
|
||||
func traverse[E, A, B, HKTA, HKTB, HKTRB any](
|
||||
_of func(Either[E, B]) HKTRB,
|
||||
_map func(HKTB, func(B) Either[E, B]) HKTRB,
|
||||
func traverse[E, A, B, HKTB, HKTRB any](
|
||||
mof func(Either[E, B]) HKTRB,
|
||||
mmap func(func(B) Either[E, B]) func(HKTB) HKTRB,
|
||||
) func(Either[E, A], func(A) HKTB) HKTRB {
|
||||
|
||||
left := F.Flow2(Left[B, E], _of)
|
||||
right := F.Bind2nd(_map, Right[E, B])
|
||||
left := F.Flow2(Left[B, E], mof)
|
||||
right := mmap(Right[E, B])
|
||||
|
||||
return func(ta Either[E, A], f func(A) HKTB) HKTRB {
|
||||
return MonadFold(ta,
|
||||
@@ -43,27 +43,21 @@ func traverse[E, A, B, HKTA, HKTB, HKTRB any](
|
||||
}
|
||||
}
|
||||
|
||||
func Traverse[E, A, B, HKTA, HKTB, HKTRB any](
|
||||
_of func(Either[E, B]) HKTRB,
|
||||
_map func(HKTB, func(B) Either[E, B]) HKTRB,
|
||||
// Traverse converts an [Either] of some higher kinded type into the higher kinded type of an [Either]
|
||||
func Traverse[A, E, B, HKTB, HKTRB any](
|
||||
mof func(Either[E, B]) HKTRB,
|
||||
mmap func(func(B) Either[E, B]) func(HKTB) HKTRB,
|
||||
) func(func(A) HKTB) func(Either[E, A]) HKTRB {
|
||||
delegate := traverse[E, A, B, HKTA](_of, _map)
|
||||
delegate := traverse[E, A, B](mof, mmap)
|
||||
return func(f func(A) HKTB) func(Either[E, A]) HKTRB {
|
||||
return F.Bind2nd(delegate, f)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
We need to pass the members of the applicative explicitly, because golang does neither support higher kinded types nor template methods on structs or interfaces
|
||||
|
||||
HKTRA = HKT<Either[A]>
|
||||
HKTA = HKT<A>
|
||||
HKTB = HKT<B>
|
||||
*/
|
||||
// Sequence converts an [Either] of some higher kinded type into the higher kinded type of an [Either]
|
||||
func Sequence[E, A, HKTA, HKTRA any](
|
||||
_of func(Either[E, A]) HKTRA,
|
||||
_map func(HKTA, func(A) Either[E, A]) HKTRA,
|
||||
mof func(Either[E, A]) HKTRA,
|
||||
mmap func(func(A) Either[E, A]) func(HKTA) HKTRA,
|
||||
) func(Either[E, HKTA]) HKTRA {
|
||||
return Fold(F.Flow2(Left[A, E], _of), F.Bind2nd(_map, Right[E, A]))
|
||||
return Fold(F.Flow2(Left[A, E], mof), mmap(Right[E, A]))
|
||||
}
|
||||
|
@@ -30,9 +30,9 @@ func TestTraverse(t *testing.T) {
|
||||
}
|
||||
return O.None[int]()
|
||||
}
|
||||
trav := Traverse[string, int, int, O.Option[Either[string, int]]](
|
||||
trav := Traverse[int](
|
||||
O.Of[Either[string, int]],
|
||||
O.MonadMap[int, Either[string, int]],
|
||||
O.Map[int, Either[string, int]],
|
||||
)(f)
|
||||
|
||||
assert.Equal(t, O.Of(Left[int]("a")), F.Pipe1(Left[int]("a"), trav))
|
||||
@@ -44,7 +44,7 @@ func TestSequence(t *testing.T) {
|
||||
|
||||
seq := Sequence(
|
||||
O.Of[Either[string, int]],
|
||||
O.MonadMap[int, Either[string, int]],
|
||||
O.Map[int, Either[string, int]],
|
||||
)
|
||||
|
||||
assert.Equal(t, O.Of(Right[string](1)), seq(Right[string](O.Of(1))))
|
||||
|
@@ -17,80 +17,60 @@ package either
|
||||
|
||||
func Variadic0[V, R any](f func([]V) (R, error)) func(...V) Either[error, R] {
|
||||
return func(v ...V) Either[error, R] {
|
||||
return TryCatchError(func() (R, error) {
|
||||
return f(v)
|
||||
})
|
||||
return TryCatchError(f(v))
|
||||
}
|
||||
}
|
||||
|
||||
func Variadic1[T1, V, R any](f func(T1, []V) (R, error)) func(T1, ...V) Either[error, R] {
|
||||
return func(t1 T1, v ...V) Either[error, R] {
|
||||
return TryCatchError(func() (R, error) {
|
||||
return f(t1, v)
|
||||
})
|
||||
return TryCatchError(f(t1, v))
|
||||
}
|
||||
}
|
||||
|
||||
func Variadic2[T1, T2, V, R any](f func(T1, T2, []V) (R, error)) func(T1, T2, ...V) Either[error, R] {
|
||||
return func(t1 T1, t2 T2, v ...V) Either[error, R] {
|
||||
return TryCatchError(func() (R, error) {
|
||||
return f(t1, t2, v)
|
||||
})
|
||||
return TryCatchError(f(t1, t2, v))
|
||||
}
|
||||
}
|
||||
|
||||
func Variadic3[T1, T2, T3, V, R any](f func(T1, T2, T3, []V) (R, error)) func(T1, T2, T3, ...V) Either[error, R] {
|
||||
return func(t1 T1, t2 T2, t3 T3, v ...V) Either[error, R] {
|
||||
return TryCatchError(func() (R, error) {
|
||||
return f(t1, t2, t3, v)
|
||||
})
|
||||
return TryCatchError(f(t1, t2, t3, v))
|
||||
}
|
||||
}
|
||||
|
||||
func Variadic4[T1, T2, T3, T4, V, R any](f func(T1, T2, T3, T4, []V) (R, error)) func(T1, T2, T3, T4, ...V) Either[error, R] {
|
||||
return func(t1 T1, t2 T2, t3 T3, t4 T4, v ...V) Either[error, R] {
|
||||
return TryCatchError(func() (R, error) {
|
||||
return f(t1, t2, t3, t4, v)
|
||||
})
|
||||
return TryCatchError(f(t1, t2, t3, t4, v))
|
||||
}
|
||||
}
|
||||
|
||||
func Unvariadic0[V, R any](f func(...V) (R, error)) func([]V) Either[error, R] {
|
||||
return func(v []V) Either[error, R] {
|
||||
return TryCatchError(func() (R, error) {
|
||||
return f(v...)
|
||||
})
|
||||
return TryCatchError(f(v...))
|
||||
}
|
||||
}
|
||||
|
||||
func Unvariadic1[T1, V, R any](f func(T1, ...V) (R, error)) func(T1, []V) Either[error, R] {
|
||||
return func(t1 T1, v []V) Either[error, R] {
|
||||
return TryCatchError(func() (R, error) {
|
||||
return f(t1, v...)
|
||||
})
|
||||
return TryCatchError(f(t1, v...))
|
||||
}
|
||||
}
|
||||
|
||||
func Unvariadic2[T1, T2, V, R any](f func(T1, T2, ...V) (R, error)) func(T1, T2, []V) Either[error, R] {
|
||||
return func(t1 T1, t2 T2, v []V) Either[error, R] {
|
||||
return TryCatchError(func() (R, error) {
|
||||
return f(t1, t2, v...)
|
||||
})
|
||||
return TryCatchError(f(t1, t2, v...))
|
||||
}
|
||||
}
|
||||
|
||||
func Unvariadic3[T1, T2, T3, V, R any](f func(T1, T2, T3, ...V) (R, error)) func(T1, T2, T3, []V) Either[error, R] {
|
||||
return func(t1 T1, t2 T2, t3 T3, v []V) Either[error, R] {
|
||||
return TryCatchError(func() (R, error) {
|
||||
return f(t1, t2, t3, v...)
|
||||
})
|
||||
return TryCatchError(f(t1, t2, t3, v...))
|
||||
}
|
||||
}
|
||||
|
||||
func Unvariadic4[T1, T2, T3, T4, V, R any](f func(T1, T2, T3, T4, ...V) (R, error)) func(T1, T2, T3, T4, []V) Either[error, R] {
|
||||
return func(t1 T1, t2 T2, t3 T3, t4 T4, v []V) Either[error, R] {
|
||||
return TryCatchError(func() (R, error) {
|
||||
return f(t1, t2, t3, t4, v...)
|
||||
})
|
||||
return TryCatchError(f(t1, t2, t3, t4, v...))
|
||||
}
|
||||
}
|
||||
|
@@ -28,6 +28,22 @@ func OnNone(msg string, args ...any) func() error {
|
||||
}
|
||||
}
|
||||
|
||||
// OnSome generates a unary function that produces a formatted error
|
||||
func OnSome[T any](msg string, args ...any) func(T) error {
|
||||
l := len(args)
|
||||
if l == 0 {
|
||||
return func(value T) error {
|
||||
return fmt.Errorf(msg, value)
|
||||
}
|
||||
}
|
||||
return func(value T) error {
|
||||
data := make([]any, l)
|
||||
copy(data[1:], args)
|
||||
data[0] = value
|
||||
return fmt.Errorf(msg, data...)
|
||||
}
|
||||
}
|
||||
|
||||
// OnError generates a unary function that produces a formatted error. The argument
|
||||
// to that function is the root cause of the error and the message will be augmented with
|
||||
// a format string containing %w
|
||||
|
@@ -20,11 +20,14 @@ import (
|
||||
)
|
||||
|
||||
type (
|
||||
// command output
|
||||
// CommandOutput represents the output of executing a command. The first field in the [Tuple2] is
|
||||
// stdout, the second one is stderr. Use [StdOut] and [StdErr] to access these fields
|
||||
CommandOutput = T.Tuple2[[]byte, []byte]
|
||||
)
|
||||
|
||||
var (
|
||||
// StdOut returns the field of a [CommandOutput] representing `stdout`
|
||||
StdOut = T.First[[]byte, []byte]
|
||||
// StdErr returns the field of a [CommandOutput] representing `stderr`
|
||||
StdErr = T.Second[[]byte, []byte]
|
||||
)
|
||||
|
@@ -15,9 +15,11 @@
|
||||
|
||||
package file
|
||||
|
||||
import "os"
|
||||
import "path/filepath"
|
||||
|
||||
// GetName is the getter for the `Name` property of [os.File]
|
||||
func GetName(f *os.File) string {
|
||||
return f.Name()
|
||||
// Join appends a filename to a root path
|
||||
func Join(name string) func(root string) string {
|
||||
return func(root string) string {
|
||||
return filepath.Join(root, name)
|
||||
}
|
||||
}
|
||||
|
@@ -26,6 +26,7 @@ func Bind2nd[T1, T2, R any](f func(T1, T2) R, t2 T2) func(T1) R {
|
||||
}
|
||||
}
|
||||
|
||||
// SK function (SKI combinator calculus).
|
||||
func SK[T1, T2 any](_ T1, t2 T2) T2 {
|
||||
return t2
|
||||
}
|
||||
|
@@ -1,21 +1,7 @@
|
||||
// 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.
|
||||
|
||||
// Code generated by go generate; DO NOT EDIT.
|
||||
// This file was generated by robots at
|
||||
// 2023-07-28 22:48:40.161663 +0200 CEST m=+0.009458101
|
||||
// 2023-10-23 08:30:44.6474482 +0200 CEST m=+0.150851901
|
||||
|
||||
package function
|
||||
|
||||
// Combinations for a total of 1 arguments
|
||||
|
30
function/cache.go
Normal file
30
function/cache.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 function
|
||||
|
||||
import (
|
||||
G "github.com/IBM/fp-go/function/generic"
|
||||
)
|
||||
|
||||
// Memoize converts a unary function into a unary function that caches the value depending on the parameter
|
||||
func Memoize[K comparable, T any](f func(K) T) func(K) T {
|
||||
return G.Memoize(f)
|
||||
}
|
||||
|
||||
// ContramapMemoize converts a unary function into a unary function that caches the value depending on the parameter
|
||||
func ContramapMemoize[A any, K comparable, T any](kf func(A) K) func(func(A) T) func(A) T {
|
||||
return G.ContramapMemoize[func(A) T](kf)
|
||||
}
|
50
function/cache_test.go
Normal file
50
function/cache_test.go
Normal file
@@ -0,0 +1,50 @@
|
||||
// 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 (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestCache(t *testing.T) {
|
||||
var count int
|
||||
|
||||
withSideEffect := func(n int) int {
|
||||
count++
|
||||
return n
|
||||
}
|
||||
|
||||
cached := Memoize(withSideEffect)
|
||||
|
||||
assert.Equal(t, 0, count)
|
||||
|
||||
assert.Equal(t, 10, cached(10))
|
||||
assert.Equal(t, 1, count)
|
||||
|
||||
assert.Equal(t, 10, cached(10))
|
||||
assert.Equal(t, 1, count)
|
||||
|
||||
assert.Equal(t, 20, cached(20))
|
||||
assert.Equal(t, 2, count)
|
||||
|
||||
assert.Equal(t, 20, cached(20))
|
||||
assert.Equal(t, 2, count)
|
||||
|
||||
assert.Equal(t, 10, cached(10))
|
||||
assert.Equal(t, 2, count)
|
||||
}
|
@@ -13,6 +13,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package function implements function composition primitives, most prominently [Pipe2] and [Flow2]
|
||||
package function
|
||||
|
||||
//go:generate go run .. pipe --count 20 --filename gen.go
|
||||
|
@@ -62,6 +62,7 @@ func First[T1, T2 any](t1 T1, _ T2) T1 {
|
||||
}
|
||||
|
||||
// Second returns the second out of two input values
|
||||
// Identical to [SK]
|
||||
func Second[T1, T2 any](_ T1, t2 T2) T2 {
|
||||
return t2
|
||||
}
|
||||
|
165
function/gen.go
165
function/gen.go
@@ -1,21 +1,7 @@
|
||||
// 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.
|
||||
|
||||
// Code generated by go generate; DO NOT EDIT.
|
||||
// This file was generated by robots at
|
||||
// 2023-07-28 22:48:27.8029852 +0200 CEST m=+0.051432001
|
||||
// 2023-10-23 08:30:41.7972101 +0200 CEST m=+0.008029101
|
||||
|
||||
package function
|
||||
|
||||
// Pipe0 takes an initial value t0 and successively applies 0 functions where the input of a function is the return value of the previous function
|
||||
@@ -38,6 +24,13 @@ func Unvariadic0[V, R any](f func(...V) R) func([]V) R {
|
||||
}
|
||||
}
|
||||
|
||||
// Unsliced0 converts a function taking a slice parameter into a function with 0 parameters
|
||||
func Unsliced0[F ~func([]T) R, T, R any](f F) func() R {
|
||||
return func() R {
|
||||
return f([]T{})
|
||||
}
|
||||
}
|
||||
|
||||
// Pipe1 takes an initial value t0 and successively applies 1 functions where the input of a function is the return value of the previous function
|
||||
// The final return value is the result of the last function application
|
||||
func Pipe1[F1 ~func(T0) T1, T0, T1 any](t0 T0, f1 F1) T1 {
|
||||
@@ -90,6 +83,13 @@ func Unvariadic1[T1, V, R any](f func(T1, ...V) R) func(T1, []V) R {
|
||||
}
|
||||
}
|
||||
|
||||
// Unsliced1 converts a function taking a slice parameter into a function with 1 parameters
|
||||
func Unsliced1[F ~func([]T) R, T, R any](f F) func(T) R {
|
||||
return func(t1 T) R {
|
||||
return f([]T{t1})
|
||||
}
|
||||
}
|
||||
|
||||
// Pipe2 takes an initial value t0 and successively applies 2 functions where the input of a function is the return value of the previous function
|
||||
// The final return value is the result of the last function application
|
||||
func Pipe2[F1 ~func(T0) T1, F2 ~func(T1) T2, T0, T1, T2 any](t0 T0, f1 F1, f2 F2) T2 {
|
||||
@@ -145,6 +145,13 @@ func Unvariadic2[T1, T2, V, R any](f func(T1, T2, ...V) R) func(T1, T2, []V) R {
|
||||
}
|
||||
}
|
||||
|
||||
// Unsliced2 converts a function taking a slice parameter into a function with 2 parameters
|
||||
func Unsliced2[F ~func([]T) R, T, R any](f F) func(T, T) R {
|
||||
return func(t1, t2 T) R {
|
||||
return f([]T{t1, t2})
|
||||
}
|
||||
}
|
||||
|
||||
// Pipe3 takes an initial value t0 and successively applies 3 functions where the input of a function is the return value of the previous function
|
||||
// The final return value is the result of the last function application
|
||||
func Pipe3[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, T0, T1, T2, T3 any](t0 T0, f1 F1, f2 F2, f3 F3) T3 {
|
||||
@@ -203,6 +210,13 @@ func Unvariadic3[T1, T2, T3, V, R any](f func(T1, T2, T3, ...V) R) func(T1, T2,
|
||||
}
|
||||
}
|
||||
|
||||
// Unsliced3 converts a function taking a slice parameter into a function with 3 parameters
|
||||
func Unsliced3[F ~func([]T) R, T, R any](f F) func(T, T, T) R {
|
||||
return func(t1, t2, t3 T) R {
|
||||
return f([]T{t1, t2, t3})
|
||||
}
|
||||
}
|
||||
|
||||
// Pipe4 takes an initial value t0 and successively applies 4 functions where the input of a function is the return value of the previous function
|
||||
// The final return value is the result of the last function application
|
||||
func Pipe4[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, T0, T1, T2, T3, T4 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4) T4 {
|
||||
@@ -264,6 +278,13 @@ func Unvariadic4[T1, T2, T3, T4, V, R any](f func(T1, T2, T3, T4, ...V) R) func(
|
||||
}
|
||||
}
|
||||
|
||||
// Unsliced4 converts a function taking a slice parameter into a function with 4 parameters
|
||||
func Unsliced4[F ~func([]T) R, T, R any](f F) func(T, T, T, T) R {
|
||||
return func(t1, t2, t3, t4 T) R {
|
||||
return f([]T{t1, t2, t3, t4})
|
||||
}
|
||||
}
|
||||
|
||||
// Pipe5 takes an initial value t0 and successively applies 5 functions where the input of a function is the return value of the previous function
|
||||
// The final return value is the result of the last function application
|
||||
func Pipe5[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, T0, T1, T2, T3, T4, T5 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5) T5 {
|
||||
@@ -328,6 +349,13 @@ func Unvariadic5[T1, T2, T3, T4, T5, V, R any](f func(T1, T2, T3, T4, T5, ...V)
|
||||
}
|
||||
}
|
||||
|
||||
// Unsliced5 converts a function taking a slice parameter into a function with 5 parameters
|
||||
func Unsliced5[F ~func([]T) R, T, R any](f F) func(T, T, T, T, T) R {
|
||||
return func(t1, t2, t3, t4, t5 T) R {
|
||||
return f([]T{t1, t2, t3, t4, t5})
|
||||
}
|
||||
}
|
||||
|
||||
// Pipe6 takes an initial value t0 and successively applies 6 functions where the input of a function is the return value of the previous function
|
||||
// The final return value is the result of the last function application
|
||||
func Pipe6[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, T0, T1, T2, T3, T4, T5, T6 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6) T6 {
|
||||
@@ -395,6 +423,13 @@ func Unvariadic6[T1, T2, T3, T4, T5, T6, V, R any](f func(T1, T2, T3, T4, T5, T6
|
||||
}
|
||||
}
|
||||
|
||||
// Unsliced6 converts a function taking a slice parameter into a function with 6 parameters
|
||||
func Unsliced6[F ~func([]T) R, T, R any](f F) func(T, T, T, T, T, T) R {
|
||||
return func(t1, t2, t3, t4, t5, t6 T) R {
|
||||
return f([]T{t1, t2, t3, t4, t5, t6})
|
||||
}
|
||||
}
|
||||
|
||||
// Pipe7 takes an initial value t0 and successively applies 7 functions where the input of a function is the return value of the previous function
|
||||
// The final return value is the result of the last function application
|
||||
func Pipe7[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, T0, T1, T2, T3, T4, T5, T6, T7 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7) T7 {
|
||||
@@ -465,6 +500,13 @@ func Unvariadic7[T1, T2, T3, T4, T5, T6, T7, V, R any](f func(T1, T2, T3, T4, T5
|
||||
}
|
||||
}
|
||||
|
||||
// Unsliced7 converts a function taking a slice parameter into a function with 7 parameters
|
||||
func Unsliced7[F ~func([]T) R, T, R any](f F) func(T, T, T, T, T, T, T) R {
|
||||
return func(t1, t2, t3, t4, t5, t6, t7 T) R {
|
||||
return f([]T{t1, t2, t3, t4, t5, t6, t7})
|
||||
}
|
||||
}
|
||||
|
||||
// Pipe8 takes an initial value t0 and successively applies 8 functions where the input of a function is the return value of the previous function
|
||||
// The final return value is the result of the last function application
|
||||
func Pipe8[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, T0, T1, T2, T3, T4, T5, T6, T7, T8 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8) T8 {
|
||||
@@ -538,6 +580,13 @@ func Unvariadic8[T1, T2, T3, T4, T5, T6, T7, T8, V, R any](f func(T1, T2, T3, T4
|
||||
}
|
||||
}
|
||||
|
||||
// Unsliced8 converts a function taking a slice parameter into a function with 8 parameters
|
||||
func Unsliced8[F ~func([]T) R, T, R any](f F) func(T, T, T, T, T, T, T, T) R {
|
||||
return func(t1, t2, t3, t4, t5, t6, t7, t8 T) R {
|
||||
return f([]T{t1, t2, t3, t4, t5, t6, t7, t8})
|
||||
}
|
||||
}
|
||||
|
||||
// Pipe9 takes an initial value t0 and successively applies 9 functions where the input of a function is the return value of the previous function
|
||||
// The final return value is the result of the last function application
|
||||
func Pipe9[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9) T9 {
|
||||
@@ -614,6 +663,13 @@ func Unvariadic9[T1, T2, T3, T4, T5, T6, T7, T8, T9, V, R any](f func(T1, T2, T3
|
||||
}
|
||||
}
|
||||
|
||||
// Unsliced9 converts a function taking a slice parameter into a function with 9 parameters
|
||||
func Unsliced9[F ~func([]T) R, T, R any](f F) func(T, T, T, T, T, T, T, T, T) R {
|
||||
return func(t1, t2, t3, t4, t5, t6, t7, t8, t9 T) R {
|
||||
return f([]T{t1, t2, t3, t4, t5, t6, t7, t8, t9})
|
||||
}
|
||||
}
|
||||
|
||||
// Pipe10 takes an initial value t0 and successively applies 10 functions where the input of a function is the return value of the previous function
|
||||
// The final return value is the result of the last function application
|
||||
func Pipe10[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10) T10 {
|
||||
@@ -693,6 +749,13 @@ func Unvariadic10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, V, R any](f func(T1,
|
||||
}
|
||||
}
|
||||
|
||||
// Unsliced10 converts a function taking a slice parameter into a function with 10 parameters
|
||||
func Unsliced10[F ~func([]T) R, T, R any](f F) func(T, T, T, T, T, T, T, T, T, T) R {
|
||||
return func(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10 T) R {
|
||||
return f([]T{t1, t2, t3, t4, t5, t6, t7, t8, t9, t10})
|
||||
}
|
||||
}
|
||||
|
||||
// Pipe11 takes an initial value t0 and successively applies 11 functions where the input of a function is the return value of the previous function
|
||||
// The final return value is the result of the last function application
|
||||
func Pipe11[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, F11 ~func(T10) T11, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11) T11 {
|
||||
@@ -775,6 +838,13 @@ func Unvariadic11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, V, R any](f func
|
||||
}
|
||||
}
|
||||
|
||||
// Unsliced11 converts a function taking a slice parameter into a function with 11 parameters
|
||||
func Unsliced11[F ~func([]T) R, T, R any](f F) func(T, T, T, T, T, T, T, T, T, T, T) R {
|
||||
return func(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11 T) R {
|
||||
return f([]T{t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11})
|
||||
}
|
||||
}
|
||||
|
||||
// Pipe12 takes an initial value t0 and successively applies 12 functions where the input of a function is the return value of the previous function
|
||||
// The final return value is the result of the last function application
|
||||
func Pipe12[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, F11 ~func(T10) T11, F12 ~func(T11) T12, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12) T12 {
|
||||
@@ -860,6 +930,13 @@ func Unvariadic12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, V, R any](f
|
||||
}
|
||||
}
|
||||
|
||||
// Unsliced12 converts a function taking a slice parameter into a function with 12 parameters
|
||||
func Unsliced12[F ~func([]T) R, T, R any](f F) func(T, T, T, T, T, T, T, T, T, T, T, T) R {
|
||||
return func(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12 T) R {
|
||||
return f([]T{t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12})
|
||||
}
|
||||
}
|
||||
|
||||
// Pipe13 takes an initial value t0 and successively applies 13 functions where the input of a function is the return value of the previous function
|
||||
// The final return value is the result of the last function application
|
||||
func Pipe13[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, F11 ~func(T10) T11, F12 ~func(T11) T12, F13 ~func(T12) T13, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12, f13 F13) T13 {
|
||||
@@ -948,6 +1025,13 @@ func Unvariadic13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, V, R a
|
||||
}
|
||||
}
|
||||
|
||||
// Unsliced13 converts a function taking a slice parameter into a function with 13 parameters
|
||||
func Unsliced13[F ~func([]T) R, T, R any](f F) func(T, T, T, T, T, T, T, T, T, T, T, T, T) R {
|
||||
return func(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13 T) R {
|
||||
return f([]T{t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13})
|
||||
}
|
||||
}
|
||||
|
||||
// Pipe14 takes an initial value t0 and successively applies 14 functions where the input of a function is the return value of the previous function
|
||||
// The final return value is the result of the last function application
|
||||
func Pipe14[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, F11 ~func(T10) T11, F12 ~func(T11) T12, F13 ~func(T12) T13, F14 ~func(T13) T14, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12, f13 F13, f14 F14) T14 {
|
||||
@@ -1039,6 +1123,13 @@ func Unvariadic14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, V
|
||||
}
|
||||
}
|
||||
|
||||
// Unsliced14 converts a function taking a slice parameter into a function with 14 parameters
|
||||
func Unsliced14[F ~func([]T) R, T, R any](f F) func(T, T, T, T, T, T, T, T, T, T, T, T, T, T) R {
|
||||
return func(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14 T) R {
|
||||
return f([]T{t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14})
|
||||
}
|
||||
}
|
||||
|
||||
// Pipe15 takes an initial value t0 and successively applies 15 functions where the input of a function is the return value of the previous function
|
||||
// The final return value is the result of the last function application
|
||||
func Pipe15[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, F11 ~func(T10) T11, F12 ~func(T11) T12, F13 ~func(T12) T13, F14 ~func(T13) T14, F15 ~func(T14) T15, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12, f13 F13, f14 F14, f15 F15) T15 {
|
||||
@@ -1133,6 +1224,13 @@ func Unvariadic15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T
|
||||
}
|
||||
}
|
||||
|
||||
// Unsliced15 converts a function taking a slice parameter into a function with 15 parameters
|
||||
func Unsliced15[F ~func([]T) R, T, R any](f F) func(T, T, T, T, T, T, T, T, T, T, T, T, T, T, T) R {
|
||||
return func(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15 T) R {
|
||||
return f([]T{t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15})
|
||||
}
|
||||
}
|
||||
|
||||
// Pipe16 takes an initial value t0 and successively applies 16 functions where the input of a function is the return value of the previous function
|
||||
// The final return value is the result of the last function application
|
||||
func Pipe16[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, F11 ~func(T10) T11, F12 ~func(T11) T12, F13 ~func(T12) T13, F14 ~func(T13) T14, F15 ~func(T14) T15, F16 ~func(T15) T16, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12, f13 F13, f14 F14, f15 F15, f16 F16) T16 {
|
||||
@@ -1230,6 +1328,13 @@ func Unvariadic16[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T
|
||||
}
|
||||
}
|
||||
|
||||
// Unsliced16 converts a function taking a slice parameter into a function with 16 parameters
|
||||
func Unsliced16[F ~func([]T) R, T, R any](f F) func(T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T) R {
|
||||
return func(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16 T) R {
|
||||
return f([]T{t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16})
|
||||
}
|
||||
}
|
||||
|
||||
// Pipe17 takes an initial value t0 and successively applies 17 functions where the input of a function is the return value of the previous function
|
||||
// The final return value is the result of the last function application
|
||||
func Pipe17[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, F11 ~func(T10) T11, F12 ~func(T11) T12, F13 ~func(T12) T13, F14 ~func(T13) T14, F15 ~func(T14) T15, F16 ~func(T15) T16, F17 ~func(T16) T17, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12, f13 F13, f14 F14, f15 F15, f16 F16, f17 F17) T17 {
|
||||
@@ -1330,6 +1435,13 @@ func Unvariadic17[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T
|
||||
}
|
||||
}
|
||||
|
||||
// Unsliced17 converts a function taking a slice parameter into a function with 17 parameters
|
||||
func Unsliced17[F ~func([]T) R, T, R any](f F) func(T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T) R {
|
||||
return func(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17 T) R {
|
||||
return f([]T{t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17})
|
||||
}
|
||||
}
|
||||
|
||||
// Pipe18 takes an initial value t0 and successively applies 18 functions where the input of a function is the return value of the previous function
|
||||
// The final return value is the result of the last function application
|
||||
func Pipe18[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, F11 ~func(T10) T11, F12 ~func(T11) T12, F13 ~func(T12) T13, F14 ~func(T13) T14, F15 ~func(T14) T15, F16 ~func(T15) T16, F17 ~func(T16) T17, F18 ~func(T17) T18, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12, f13 F13, f14 F14, f15 F15, f16 F16, f17 F17, f18 F18) T18 {
|
||||
@@ -1433,6 +1545,13 @@ func Unvariadic18[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T
|
||||
}
|
||||
}
|
||||
|
||||
// Unsliced18 converts a function taking a slice parameter into a function with 18 parameters
|
||||
func Unsliced18[F ~func([]T) R, T, R any](f F) func(T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T) R {
|
||||
return func(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18 T) R {
|
||||
return f([]T{t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18})
|
||||
}
|
||||
}
|
||||
|
||||
// Pipe19 takes an initial value t0 and successively applies 19 functions where the input of a function is the return value of the previous function
|
||||
// The final return value is the result of the last function application
|
||||
func Pipe19[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, F11 ~func(T10) T11, F12 ~func(T11) T12, F13 ~func(T12) T13, F14 ~func(T13) T14, F15 ~func(T14) T15, F16 ~func(T15) T16, F17 ~func(T16) T17, F18 ~func(T17) T18, F19 ~func(T18) T19, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12, f13 F13, f14 F14, f15 F15, f16 F16, f17 F17, f18 F18, f19 F19) T19 {
|
||||
@@ -1539,6 +1658,13 @@ func Unvariadic19[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T
|
||||
}
|
||||
}
|
||||
|
||||
// Unsliced19 converts a function taking a slice parameter into a function with 19 parameters
|
||||
func Unsliced19[F ~func([]T) R, T, R any](f F) func(T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T) R {
|
||||
return func(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18, t19 T) R {
|
||||
return f([]T{t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18, t19})
|
||||
}
|
||||
}
|
||||
|
||||
// Pipe20 takes an initial value t0 and successively applies 20 functions where the input of a function is the return value of the previous function
|
||||
// The final return value is the result of the last function application
|
||||
func Pipe20[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, F11 ~func(T10) T11, F12 ~func(T11) T12, F13 ~func(T12) T13, F14 ~func(T13) T14, F15 ~func(T14) T15, F16 ~func(T15) T16, F17 ~func(T16) T17, F18 ~func(T17) T18, F19 ~func(T18) T19, F20 ~func(T19) T20, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12, f13 F13, f14 F14, f15 F15, f16 F16, f17 F17, f18 F18, f19 F19, f20 F20) T20 {
|
||||
@@ -1647,3 +1773,10 @@ func Unvariadic20[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T
|
||||
return f(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18, t19, t20, v...)
|
||||
}
|
||||
}
|
||||
|
||||
// Unsliced20 converts a function taking a slice parameter into a function with 20 parameters
|
||||
func Unsliced20[F ~func([]T) R, T, R any](f F) func(T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T) R {
|
||||
return func(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18, t19, t20 T) R {
|
||||
return f([]T{t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18, t19, t20})
|
||||
}
|
||||
}
|
||||
|
65
function/generic/cache.go
Normal file
65
function/generic/cache.go
Normal file
@@ -0,0 +1,65 @@
|
||||
// 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 (
|
||||
"sync"
|
||||
|
||||
L "github.com/IBM/fp-go/internal/lazy"
|
||||
)
|
||||
|
||||
// Memoize converts a unary function into a unary function that caches the value depending on the parameter
|
||||
func Memoize[F ~func(K) T, K comparable, T any](f F) F {
|
||||
return ContramapMemoize[F](func(k K) K { return k })(f)
|
||||
}
|
||||
|
||||
// ContramapMemoize converts a unary function into a unary function that caches the value depending on the parameter
|
||||
func ContramapMemoize[F ~func(A) T, KF func(A) K, A any, K comparable, T any](kf KF) func(F) F {
|
||||
return CacheCallback[F](kf, getOrCreate[K, T]())
|
||||
}
|
||||
|
||||
// getOrCreate is a naive implementation of a cache, without bounds
|
||||
func getOrCreate[K comparable, T any]() func(K, func() func() T) func() T {
|
||||
cache := make(map[K]func() T)
|
||||
var l sync.Mutex
|
||||
|
||||
return func(k K, cb func() func() T) func() T {
|
||||
// only lock to access a lazy accessor to the value
|
||||
l.Lock()
|
||||
existing, ok := cache[k]
|
||||
if !ok {
|
||||
existing = cb()
|
||||
cache[k] = existing
|
||||
}
|
||||
l.Unlock()
|
||||
// compute the value outside of the lock
|
||||
return existing
|
||||
}
|
||||
}
|
||||
|
||||
// CacheCallback converts a unary function into a unary function that caches the value depending on the parameter
|
||||
func CacheCallback[F ~func(A) T, KF func(A) K, C ~func(K, func() func() T) func() T, A any, K comparable, T any](kf KF, getOrCreate C) func(F) F {
|
||||
return func(f F) F {
|
||||
return func(a A) T {
|
||||
// cache entry
|
||||
return getOrCreate(kf(a), func() func() T {
|
||||
return L.Memoize[func() T](func() T {
|
||||
return f(a)
|
||||
})
|
||||
})()
|
||||
}
|
||||
}
|
||||
}
|
62
function/pipe_test.go
Normal file
62
function/pipe_test.go
Normal file
@@ -0,0 +1,62 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package function
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func addSthg(value int) int {
|
||||
return value + 1
|
||||
}
|
||||
|
||||
func doSthgElse(value int) int {
|
||||
return value * 2
|
||||
}
|
||||
|
||||
func doFinalSthg(value int) string {
|
||||
return fmt.Sprintf("final value: %d", value)
|
||||
}
|
||||
|
||||
func Example() {
|
||||
// start point
|
||||
value := 1
|
||||
// imperative style
|
||||
value1 := addSthg(value) // 2
|
||||
value2 := doSthgElse(value1) // 4
|
||||
finalValueImperative := doFinalSthg(value2) // "final value: 4"
|
||||
|
||||
// the same but inline
|
||||
finalValueInline := doFinalSthg(doSthgElse(addSthg(value)))
|
||||
|
||||
// with pipe
|
||||
finalValuePipe := Pipe3(value, addSthg, doSthgElse, doFinalSthg)
|
||||
|
||||
// with flow
|
||||
transform := Flow3(addSthg, doSthgElse, doFinalSthg)
|
||||
finalValueFlow := transform(value)
|
||||
|
||||
fmt.Println(finalValueImperative)
|
||||
fmt.Println(finalValueInline)
|
||||
fmt.Println(finalValuePipe)
|
||||
fmt.Println(finalValueFlow)
|
||||
|
||||
// Output:
|
||||
// final value: 4
|
||||
// final value: 4
|
||||
// final value: 4
|
||||
// final value: 4
|
||||
}
|
45
function/unvariadic_test.go
Normal file
45
function/unvariadic_test.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 function
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func fromLibrary(data ...string) string {
|
||||
return strings.Join(data, "-")
|
||||
}
|
||||
|
||||
func TestUnvariadic(t *testing.T) {
|
||||
|
||||
res := Pipe1(
|
||||
[]string{"A", "B"},
|
||||
Unvariadic0(fromLibrary),
|
||||
)
|
||||
|
||||
assert.Equal(t, "A-B", res)
|
||||
}
|
||||
|
||||
func TestVariadicArity(t *testing.T) {
|
||||
|
||||
f := Unsliced2(Unvariadic0(fromLibrary))
|
||||
|
||||
res := f("A", "B")
|
||||
assert.Equal(t, "A-B", res)
|
||||
}
|
2
go.mod
2
go.mod
@@ -8,7 +8,7 @@ require (
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
|
4
go.sum
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.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
|
@@ -57,7 +57,7 @@ var (
|
||||
GetHeader,
|
||||
R.Lookup[H.Header](HeaderContentType),
|
||||
O.Chain(A.First[string]),
|
||||
E.FromOption[error, string](errors.OnNone("unable to access the [%s] header", HeaderContentType)),
|
||||
E.FromOption[string](errors.OnNone("unable to access the [%s] header", HeaderContentType)),
|
||||
E.ChainFirst(validateJsonContentTypeString),
|
||||
)))
|
||||
)
|
||||
@@ -68,10 +68,8 @@ const (
|
||||
|
||||
// ParseMediaType parses a media type into a tuple
|
||||
func ParseMediaType(mediaType string) E.Either[error, ParsedMediaType] {
|
||||
return E.TryCatchError(func() (ParsedMediaType, error) {
|
||||
m, p, err := mime.ParseMediaType(mediaType)
|
||||
return T.MakeTuple2(m, p), err
|
||||
})
|
||||
m, p, err := mime.ParseMediaType(mediaType)
|
||||
return E.TryCatchError(T.MakeTuple2(m, p), err)
|
||||
}
|
||||
|
||||
func GetHeader(resp *H.Response) H.Header {
|
||||
|
@@ -1,21 +1,7 @@
|
||||
// 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.
|
||||
|
||||
// Code generated by go generate; DO NOT EDIT.
|
||||
// This file was generated by robots at
|
||||
// 2023-07-28 22:48:46.7920356 +0200 CEST m=+0.045177201
|
||||
// 2023-10-23 08:30:50.5492271 +0200 CEST m=+0.023274501
|
||||
|
||||
package identity
|
||||
|
||||
import (
|
||||
|
@@ -18,6 +18,7 @@ package generic
|
||||
import (
|
||||
F "github.com/IBM/fp-go/function"
|
||||
C "github.com/IBM/fp-go/internal/chain"
|
||||
FC "github.com/IBM/fp-go/internal/functor"
|
||||
)
|
||||
|
||||
func MonadAp[GAB ~func(A) B, B, A any](fab GAB, fa A) B {
|
||||
@@ -51,3 +52,11 @@ func MonadChainFirst[GAB ~func(A) B, A, B any](fa A, f GAB) A {
|
||||
func ChainFirst[GAB ~func(A) B, A, B any](f GAB) func(A) A {
|
||||
return C.ChainFirst(MonadChain[func(A) A, A, A], MonadMap[func(B) A, B, A], f)
|
||||
}
|
||||
|
||||
func MonadFlap[GAB ~func(A) B, A, B any](fab GAB, a A) B {
|
||||
return FC.MonadFlap(MonadMap[func(GAB) B, GAB, B], fab, a)
|
||||
}
|
||||
|
||||
func Flap[GAB ~func(A) B, A, B any](a A) func(GAB) B {
|
||||
return F.Bind2nd(MonadFlap[GAB, A, B], a)
|
||||
}
|
||||
|
@@ -63,3 +63,11 @@ func MonadChainFirst[A, B any](fa A, f func(A) B) A {
|
||||
func ChainFirst[A, B any](f func(A) B) func(A) A {
|
||||
return G.ChainFirst(f)
|
||||
}
|
||||
|
||||
func MonadFlap[B, A any](fab func(A) B, a A) B {
|
||||
return G.MonadFlap[func(A) B](fab, a)
|
||||
}
|
||||
|
||||
func Flap[B, A any](a A) func(func(A) B) B {
|
||||
return G.Flap[func(A) B](a)
|
||||
}
|
||||
|
@@ -1,21 +1,7 @@
|
||||
// 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.
|
||||
|
||||
// Code generated by go generate; DO NOT EDIT.
|
||||
// This file was generated by robots at
|
||||
// 2023-07-28 22:48:49.4210571 +0200 CEST m=+0.040316501
|
||||
// 2023-10-23 08:30:54.1909432 +0200 CEST m=+0.014252201
|
||||
|
||||
package apply
|
||||
|
||||
import (
|
||||
|
@@ -42,10 +42,27 @@ func Reduce[GA ~[]A, A, B any](fa GA, f func(B, A) B, initial B) B {
|
||||
return current
|
||||
}
|
||||
|
||||
func ReduceWithIndex[GA ~[]A, A, B any](fa GA, f func(int, B, A) B, initial B) B {
|
||||
current := initial
|
||||
count := len(fa)
|
||||
for i := 0; i < count; i++ {
|
||||
current = f(i, current, fa[i])
|
||||
}
|
||||
return current
|
||||
}
|
||||
|
||||
func Append[GA ~[]A, A any](as GA, a A) GA {
|
||||
return append(as, a)
|
||||
}
|
||||
|
||||
func Push[GA ~[]A, A any](as GA, a A) GA {
|
||||
l := len(as)
|
||||
cpy := make(GA, l+1)
|
||||
copy(cpy, as)
|
||||
cpy[l] = a
|
||||
return cpy
|
||||
}
|
||||
|
||||
func Empty[GA ~[]A, A any]() GA {
|
||||
return make(GA, 0)
|
||||
}
|
||||
|
@@ -37,6 +37,24 @@ func MonadTraverse[GA ~[]A, GB ~[]B, A, B, HKTB, HKTAB, HKTRB any](
|
||||
return MonadTraverseReduce(fof, fmap, fap, ta, f, Append[GB, B], Empty[GB]())
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
We need to pass the members of the applicative explicitly, because golang does neither support higher kinded types nor template methods on structs or interfaces
|
||||
|
||||
HKTRB = HKT<GB>
|
||||
HKTB = HKT<B>
|
||||
HKTAB = HKT<func(A)B>
|
||||
*/
|
||||
func MonadTraverseWithIndex[GA ~[]A, GB ~[]B, A, B, HKTB, HKTAB, HKTRB any](
|
||||
fof func(GB) HKTRB,
|
||||
fmap func(func(GB) func(B) GB) func(HKTRB) HKTAB,
|
||||
fap func(HKTB) func(HKTAB) HKTRB,
|
||||
|
||||
ta GA,
|
||||
f func(int, A) HKTB) HKTRB {
|
||||
return MonadTraverseReduceWithIndex(fof, fmap, fap, ta, f, Append[GB, B], Empty[GB]())
|
||||
}
|
||||
|
||||
func Traverse[GA ~[]A, GB ~[]B, A, B, HKTB, HKTAB, HKTRB any](
|
||||
fof func(GB) HKTRB,
|
||||
fmap func(func(GB) func(B) GB) func(HKTRB) HKTAB,
|
||||
@@ -49,6 +67,18 @@ func Traverse[GA ~[]A, GB ~[]B, A, B, HKTB, HKTAB, HKTRB any](
|
||||
}
|
||||
}
|
||||
|
||||
func TraverseWithIndex[GA ~[]A, GB ~[]B, A, B, HKTB, HKTAB, HKTRB any](
|
||||
fof func(GB) HKTRB,
|
||||
fmap func(func(GB) func(B) GB) func(HKTRB) HKTAB,
|
||||
fap func(HKTB) func(HKTAB) HKTRB,
|
||||
|
||||
f func(int, A) HKTB) func(GA) HKTRB {
|
||||
|
||||
return func(ma GA) HKTRB {
|
||||
return MonadTraverseWithIndex(fof, fmap, fap, ma, f)
|
||||
}
|
||||
}
|
||||
|
||||
func MonadTraverseReduce[GA ~[]A, GB, A, B, HKTB, HKTAB, HKTRB any](
|
||||
fof func(GB) HKTRB,
|
||||
fmap func(func(GB) func(B) GB) func(HKTRB) HKTAB,
|
||||
@@ -71,6 +101,28 @@ func MonadTraverseReduce[GA ~[]A, GB, A, B, HKTB, HKTAB, HKTRB any](
|
||||
}, fof(initial))
|
||||
}
|
||||
|
||||
func MonadTraverseReduceWithIndex[GA ~[]A, GB, A, B, HKTB, HKTAB, HKTRB any](
|
||||
fof func(GB) HKTRB,
|
||||
fmap func(func(GB) func(B) GB) func(HKTRB) HKTAB,
|
||||
fap func(HKTB) func(HKTAB) HKTRB,
|
||||
|
||||
ta GA,
|
||||
|
||||
transform func(int, A) HKTB,
|
||||
reduce func(GB, B) GB,
|
||||
initial GB,
|
||||
) HKTRB {
|
||||
mmap := fmap(F.Curry2(reduce))
|
||||
|
||||
return ReduceWithIndex(ta, func(idx int, r HKTRB, a A) HKTRB {
|
||||
return F.Pipe2(
|
||||
r,
|
||||
mmap,
|
||||
fap(transform(idx, a)),
|
||||
)
|
||||
}, fof(initial))
|
||||
}
|
||||
|
||||
func TraverseReduce[GA ~[]A, GB, A, B, HKTB, HKTAB, HKTRB any](
|
||||
fof func(GB) HKTRB,
|
||||
fmap func(func(GB) func(B) GB) func(HKTRB) HKTAB,
|
||||
@@ -84,3 +136,17 @@ func TraverseReduce[GA ~[]A, GB, A, B, HKTB, HKTAB, HKTRB any](
|
||||
return MonadTraverseReduce(fof, fmap, fap, ta, transform, reduce, initial)
|
||||
}
|
||||
}
|
||||
|
||||
func TraverseReduceWithIndex[GA ~[]A, GB, A, B, HKTB, HKTAB, HKTRB any](
|
||||
fof func(GB) HKTRB,
|
||||
fmap func(func(GB) func(B) GB) func(HKTRB) HKTAB,
|
||||
fap func(HKTB) func(HKTAB) HKTRB,
|
||||
|
||||
transform func(int, A) HKTB,
|
||||
reduce func(GB, B) GB,
|
||||
initial GB,
|
||||
) func(GA) HKTRB {
|
||||
return func(ta GA) HKTRB {
|
||||
return MonadTraverseReduceWithIndex(fof, fmap, fap, ta, transform, reduce, initial)
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user