mirror of
https://github.com/IBM/fp-go.git
synced 2025-08-10 22:31:32 +02:00
Merge pull request #1 from IBM/cleue-initial-checkin
Cleue initial checkin
This commit is contained in:
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
fp-go.exe
|
||||
main.exe
|
||||
build/
|
128
README.md
128
README.md
@@ -1,2 +1,126 @@
|
||||
# fp-go
|
||||
functional programming library for golang
|
||||
# Functional programming library for golang
|
||||
|
||||
**🚧 Work in progress! 🚧**
|
||||
|
||||

|
||||
|
||||
## 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:
|
||||
|
||||
- write many small, testable and pure functions, i.e. functions that produce output only depending on their input and that do not execute side effects
|
||||
- offer helpers to isolate side effects into lazily executed functions (IO)
|
||||
- expose a consistent set of composition to create new functions from existing ones
|
||||
- for each data type there exists a small set of composition functions
|
||||
- these functions are called the same across all data types, so you only have to learn a small number of function names
|
||||
- the semantic of functions of the same name is consistent across all data types
|
||||
|
||||
### How does this play with the [🧘🏽 Zen Of Go](https://the-zen-of-go.netlify.app/)?
|
||||
|
||||
#### 🧘🏽 Each package fulfils a single purpose
|
||||
|
||||
✔️ Each of the top level packages (e.g. Option, Either, Task, ...) fulfils the purpose of defining the respective data type and implementing the set of common operations for this data type.
|
||||
|
||||
#### 🧘🏽 Handle errors explicitly
|
||||
|
||||
✔️ The library makes a clear distinction between that operations that cannot fail by design and operations that can fail. Failure is represented via the `Either` type and errors are handled explicitly by using `Either`'s monadic set of operations.
|
||||
|
||||
#### 🧘🏽 Return early rather than nesting deeply
|
||||
|
||||
✔️ We recommend to implement simple, small functions that implement one feature and that would typically not invoke other functions. Interaction with other functions is done by function composition and the composition makes sure to run one function after the other. In the error case the `Either` monad makes sure to skip the error path.
|
||||
|
||||
#### 🧘🏽 Leave concurrency to the caller
|
||||
|
||||
✔️ All operations are synchronous by default, including `Task`. Concurrency must be coded by the consumer of these functions explicitly, but the implementation is ready to deal with concurrent usage.
|
||||
|
||||
#### 🧘🏽 Before you launch a goroutine, know when it will stop
|
||||
|
||||
🤷🏽 This is left to the user of the library since the library itself will not start goroutines on its own. The Task monad offers support for cancellation via the golang context, though.
|
||||
|
||||
#### 🧘🏽 Avoid package level state
|
||||
|
||||
✔️ No package level state anywhere, this would be a significant anti-pattern
|
||||
|
||||
#### 🧘🏽 Simplicity matters
|
||||
|
||||
✔️ The library is simple in the sense that it offers a small, consistent interface to a variety of data types. Users can concentrate on implementing business logic rather than dealing with low level data structures.
|
||||
|
||||
#### 🧘🏽 Write tests to lock in the behaviour of your package’s API
|
||||
|
||||
🟡 The programming pattern suggested by this library encourages writing test cases. The library itself also has a growing number of tests, but not enough, yet. TBD
|
||||
|
||||
#### 🧘🏽 If you think it’s slow, first prove it with a benchmark
|
||||
|
||||
✔️ Absolutely. If you think the function composition offered by this library is too slow, please provide a benchmark.
|
||||
|
||||
#### 🧘🏽 Moderation is a virtue
|
||||
|
||||
✔️ The library does not implement its own goroutines and also does not require any expensive synchronization primitives. Coordination of Tasks is implemented via atomic counters without additional primitives. Channels are only used in the `Wait` function of a Task that should be invoked at most once in a complete application.
|
||||
|
||||
#### 🧘🏽 Maintainability counts
|
||||
|
||||
✔️ Code that consumes this library is easy to maintain because of the small and concise set of operations exposed. Also the suggested programming paradigm to decompose an application into small functions increases maintainability, because these functions are easy to understand and if they are pure, it's often sufficient to look at the type signature to understand the purpose.
|
||||
|
||||
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.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
### Generics
|
||||
|
||||
All monadic operations are implemented via generics, i.e. they offer a type safe way to compose operations. This allows for convenient IDE support and also gives confidence about the correctness of the composition at compile time.
|
||||
|
||||
Downside is that this will result in different versions of each operation per type, these versions are generated by the golang compiler at build time (unlike type erasure in languages such as Java of TypeScript). This might lead to large binaries for codebases with many different types. If this is a concern, you can always implement type erasure on top, i.e. use the monadic operations with the `any` type as if generics were not supported. You loose type safety, but this might result in smaller binaries.
|
||||
|
||||
### Use of the [~ Operator](https://go.googlesource.com/proposal/+/master/design/47781-parameterized-go-ast.md)
|
||||
|
||||
The FP library attempts to be easy to consume and one aspect of this is the definition of higher level type definitions instead of having to use their low level equivalent. It is e.g. more convenient and readable to use
|
||||
|
||||
```go
|
||||
TaskEither[E, A]
|
||||
```
|
||||
|
||||
than
|
||||
|
||||
```go
|
||||
func(func(Either.Either[E, A]))
|
||||
```
|
||||
|
||||
although both are logically equivalent. At the time of this writing the go type system does not support generic type aliases, only generic type definition, i.e. it is not possible to write:
|
||||
|
||||
```go
|
||||
type TaskEither[E, A any] = T.Task[ET.Either[E, A]]
|
||||
```
|
||||
|
||||
only
|
||||
|
||||
```go
|
||||
type TaskEither[E, A any] T.Task[ET.Either[E, A]]
|
||||
```
|
||||
|
||||
This makes a big difference, because in the second case the type `TaskEither[E, A any]` is considered a completely new type, not compatible to its right hand side, so it's not just a shortcut but a fully new type.
|
||||
|
||||
From the implementation perspective however there is no reason to restrict the implementation to the new type, it can be generic for all compatible types. The way to express this in go is the [~](https://go.googlesource.com/proposal/+/master/design/47781-parameterized-go-ast.md) operator. This comes with some quite complicated type declarations in some cases, which undermines the goal of the library to be easy to use.
|
||||
|
||||
For that reason there exist sub-packages called `Generic` for all higher level types. These packages contain the fully generic implementation of the operations, preferring abstraction over usability. These packages are not meant to be used by end-users but are meant to be used by library extensions. The implementation for the convenient higher level types specializes the generic implementation for the particular higher level type, i.e. this layer does not contain any business logic but only *type magic*.
|
||||
|
||||
### Higher Kinded Types
|
||||
|
||||
Go does not support higher kinded types (HKT). Such types occur if a generic type itself is parametrized by another generic type. Example:
|
||||
|
||||
The `Map` operation for `Task` is defined as:
|
||||
|
||||
```go
|
||||
func Map[A, B any](f func(A) B) func(Task[A]) Task[B]
|
||||
```
|
||||
|
||||
and in fact the equivalent operations for all other mondas follow the same pattern, we could try to introduce a new type for `Task` (without a parameter) as a HKT, e.g. like so (made-up syntax, does not work in go):
|
||||
|
||||
```go
|
||||
func Map[HKT, A, B any](f func(A) B) func(HKT[A]) HKT[B]
|
||||
```
|
||||
|
||||
this would be the completely generic method signature for all possible monads. In particular in many cases it is possible to compose functions independent of the concrete knowledge of the actual `HKT`. From the perspective of a library this is the ideal situation because then a particular algorithm only has to be implemented and tested once.
|
||||
|
||||
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.
|
||||
|
279
array/array.go
Normal file
279
array/array.go
Normal file
@@ -0,0 +1,279 @@
|
||||
package array
|
||||
|
||||
import (
|
||||
G "github.com/ibm/fp-go/array/generic"
|
||||
F "github.com/ibm/fp-go/function"
|
||||
"github.com/ibm/fp-go/internal/array"
|
||||
M "github.com/ibm/fp-go/monoid"
|
||||
O "github.com/ibm/fp-go/option"
|
||||
"github.com/ibm/fp-go/tuple"
|
||||
)
|
||||
|
||||
// From constructs an array from a set of variadic arguments
|
||||
func From[A any](data ...A) []A {
|
||||
return G.From[[]A](data...)
|
||||
}
|
||||
|
||||
// MakeBy returns a `Array` of length `n` with element `i` initialized with `f(i)`.
|
||||
func MakeBy[A any](n int, f func(int) A) []A {
|
||||
// sanity check
|
||||
if n <= 0 {
|
||||
return Empty[A]()
|
||||
}
|
||||
// run the generator function across the input
|
||||
as := make([]A, n)
|
||||
for i := n - 1; i >= 0; i-- {
|
||||
as[i] = f(i)
|
||||
}
|
||||
return as
|
||||
}
|
||||
|
||||
// Replicate creates a `Array` containing a value repeated the specified number of times.
|
||||
func Replicate[A any](n int, a A) []A {
|
||||
return MakeBy(n, F.Constant1[int](a))
|
||||
}
|
||||
|
||||
func MonadMap[A, B any](as []A, f func(a A) B) []B {
|
||||
return G.MonadMap[[]A, []B](as, f)
|
||||
}
|
||||
|
||||
func MonadMapRef[A, B any](as []A, f func(a *A) B) []B {
|
||||
count := len(as)
|
||||
bs := make([]B, count)
|
||||
for i := count - 1; i >= 0; i-- {
|
||||
bs[i] = f(&as[i])
|
||||
}
|
||||
return bs
|
||||
}
|
||||
|
||||
func Map[A, B any](f func(a A) B) func([]A) []B {
|
||||
return F.Bind2nd(MonadMap[A, B], f)
|
||||
}
|
||||
|
||||
func MapRef[A, B any](f func(a *A) B) func([]A) []B {
|
||||
return F.Bind2nd(MonadMapRef[A, B], f)
|
||||
}
|
||||
|
||||
func filter[A any](fa []A, pred func(A) bool) []A {
|
||||
var result []A
|
||||
count := len(fa)
|
||||
for i := 0; i < count; i++ {
|
||||
a := fa[i]
|
||||
if pred(a) {
|
||||
result = append(result, a)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func filterRef[A any](fa []A, pred func(a *A) bool) []A {
|
||||
var result []A
|
||||
count := len(fa)
|
||||
for i := 0; i < count; i++ {
|
||||
a := fa[i]
|
||||
if pred(&a) {
|
||||
result = append(result, a)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func filterMapRef[A, B any](fa []A, pred func(a *A) bool, f func(a *A) B) []B {
|
||||
var result []B
|
||||
count := len(fa)
|
||||
for i := 0; i < count; i++ {
|
||||
a := fa[i]
|
||||
if pred(&a) {
|
||||
result = append(result, f(&a))
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func Filter[A any](pred func(A) bool) func([]A) []A {
|
||||
return F.Bind2nd(filter[A], pred)
|
||||
}
|
||||
|
||||
func FilterRef[A any](pred func(*A) bool) func([]A) []A {
|
||||
return F.Bind2nd(filterRef[A], pred)
|
||||
}
|
||||
|
||||
func MonadFilterMap[A, B any](fa []A, f func(a A) O.Option[B]) []B {
|
||||
return G.MonadFilterMap[[]A, []B](fa, f)
|
||||
}
|
||||
|
||||
func FilterMap[A, B any](f func(a A) O.Option[B]) func([]A) []B {
|
||||
return G.FilterMap[[]A, []B](f)
|
||||
}
|
||||
|
||||
func FilterMapRef[A, B any](pred func(a *A) bool, f func(a *A) B) func([]A) []B {
|
||||
return func(fa []A) []B {
|
||||
return filterMapRef(fa, pred, f)
|
||||
}
|
||||
}
|
||||
|
||||
func reduceRef[A, B any](fa []A, f func(B, *A) B, initial B) B {
|
||||
current := initial
|
||||
count := len(fa)
|
||||
for i := 0; i < count; i++ {
|
||||
current = f(current, &fa[i])
|
||||
}
|
||||
return current
|
||||
}
|
||||
|
||||
func Reduce[A, B any](f func(B, A) B, initial B) func([]A) B {
|
||||
return func(as []A) B {
|
||||
return array.Reduce(as, f, initial)
|
||||
}
|
||||
}
|
||||
|
||||
func ReduceRef[A, B any](f func(B, *A) B, initial B) func([]A) B {
|
||||
return func(as []A) B {
|
||||
return reduceRef(as, f, initial)
|
||||
}
|
||||
}
|
||||
|
||||
func Append[A any](as []A, a A) []A {
|
||||
return G.Append(as, a)
|
||||
}
|
||||
|
||||
func IsEmpty[A any](as []A) bool {
|
||||
return array.IsEmpty(as)
|
||||
}
|
||||
|
||||
func IsNonEmpty[A any](as []A) bool {
|
||||
return len(as) > 0
|
||||
}
|
||||
|
||||
func Empty[A any]() []A {
|
||||
return G.Empty[[]A]()
|
||||
}
|
||||
|
||||
func Zero[A any]() []A {
|
||||
return Empty[A]()
|
||||
}
|
||||
|
||||
// Of constructs a single element array
|
||||
func Of[A any](a A) []A {
|
||||
return G.Of[[]A](a)
|
||||
}
|
||||
|
||||
func MonadChain[A, B any](fa []A, f func(a A) []B) []B {
|
||||
return array.Reduce(fa, func(bs []B, a A) []B {
|
||||
return append(bs, f(a)...)
|
||||
}, Zero[B]())
|
||||
}
|
||||
|
||||
func Chain[A, B any](f func(a A) []B) func([]A) []B {
|
||||
return F.Bind2nd(MonadChain[A, B], f)
|
||||
}
|
||||
|
||||
func MonadAp[B, A any](fab []func(A) B, fa []A) []B {
|
||||
return MonadChain(fab, F.Bind1st(MonadMap[A, B], fa))
|
||||
}
|
||||
|
||||
func Ap[B, A any](fa []A) func([]func(A) B) []B {
|
||||
return F.Bind2nd(MonadAp[B, A], fa)
|
||||
}
|
||||
|
||||
func Match[A, B any](onEmpty func() B, onNonEmpty func([]A) B) func([]A) B {
|
||||
return func(as []A) B {
|
||||
if IsEmpty(as) {
|
||||
return onEmpty()
|
||||
}
|
||||
return onNonEmpty(as)
|
||||
}
|
||||
}
|
||||
|
||||
func Tail[A any](as []A) O.Option[[]A] {
|
||||
return G.Tail(as)
|
||||
}
|
||||
|
||||
func Head[A any](as []A) O.Option[A] {
|
||||
return G.Head(as)
|
||||
}
|
||||
|
||||
func First[A any](as []A) O.Option[A] {
|
||||
return G.First(as)
|
||||
}
|
||||
|
||||
func Last[A any](as []A) O.Option[A] {
|
||||
return G.Last(as)
|
||||
}
|
||||
|
||||
func PrependAll[A any](middle A) func([]A) []A {
|
||||
return func(as []A) []A {
|
||||
count := len(as)
|
||||
dst := count * 2
|
||||
result := make([]A, dst)
|
||||
for i := count - 1; i >= 0; i-- {
|
||||
dst--
|
||||
result[dst] = as[i]
|
||||
dst--
|
||||
result[dst] = middle
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
func Intersperse[A any](middle A) func([]A) []A {
|
||||
prepend := PrependAll(middle)
|
||||
return func(as []A) []A {
|
||||
if IsEmpty(as) {
|
||||
return as
|
||||
}
|
||||
return prepend(as)[1:]
|
||||
}
|
||||
}
|
||||
|
||||
func Intercalate[A any](m M.Monoid[A]) func(A) func([]A) A {
|
||||
concatAll := ConcatAll[A](m)(m.Empty())
|
||||
return func(middle A) func([]A) A {
|
||||
return Match(m.Empty, F.Flow2(Intersperse(middle), concatAll))
|
||||
}
|
||||
}
|
||||
|
||||
func Flatten[A any](mma [][]A) []A {
|
||||
return MonadChain(mma, F.Identity[[]A])
|
||||
}
|
||||
|
||||
func Slice[A any](low, high int) func(as []A) []A {
|
||||
return array.Slice[[]A](low, high)
|
||||
}
|
||||
|
||||
func Lookup[A any](idx int) func([]A) O.Option[A] {
|
||||
return G.Lookup[[]A](idx)
|
||||
}
|
||||
|
||||
func UpsertAt[A any](a A) func([]A) []A {
|
||||
return G.UpsertAt[[]A](a)
|
||||
}
|
||||
|
||||
func Size[A any](as []A) int {
|
||||
return G.Size(as)
|
||||
}
|
||||
|
||||
func MonadPartition[A any](as []A, pred func(A) bool) tuple.Tuple2[[]A, []A] {
|
||||
return G.MonadPartition(as, pred)
|
||||
}
|
||||
|
||||
// Partition creates two new arrays out of one, the left result contains the elements
|
||||
// for which the predicate returns false, the right one those for which the predicate returns true
|
||||
func Partition[A any](pred func(A) bool) func([]A) tuple.Tuple2[[]A, []A] {
|
||||
return G.Partition[[]A](pred)
|
||||
}
|
||||
|
||||
// IsNil checks if the array is set to nil
|
||||
func IsNil[A any](as []A) bool {
|
||||
return array.IsNil(as)
|
||||
}
|
||||
|
||||
// IsNonNil checks if the array is set to nil
|
||||
func IsNonNil[A any](as []A) bool {
|
||||
return array.IsNonNil(as)
|
||||
}
|
||||
|
||||
// ConstNil returns a nil array
|
||||
func ConstNil[A any]() []A {
|
||||
return array.ConstNil[[]A]()
|
||||
}
|
129
array/array_test.go
Normal file
129
array/array_test.go
Normal file
@@ -0,0 +1,129 @@
|
||||
package array
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
F "github.com/ibm/fp-go/function"
|
||||
"github.com/ibm/fp-go/internal/utils"
|
||||
O "github.com/ibm/fp-go/option"
|
||||
S "github.com/ibm/fp-go/string"
|
||||
T "github.com/ibm/fp-go/tuple"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestMap1(t *testing.T) {
|
||||
|
||||
src := []string{"a", "b", "c"}
|
||||
|
||||
up := Map(strings.ToUpper)(src)
|
||||
|
||||
var up1 = []string{}
|
||||
for _, s := range src {
|
||||
up1 = append(up1, strings.ToUpper(s))
|
||||
}
|
||||
|
||||
var up2 = []string{}
|
||||
for i := range src {
|
||||
up2 = append(up2, strings.ToUpper(src[i]))
|
||||
}
|
||||
|
||||
assert.Equal(t, up, up1)
|
||||
assert.Equal(t, up, up2)
|
||||
}
|
||||
|
||||
func TestMap(t *testing.T) {
|
||||
|
||||
mapper := Map(utils.Upper)
|
||||
|
||||
src := []string{"a", "b", "c"}
|
||||
|
||||
dst := mapper(src)
|
||||
|
||||
assert.Equal(t, dst, []string{"A", "B", "C"})
|
||||
}
|
||||
|
||||
func TestReduce(t *testing.T) {
|
||||
|
||||
values := MakeBy(101, F.Identity[int])
|
||||
|
||||
sum := func(val int, current int) int {
|
||||
return val + current
|
||||
}
|
||||
reducer := Reduce(sum, 0)
|
||||
|
||||
result := reducer(values)
|
||||
assert.Equal(t, result, 5050)
|
||||
|
||||
}
|
||||
|
||||
func TestEmpty(t *testing.T) {
|
||||
assert.True(t, IsNonEmpty(MakeBy(101, F.Identity[int])))
|
||||
assert.True(t, IsEmpty([]int{}))
|
||||
}
|
||||
|
||||
func TestAp(t *testing.T) {
|
||||
assert.Equal(t,
|
||||
[]int{2, 4, 6, 3, 6, 9},
|
||||
F.Pipe1(
|
||||
[]func(int) int{
|
||||
utils.Double,
|
||||
utils.Triple,
|
||||
},
|
||||
Ap[int, int]([]int{1, 2, 3}),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
func TestIntercalate(t *testing.T) {
|
||||
is := Intercalate(S.Monoid)("-")
|
||||
|
||||
assert.Equal(t, "", is(Empty[string]()))
|
||||
assert.Equal(t, "a", is([]string{"a"}))
|
||||
assert.Equal(t, "a-b-c", is([]string{"a", "b", "c"}))
|
||||
assert.Equal(t, "a--c", is([]string{"a", "", "c"}))
|
||||
assert.Equal(t, "a-b", is([]string{"a", "b"}))
|
||||
assert.Equal(t, "a-b-c-d", is([]string{"a", "b", "c", "d"}))
|
||||
}
|
||||
|
||||
func TestPrependAll(t *testing.T) {
|
||||
empty := Empty[int]()
|
||||
prep := PrependAll(0)
|
||||
assert.Equal(t, empty, prep(empty))
|
||||
assert.Equal(t, []int{0, 1, 0, 2, 0, 3}, prep([]int{1, 2, 3}))
|
||||
assert.Equal(t, []int{0, 1}, prep([]int{1}))
|
||||
assert.Equal(t, []int{0, 1, 0, 2, 0, 3, 0, 4}, prep([]int{1, 2, 3, 4}))
|
||||
}
|
||||
|
||||
func TestFlatten(t *testing.T) {
|
||||
assert.Equal(t, []int{1, 2, 3}, Flatten([][]int{{1}, {2}, {3}}))
|
||||
}
|
||||
|
||||
func TestLookup(t *testing.T) {
|
||||
data := []int{0, 1, 2}
|
||||
none := O.None[int]()
|
||||
|
||||
assert.Equal(t, none, Lookup[int](-1)(data))
|
||||
assert.Equal(t, none, Lookup[int](10)(data))
|
||||
assert.Equal(t, O.Some(1), Lookup[int](1)(data))
|
||||
}
|
||||
|
||||
func TestSlice(t *testing.T) {
|
||||
data := []int{0, 1, 2, 3}
|
||||
|
||||
assert.Equal(t, []int{1, 2}, Slice[int](1, 3)(data))
|
||||
}
|
||||
|
||||
func TestFrom(t *testing.T) {
|
||||
assert.Equal(t, []int{1, 2, 3}, From(1, 2, 3))
|
||||
}
|
||||
|
||||
func TestPartition(t *testing.T) {
|
||||
|
||||
pred := func(n int) bool {
|
||||
return n > 2
|
||||
}
|
||||
|
||||
assert.Equal(t, T.MakeTuple2(Empty[int](), Empty[int]()), Partition(pred)(Empty[int]()))
|
||||
assert.Equal(t, T.MakeTuple2(From(1), From(3)), Partition(pred)(From(1, 3)))
|
||||
}
|
25
array/eq.go
Normal file
25
array/eq.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package array
|
||||
|
||||
import (
|
||||
E "github.com/ibm/fp-go/eq"
|
||||
)
|
||||
|
||||
func equals[T any](left []T, right []T, eq func(T, T) bool) bool {
|
||||
if len(left) != len(right) {
|
||||
return false
|
||||
}
|
||||
for i, v1 := range left {
|
||||
v2 := right[i]
|
||||
if !eq(v1, v2) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func Eq[T any](e E.Eq[T]) E.Eq[[]T] {
|
||||
eq := e.Equals
|
||||
return E.FromEquals(func(left, right []T) bool {
|
||||
return equals(left, right, eq)
|
||||
})
|
||||
}
|
109
array/generic/array.go
Normal file
109
array/generic/array.go
Normal file
@@ -0,0 +1,109 @@
|
||||
package generic
|
||||
|
||||
import (
|
||||
F "github.com/ibm/fp-go/function"
|
||||
"github.com/ibm/fp-go/internal/array"
|
||||
O "github.com/ibm/fp-go/option"
|
||||
"github.com/ibm/fp-go/tuple"
|
||||
)
|
||||
|
||||
// Of constructs a single element array
|
||||
func Of[GA ~[]A, A any](value A) GA {
|
||||
return GA{value}
|
||||
}
|
||||
|
||||
// From constructs an array from a set of variadic arguments
|
||||
func From[GA ~[]A, A any](data ...A) GA {
|
||||
return data
|
||||
}
|
||||
|
||||
func Lookup[GA ~[]A, A any](idx int) func(GA) O.Option[A] {
|
||||
none := O.None[A]()
|
||||
if idx < 0 {
|
||||
return F.Constant1[GA](none)
|
||||
}
|
||||
return func(as GA) O.Option[A] {
|
||||
if idx < len(as) {
|
||||
return O.Some(as[idx])
|
||||
}
|
||||
return none
|
||||
}
|
||||
}
|
||||
|
||||
func Tail[GA ~[]A, A any](as GA) O.Option[GA] {
|
||||
if array.IsEmpty(as) {
|
||||
return O.None[GA]()
|
||||
}
|
||||
return O.Some(as[1:])
|
||||
}
|
||||
|
||||
func Head[GA ~[]A, A any](as GA) O.Option[A] {
|
||||
if array.IsEmpty(as) {
|
||||
return O.None[A]()
|
||||
}
|
||||
return O.Some(as[0])
|
||||
}
|
||||
|
||||
func First[GA ~[]A, A any](as GA) O.Option[A] {
|
||||
return Head(as)
|
||||
}
|
||||
|
||||
func Last[GA ~[]A, A any](as GA) O.Option[A] {
|
||||
if array.IsEmpty(as) {
|
||||
return O.None[A]()
|
||||
}
|
||||
return O.Some(as[len(as)-1])
|
||||
}
|
||||
|
||||
func Append[GA ~[]A, A any](as GA, a A) GA {
|
||||
return array.Append(as, a)
|
||||
}
|
||||
|
||||
func Empty[GA ~[]A, A any]() GA {
|
||||
return array.Empty[GA]()
|
||||
}
|
||||
|
||||
func UpsertAt[GA ~[]A, A any](a A) func(GA) GA {
|
||||
return array.UpsertAt[GA](a)
|
||||
}
|
||||
|
||||
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 Size[GA ~[]A, A any](as GA) int {
|
||||
return len(as)
|
||||
}
|
||||
|
||||
func filterMap[GA ~[]A, GB ~[]B, A, B any](fa GA, f func(a A) O.Option[B]) GB {
|
||||
return array.Reduce(fa, func(bs GB, a A) GB {
|
||||
return O.MonadFold(f(a), F.Constant(bs), F.Bind1st(Append[GB, B], bs))
|
||||
}, Empty[GB]())
|
||||
}
|
||||
|
||||
func MonadFilterMap[GA ~[]A, GB ~[]B, A, B any](fa GA, f func(a A) O.Option[B]) GB {
|
||||
return filterMap[GA, GB](fa, f)
|
||||
}
|
||||
|
||||
func FilterMap[GA ~[]A, GB ~[]B, A, B any](f func(a A) O.Option[B]) func(GA) GB {
|
||||
return F.Bind2nd(MonadFilterMap[GA, GB, A, B], f)
|
||||
}
|
||||
|
||||
func MonadPartition[GA ~[]A, A any](as GA, pred func(A) bool) tuple.Tuple2[GA, GA] {
|
||||
left := Empty[GA]()
|
||||
right := Empty[GA]()
|
||||
array.Reduce(as, func(c bool, a A) bool {
|
||||
if pred(a) {
|
||||
right = append(right, a)
|
||||
} else {
|
||||
left = append(left, a)
|
||||
}
|
||||
return c
|
||||
}, true)
|
||||
// returns the partition
|
||||
return tuple.MakeTuple2(left, right)
|
||||
}
|
||||
|
||||
func Partition[GA ~[]A, A any](pred func(A) bool) func(GA) tuple.Tuple2[GA, GA] {
|
||||
return F.Bind2nd(MonadPartition[GA, A], pred)
|
||||
}
|
26
array/generic/sort.go
Normal file
26
array/generic/sort.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package generic
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
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 func(ma GA) GA {
|
||||
// nothing to sort
|
||||
l := len(ma)
|
||||
if l < 2 {
|
||||
return ma
|
||||
}
|
||||
// copy
|
||||
cpy := make(GA, l)
|
||||
copy(cpy, ma)
|
||||
sort.Slice(cpy, func(i, j int) bool {
|
||||
return ord.Compare(cpy[i], cpy[j]) < 0
|
||||
})
|
||||
return cpy
|
||||
}
|
||||
}
|
10
array/magma.go
Normal file
10
array/magma.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package array
|
||||
|
||||
import (
|
||||
F "github.com/ibm/fp-go/function"
|
||||
M "github.com/ibm/fp-go/magma"
|
||||
)
|
||||
|
||||
func ConcatAll[A any](m M.Magma[A]) func(A) func([]A) A {
|
||||
return F.Bind1st(Reduce[A, A], m.Concat)
|
||||
}
|
21
array/magma_test.go
Normal file
21
array/magma_test.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package array
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
M "github.com/ibm/fp-go/magma"
|
||||
)
|
||||
|
||||
var subInt = M.MakeMagma(func(first int, second int) int {
|
||||
return first - second
|
||||
})
|
||||
|
||||
func TestConcatAll(t *testing.T) {
|
||||
|
||||
var subAll = ConcatAll(subInt)(0)
|
||||
|
||||
assert.Equal(t, subAll([]int{1, 2, 3}), -6)
|
||||
|
||||
}
|
43
array/monoid.go
Normal file
43
array/monoid.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package array
|
||||
|
||||
import (
|
||||
"github.com/ibm/fp-go/internal/array"
|
||||
M "github.com/ibm/fp-go/monoid"
|
||||
)
|
||||
|
||||
func concat[T any](left, right []T) []T {
|
||||
// some performance checks
|
||||
ll := len(left)
|
||||
lr := len(right)
|
||||
if ll == 0 {
|
||||
return right
|
||||
}
|
||||
if lr == 0 {
|
||||
return left
|
||||
}
|
||||
// need to copy
|
||||
buf := make([]T, ll+lr)
|
||||
copy(buf[copy(buf, left):], right)
|
||||
return buf
|
||||
}
|
||||
|
||||
func Monoid[T any]() M.Monoid[[]T] {
|
||||
return M.MakeMonoid(concat[T], Empty[T]())
|
||||
}
|
||||
|
||||
func addLen[A any](count int, data []A) int {
|
||||
return count + len(data)
|
||||
}
|
||||
|
||||
// ConcatAll efficiently concatenates the input arrays into a final array
|
||||
func ArrayConcatAll[A any](data ...[]A) []A {
|
||||
// get the full size
|
||||
count := array.Reduce(data, addLen[A], 0)
|
||||
buf := make([]A, count)
|
||||
// copy
|
||||
array.Reduce(data, func(idx int, seg []A) int {
|
||||
return idx + copy(buf[idx:], seg)
|
||||
}, 0)
|
||||
// returns the final array
|
||||
return buf
|
||||
}
|
11
array/monoid_test.go
Normal file
11
array/monoid_test.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package array
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
M "github.com/ibm/fp-go/monoid/testing"
|
||||
)
|
||||
|
||||
func TestMonoid(t *testing.T) {
|
||||
M.AssertLaws(t, Monoid[int]())([][]int{{}, {1}, {1, 2}})
|
||||
}
|
43
array/sequence.go
Normal file
43
array/sequence.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package array
|
||||
|
||||
import (
|
||||
F "github.com/ibm/fp-go/function"
|
||||
O "github.com/ibm/fp-go/option"
|
||||
)
|
||||
|
||||
// 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
|
||||
|
||||
// HKTA = HKT<A>
|
||||
// HKTRA = HKT<[]A>
|
||||
// HKTFRA = HKT<func(A)[]A>
|
||||
|
||||
// Sequence takes an `Array` where elements are `HKT<A>` (higher kinded type) and,
|
||||
// using an applicative of that `HKT`, returns an `HKT` of `[]A`.
|
||||
// e.g. it can turn an `[]Either[error, string]` into an `Either[error, []string]`.
|
||||
//
|
||||
// Sequence requires an `Applicative` of the `HKT` you are targeting, e.g. to turn an
|
||||
// `[]Either[E, A]` into an `Either[E, []A]`, it needs an
|
||||
// Applicative` for `Either`, to to turn an `[]Option[A]` into an `Option[ []A]`,
|
||||
// it needs an `Applicative` for `Option`.
|
||||
func Sequence[A, HKTA, HKTRA, HKTFRA any](
|
||||
_of func([]A) HKTRA,
|
||||
_map func(HKTRA, func([]A) func(A) []A) HKTFRA,
|
||||
_ap func(HKTFRA, HKTA) HKTRA,
|
||||
) func([]HKTA) HKTRA {
|
||||
ca := F.Curry2(Append[A])
|
||||
return Reduce(func(fas HKTRA, fa HKTA) HKTRA {
|
||||
return _ap(
|
||||
_map(fas, ca),
|
||||
fa,
|
||||
)
|
||||
}, _of(Empty[A]()))
|
||||
}
|
||||
|
||||
// ArrayOption returns a function to convert sequence of options into an option of a sequence
|
||||
func ArrayOption[A any]() func([]O.Option[A]) O.Option[[]A] {
|
||||
return Sequence(
|
||||
O.Of[[]A],
|
||||
O.MonadMap[[]A, func(A) []A],
|
||||
O.MonadAp[[]A, A],
|
||||
)
|
||||
}
|
16
array/sequence_test.go
Normal file
16
array/sequence_test.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package array
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
O "github.com/ibm/fp-go/option"
|
||||
)
|
||||
|
||||
func TestSequenceOption(t *testing.T) {
|
||||
seq := ArrayOption[int]()
|
||||
|
||||
assert.Equal(t, O.Of([]int{1, 3}), seq([]O.Option[int]{O.Of(1), O.Of(3)}))
|
||||
assert.Equal(t, O.None[[]int](), seq([]O.Option[int]{O.Of(1), O.None[int]()}))
|
||||
}
|
11
array/sort.go
Normal file
11
array/sort.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package array
|
||||
|
||||
import (
|
||||
G "github.com/ibm/fp-go/array/generic"
|
||||
O "github.com/ibm/fp-go/ord"
|
||||
)
|
||||
|
||||
// Sort implements a stable sort on the array given the provided ordering
|
||||
func Sort[T any](ord O.Ord[T]) func(ma []T) []T {
|
||||
return G.Sort[[]T](ord)
|
||||
}
|
21
array/sort_test.go
Normal file
21
array/sort_test.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package array
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
O "github.com/ibm/fp-go/ord"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestSort(t *testing.T) {
|
||||
|
||||
ordInt := O.FromStrictCompare[int]()
|
||||
|
||||
input := []int{2, 1, 3}
|
||||
|
||||
res := Sort(ordInt)(input)
|
||||
|
||||
assert.Equal(t, []int{1, 2, 3}, res)
|
||||
assert.Equal(t, []int{2, 1, 3}, input)
|
||||
|
||||
}
|
23
array/traverse.go
Normal file
23
array/traverse.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package array
|
||||
|
||||
import "github.com/ibm/fp-go/internal/array"
|
||||
|
||||
func Traverse[A, B, HKTB, HKTAB, HKTRB any](
|
||||
fof func([]B) HKTRB,
|
||||
fmap func(func([]B) func(B) []B) func(HKTRB) HKTAB,
|
||||
fap func(HKTB) func(HKTAB) HKTRB,
|
||||
|
||||
f func(A) HKTB) func([]A) HKTRB {
|
||||
return array.Traverse[[]A](fof, fmap, fap, f)
|
||||
}
|
||||
|
||||
func MonadTraverse[A, B, HKTB, HKTAB, HKTRB any](
|
||||
fof func([]B) HKTRB,
|
||||
fmap func(func([]B) func(B) []B) func(HKTRB) HKTAB,
|
||||
fap func(HKTB) func(HKTAB) HKTRB,
|
||||
|
||||
ta []A,
|
||||
f func(A) HKTB) HKTRB {
|
||||
|
||||
return array.MonadTraverse(fof, fmap, fap, ta, f)
|
||||
}
|
28
array/traverse_test.go
Normal file
28
array/traverse_test.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package array
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
O "github.com/ibm/fp-go/option"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
type ArrayType = []int
|
||||
|
||||
func TestTraverse(t *testing.T) {
|
||||
|
||||
traverse := Traverse(
|
||||
O.Of[ArrayType],
|
||||
O.Map[ArrayType, func(int) ArrayType],
|
||||
O.Ap[ArrayType, int],
|
||||
|
||||
func(n int) O.Option[int] {
|
||||
if n%2 == 0 {
|
||||
return O.None[int]()
|
||||
}
|
||||
return O.Of(n)
|
||||
})
|
||||
|
||||
assert.Equal(t, O.None[[]int](), traverse(ArrayType{1, 2}))
|
||||
assert.Equal(t, O.Of(ArrayType{1, 3}), traverse(ArrayType{1, 3}))
|
||||
}
|
5
bytes/bytes.go
Normal file
5
bytes/bytes.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package bytes
|
||||
|
||||
func ToString(a []byte) string {
|
||||
return string(a)
|
||||
}
|
19
bytes/monoid.go
Normal file
19
bytes/monoid.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package bytes
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
A "github.com/ibm/fp-go/array"
|
||||
O "github.com/ibm/fp-go/ord"
|
||||
)
|
||||
|
||||
var (
|
||||
// monoid for byte arrays
|
||||
Monoid = A.Monoid[byte]()
|
||||
|
||||
// ConcatAll concatenates all bytes
|
||||
ConcatAll = A.ArrayConcatAll[byte]
|
||||
|
||||
// Ord implements the default ordering on bytes
|
||||
Ord = O.MakeOrd(bytes.Compare, bytes.Equal)
|
||||
)
|
11
bytes/monoid_test.go
Normal file
11
bytes/monoid_test.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package bytes
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
M "github.com/ibm/fp-go/monoid/testing"
|
||||
)
|
||||
|
||||
func TestMonoid(t *testing.T) {
|
||||
M.AssertLaws(t, Monoid)([][]byte{[]byte(""), []byte("a"), []byte("some value")})
|
||||
}
|
417
cli/apply.go
Normal file
417
cli/apply.go
Normal file
@@ -0,0 +1,417 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
C "github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func generateTraverseTuple(f *os.File, i int) {
|
||||
fmt.Fprintf(f, "\n// TraverseTuple%d is a utility function used to implement the sequence operation for higher kinded types based only on map and ap.\n", i)
|
||||
fmt.Fprintf(f, "// The function takes a [Tuple%d] of base types and %d functions that transform these based types into higher higher kinded types. It returns a higher kinded type of a [Tuple%d] with the resolved values.\n", i, i, i)
|
||||
fmt.Fprintf(f, "func TraverseTuple%d[\n", i)
|
||||
// map as the starting point
|
||||
fmt.Fprintf(f, " MAP ~func(")
|
||||
for j := 0; j < i; j++ {
|
||||
if j > 0 {
|
||||
fmt.Fprintf(f, " ")
|
||||
}
|
||||
fmt.Fprintf(f, "func(T%d)", j+1)
|
||||
}
|
||||
fmt.Fprintf(f, " ")
|
||||
fmt.Fprintf(f, "T.")
|
||||
writeTupleType(f, "T", i)
|
||||
fmt.Fprintf(f, ") func(HKT_T1)")
|
||||
if i > 1 {
|
||||
fmt.Fprintf(f, " HKT_F")
|
||||
for k := 1; k < i; k++ {
|
||||
fmt.Fprintf(f, "_T%d", k+1)
|
||||
}
|
||||
} else {
|
||||
fmt.Fprintf(f, " HKT_TUPLE%d", i)
|
||||
}
|
||||
fmt.Fprintf(f, ",\n")
|
||||
// the applicatives
|
||||
for j := 1; j < i; j++ {
|
||||
fmt.Fprintf(f, " AP%d ~func(", j)
|
||||
fmt.Fprintf(f, "HKT_T%d) func(", j+1)
|
||||
fmt.Fprintf(f, "HKT_F")
|
||||
for k := j; k < i; k++ {
|
||||
fmt.Fprintf(f, "_T%d", k+1)
|
||||
}
|
||||
fmt.Fprintf(f, ")")
|
||||
if j+1 < i {
|
||||
fmt.Fprintf(f, " HKT_F")
|
||||
for k := j + 1; k < i; k++ {
|
||||
fmt.Fprintf(f, "_T%d", k+1)
|
||||
}
|
||||
} else {
|
||||
fmt.Fprintf(f, " HKT_TUPLE%d", i)
|
||||
}
|
||||
fmt.Fprintf(f, ",\n")
|
||||
}
|
||||
for j := 0; j < i; j++ {
|
||||
fmt.Fprintf(f, " F%d ~func(A%d) HKT_T%d,\n", j+1, j+1, j+1)
|
||||
}
|
||||
for j := 0; j < i; j++ {
|
||||
fmt.Fprintf(f, " A%d, T%d,\n", j+1, j+1)
|
||||
}
|
||||
for j := 0; j < i; j++ {
|
||||
fmt.Fprintf(f, " HKT_T%d, // HKT[T%d]\n", j+1, j+1)
|
||||
}
|
||||
for j := 1; j < i; j++ {
|
||||
fmt.Fprintf(f, " HKT_F")
|
||||
for k := j; k < i; k++ {
|
||||
fmt.Fprintf(f, "_T%d", k+1)
|
||||
}
|
||||
fmt.Fprintf(f, ", // HKT[")
|
||||
for k := j; k < i; k++ {
|
||||
fmt.Fprintf(f, "func(T%d) ", k+1)
|
||||
}
|
||||
fmt.Fprintf(f, "T.")
|
||||
writeTupleType(f, "T", i)
|
||||
fmt.Fprintf(f, "]\n")
|
||||
}
|
||||
fmt.Fprintf(f, " HKT_TUPLE%d any, // HKT[", i)
|
||||
writeTupleType(f, "T", i)
|
||||
fmt.Fprintf(f, "]\n")
|
||||
fmt.Fprintf(f, "](\n")
|
||||
|
||||
// the callbacks
|
||||
fmt.Fprintf(f, " fmap MAP,\n")
|
||||
for j := 1; j < i; j++ {
|
||||
fmt.Fprintf(f, " fap%d AP%d,\n", j, j)
|
||||
}
|
||||
// the transformer functions
|
||||
for j := 1; j <= i; j++ {
|
||||
fmt.Fprintf(f, " f%d F%d,\n", j, j)
|
||||
}
|
||||
// the parameters
|
||||
fmt.Fprintf(f, " t T.Tuple%d[", i)
|
||||
for j := 0; j < i; j++ {
|
||||
if j > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "A%d", j+1)
|
||||
}
|
||||
fmt.Fprintf(f, "],\n")
|
||||
fmt.Fprintf(f, ") HKT_TUPLE%d {\n", i)
|
||||
|
||||
fmt.Fprintf(f, " return F.Pipe%d(\n", i)
|
||||
fmt.Fprintf(f, " f1(t.F1),\n")
|
||||
fmt.Fprintf(f, " fmap(tupleConstructor%d[", i)
|
||||
for j := 0; j < i; j++ {
|
||||
if j > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "T%d", j+1)
|
||||
}
|
||||
fmt.Fprintf(f, "]()),\n")
|
||||
for j := 1; j < i; j++ {
|
||||
fmt.Fprintf(f, " fap%d(f%d(t.F%d)),\n", j, j+1, j+1)
|
||||
}
|
||||
fmt.Fprintf(f, ")\n")
|
||||
fmt.Fprintf(f, "}\n")
|
||||
}
|
||||
|
||||
func generateSequenceTuple(f *os.File, i int) {
|
||||
fmt.Fprintf(f, "\n// SequenceTuple%d is a utility function used to implement the sequence operation for higher kinded types based only on map and ap.\n", i)
|
||||
fmt.Fprintf(f, "// The function takes a [Tuple%d] of higher higher kinded types and returns a higher kinded type of a [Tuple%d] with the resolved values.\n", i, i)
|
||||
fmt.Fprintf(f, "func SequenceTuple%d[\n", i)
|
||||
// map as the starting point
|
||||
fmt.Fprintf(f, " MAP ~func(")
|
||||
for j := 0; j < i; j++ {
|
||||
if j > 0 {
|
||||
fmt.Fprintf(f, " ")
|
||||
}
|
||||
fmt.Fprintf(f, "func(T%d)", j+1)
|
||||
}
|
||||
fmt.Fprintf(f, " ")
|
||||
fmt.Fprintf(f, "T.")
|
||||
writeTupleType(f, "T", i)
|
||||
fmt.Fprintf(f, ") func(HKT_T1)")
|
||||
if i > 1 {
|
||||
fmt.Fprintf(f, " HKT_F")
|
||||
for k := 1; k < i; k++ {
|
||||
fmt.Fprintf(f, "_T%d", k+1)
|
||||
}
|
||||
} else {
|
||||
fmt.Fprintf(f, " HKT_TUPLE%d", i)
|
||||
}
|
||||
fmt.Fprintf(f, ",\n")
|
||||
// the applicatives
|
||||
for j := 1; j < i; j++ {
|
||||
fmt.Fprintf(f, " AP%d ~func(", j)
|
||||
fmt.Fprintf(f, "HKT_T%d) func(", j+1)
|
||||
fmt.Fprintf(f, "HKT_F")
|
||||
for k := j; k < i; k++ {
|
||||
fmt.Fprintf(f, "_T%d", k+1)
|
||||
}
|
||||
fmt.Fprintf(f, ")")
|
||||
if j+1 < i {
|
||||
fmt.Fprintf(f, " HKT_F")
|
||||
for k := j + 1; k < i; k++ {
|
||||
fmt.Fprintf(f, "_T%d", k+1)
|
||||
}
|
||||
} else {
|
||||
fmt.Fprintf(f, " HKT_TUPLE%d", i)
|
||||
}
|
||||
fmt.Fprintf(f, ",\n")
|
||||
}
|
||||
|
||||
for j := 0; j < i; j++ {
|
||||
fmt.Fprintf(f, " T%d,\n", j+1)
|
||||
}
|
||||
for j := 0; j < i; j++ {
|
||||
fmt.Fprintf(f, " HKT_T%d, // HKT[T%d]\n", j+1, j+1)
|
||||
}
|
||||
for j := 1; j < i; j++ {
|
||||
fmt.Fprintf(f, " HKT_F")
|
||||
for k := j; k < i; k++ {
|
||||
fmt.Fprintf(f, "_T%d", k+1)
|
||||
}
|
||||
fmt.Fprintf(f, ", // HKT[")
|
||||
for k := j; k < i; k++ {
|
||||
fmt.Fprintf(f, "func(T%d) ", k+1)
|
||||
}
|
||||
fmt.Fprintf(f, "T.")
|
||||
writeTupleType(f, "T", i)
|
||||
fmt.Fprintf(f, "]\n")
|
||||
}
|
||||
fmt.Fprintf(f, " HKT_TUPLE%d any, // HKT[", i)
|
||||
writeTupleType(f, "T", i)
|
||||
fmt.Fprintf(f, "]\n")
|
||||
fmt.Fprintf(f, "](\n")
|
||||
|
||||
// the callbacks
|
||||
fmt.Fprintf(f, " fmap MAP,\n")
|
||||
for j := 1; j < i; j++ {
|
||||
fmt.Fprintf(f, " fap%d AP%d,\n", j, j)
|
||||
}
|
||||
// the parameters
|
||||
fmt.Fprintf(f, " t T.Tuple%d[", i)
|
||||
for j := 0; j < i; j++ {
|
||||
if j > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "HKT_T%d", j+1)
|
||||
}
|
||||
fmt.Fprintf(f, "],\n")
|
||||
fmt.Fprintf(f, ") HKT_TUPLE%d {\n", i)
|
||||
|
||||
fmt.Fprintf(f, " return F.Pipe%d(\n", i)
|
||||
fmt.Fprintf(f, " t.F1,\n")
|
||||
fmt.Fprintf(f, " fmap(tupleConstructor%d[", i)
|
||||
for j := 0; j < i; j++ {
|
||||
if j > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "T%d", j+1)
|
||||
}
|
||||
fmt.Fprintf(f, "]()),\n")
|
||||
for j := 1; j < i; j++ {
|
||||
fmt.Fprintf(f, " fap%d(t.F%d),\n", j, j+1)
|
||||
}
|
||||
fmt.Fprintf(f, ")\n")
|
||||
fmt.Fprintf(f, "}\n")
|
||||
}
|
||||
|
||||
func generateSequenceT(f *os.File, i int) {
|
||||
fmt.Fprintf(f, "\n// SequenceT%d is a utility function used to implement the sequence operation for higher kinded types based only on map and ap.\n", i)
|
||||
fmt.Fprintf(f, "// The function takes %d higher higher kinded types and returns a higher kinded type of a [Tuple%d] with the resolved values.\n", i, i)
|
||||
fmt.Fprintf(f, "func SequenceT%d[\n", i)
|
||||
// map as the starting point
|
||||
fmt.Fprintf(f, " MAP ~func(")
|
||||
for j := 0; j < i; j++ {
|
||||
if j > 0 {
|
||||
fmt.Fprintf(f, " ")
|
||||
}
|
||||
fmt.Fprintf(f, "func(T%d)", j+1)
|
||||
}
|
||||
fmt.Fprintf(f, " ")
|
||||
fmt.Fprintf(f, "T.")
|
||||
writeTupleType(f, "T", i)
|
||||
fmt.Fprintf(f, ") func(HKT_T1)")
|
||||
if i > 1 {
|
||||
fmt.Fprintf(f, " HKT_F")
|
||||
for k := 1; k < i; k++ {
|
||||
fmt.Fprintf(f, "_T%d", k+1)
|
||||
}
|
||||
} else {
|
||||
fmt.Fprintf(f, " HKT_TUPLE%d", i)
|
||||
}
|
||||
fmt.Fprintf(f, ",\n")
|
||||
// the applicatives
|
||||
for j := 1; j < i; j++ {
|
||||
fmt.Fprintf(f, " AP%d ~func(", j)
|
||||
fmt.Fprintf(f, "HKT_T%d) func(", j+1)
|
||||
fmt.Fprintf(f, "HKT_F")
|
||||
for k := j; k < i; k++ {
|
||||
fmt.Fprintf(f, "_T%d", k+1)
|
||||
}
|
||||
fmt.Fprintf(f, ")")
|
||||
if j+1 < i {
|
||||
fmt.Fprintf(f, " HKT_F")
|
||||
for k := j + 1; k < i; k++ {
|
||||
fmt.Fprintf(f, "_T%d", k+1)
|
||||
}
|
||||
} else {
|
||||
fmt.Fprintf(f, " HKT_TUPLE%d", i)
|
||||
}
|
||||
fmt.Fprintf(f, ",\n")
|
||||
}
|
||||
|
||||
for j := 0; j < i; j++ {
|
||||
fmt.Fprintf(f, " T%d,\n", j+1)
|
||||
}
|
||||
for j := 0; j < i; j++ {
|
||||
fmt.Fprintf(f, " HKT_T%d, // HKT[T%d]\n", j+1, j+1)
|
||||
}
|
||||
for j := 1; j < i; j++ {
|
||||
fmt.Fprintf(f, " HKT_F")
|
||||
for k := j; k < i; k++ {
|
||||
fmt.Fprintf(f, "_T%d", k+1)
|
||||
}
|
||||
fmt.Fprintf(f, ", // HKT[")
|
||||
for k := j; k < i; k++ {
|
||||
fmt.Fprintf(f, "func(T%d) ", k+1)
|
||||
}
|
||||
fmt.Fprintf(f, "T.")
|
||||
writeTupleType(f, "T", i)
|
||||
fmt.Fprintf(f, "]\n")
|
||||
}
|
||||
fmt.Fprintf(f, " HKT_TUPLE%d any, // HKT[", i)
|
||||
writeTupleType(f, "T", i)
|
||||
fmt.Fprintf(f, "]\n")
|
||||
fmt.Fprintf(f, "](\n")
|
||||
|
||||
// the callbacks
|
||||
fmt.Fprintf(f, " fmap MAP,\n")
|
||||
for j := 1; j < i; j++ {
|
||||
fmt.Fprintf(f, " fap%d AP%d,\n", j, j)
|
||||
}
|
||||
// the parameters
|
||||
for j := 0; j < i; j++ {
|
||||
fmt.Fprintf(f, " t%d HKT_T%d,\n", j+1, j+1)
|
||||
}
|
||||
fmt.Fprintf(f, ") HKT_TUPLE%d {\n", i)
|
||||
|
||||
fmt.Fprintf(f, " return F.Pipe%d(\n", i)
|
||||
fmt.Fprintf(f, " t1,\n")
|
||||
fmt.Fprintf(f, " fmap(tupleConstructor%d[", i)
|
||||
for j := 0; j < i; j++ {
|
||||
if j > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "T%d", j+1)
|
||||
}
|
||||
fmt.Fprintf(f, "]()),\n")
|
||||
for j := 1; j < i; j++ {
|
||||
fmt.Fprintf(f, " fap%d(t%d),\n", j, j+1)
|
||||
}
|
||||
fmt.Fprintf(f, ")\n")
|
||||
fmt.Fprintf(f, "}\n")
|
||||
}
|
||||
|
||||
func generateTupleConstructor(f *os.File, i int) {
|
||||
// Create the optionize version
|
||||
fmt.Fprintf(f, "\n// tupleConstructor%d returns a curried version of [T.MakeTuple%d]\n", i, i)
|
||||
fmt.Fprintf(f, "func tupleConstructor%d[", i)
|
||||
for j := 0; j < i; j++ {
|
||||
if j > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "T%d", j+1)
|
||||
}
|
||||
fmt.Fprintf(f, " any]()")
|
||||
for j := 0; j < i; j++ {
|
||||
fmt.Fprintf(f, " func(T%d)", j+1)
|
||||
}
|
||||
fmt.Fprintf(f, " T.Tuple%d[", i)
|
||||
for j := 0; j < i; j++ {
|
||||
if j > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "T%d", j+1)
|
||||
}
|
||||
fmt.Fprintf(f, "] {\n")
|
||||
|
||||
fmt.Fprintf(f, " return F.Curry%d(T.MakeTuple%d[", i, i)
|
||||
for j := 0; j < i; j++ {
|
||||
if j > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "T%d", j+1)
|
||||
}
|
||||
fmt.Fprintf(f, "])\n")
|
||||
|
||||
fmt.Fprintf(f, "}\n")
|
||||
}
|
||||
|
||||
func generateApplyHelpers(filename string, count int) error {
|
||||
dir, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
absDir, err := filepath.Abs(dir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pkg := filepath.Base(absDir)
|
||||
f, err := os.Create(filepath.Clean(filename))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
// log
|
||||
log.Printf("Generating code in [%s] for package [%s] with [%d] repetitions ...", filename, pkg, count)
|
||||
|
||||
// some header
|
||||
fmt.Fprintln(f, "// Code generated by go generate; DO NOT EDIT.")
|
||||
fmt.Fprintln(f, "// This file was generated by robots at")
|
||||
fmt.Fprintf(f, "// %s\n", time.Now())
|
||||
|
||||
fmt.Fprintf(f, "package %s\n\n", pkg)
|
||||
|
||||
// print out some helpers
|
||||
fmt.Fprintf(f, `
|
||||
import (
|
||||
F "github.com/ibm/fp-go/function"
|
||||
T "github.com/ibm/fp-go/tuple"
|
||||
)
|
||||
`)
|
||||
|
||||
for i := 1; i <= count; i++ {
|
||||
// tuple constructor
|
||||
generateTupleConstructor(f, i)
|
||||
// sequenceT
|
||||
generateSequenceT(f, i)
|
||||
// sequenceTuple
|
||||
generateSequenceTuple(f, i)
|
||||
// traverseTuple
|
||||
generateTraverseTuple(f, i)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func ApplyCommand() *C.Command {
|
||||
return &C.Command{
|
||||
Name: "apply",
|
||||
Usage: "generate code for the sequence operations of apply",
|
||||
Flags: []C.Flag{
|
||||
flagCount,
|
||||
flagFilename,
|
||||
},
|
||||
Action: func(ctx *C.Context) error {
|
||||
return generateApplyHelpers(
|
||||
ctx.String(keyFilename),
|
||||
ctx.Int(keyCount),
|
||||
)
|
||||
},
|
||||
}
|
||||
}
|
279
cli/bind.go
Normal file
279
cli/bind.go
Normal file
@@ -0,0 +1,279 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
C "github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func createCombinations(n int, all, prev []int) [][]int {
|
||||
l := len(prev)
|
||||
if l == n {
|
||||
return [][]int{prev}
|
||||
}
|
||||
var res [][]int
|
||||
for idx, val := range all {
|
||||
cpy := make([]int, l+1)
|
||||
copy(cpy, prev)
|
||||
cpy[l] = val
|
||||
|
||||
res = append(res, createCombinations(n, all[idx+1:], cpy)...)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func remaining(comb []int, total int) []int {
|
||||
var res []int
|
||||
mp := make(map[int]int)
|
||||
for _, idx := range comb {
|
||||
mp[idx] = idx
|
||||
}
|
||||
for i := 1; i <= total; i++ {
|
||||
_, ok := mp[i]
|
||||
if !ok {
|
||||
res = append(res, i)
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func generateCombSingleBind(f *os.File, comb [][]int, total int) {
|
||||
for _, c := range comb {
|
||||
// remaining indexes
|
||||
rem := remaining(c, total)
|
||||
|
||||
// bind function
|
||||
fmt.Fprintf(f, "\n// Bind")
|
||||
for _, idx := range c {
|
||||
fmt.Fprintf(f, "%d", idx)
|
||||
}
|
||||
fmt.Fprintf(f, "of%d takes a function with %d parameters and returns a new function with %d parameters that will bind these parameters to the positions [", total, total, len(c))
|
||||
for i, idx := range c {
|
||||
if i > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "%d", idx)
|
||||
}
|
||||
fmt.Fprintf(f, "] of the original function.\n// The return value of is a function with the remaining %d parameters at positions [", len(rem))
|
||||
for i, idx := range rem {
|
||||
if i > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "%d", idx)
|
||||
}
|
||||
fmt.Fprintf(f, "] of the original function.\n")
|
||||
fmt.Fprintf(f, "func Bind")
|
||||
for _, idx := range c {
|
||||
fmt.Fprintf(f, "%d", idx)
|
||||
}
|
||||
fmt.Fprintf(f, "of%d[F ~func(", total)
|
||||
for i := 0; i < total; i++ {
|
||||
if i > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "T%d", i+1)
|
||||
}
|
||||
fmt.Fprintf(f, ") R")
|
||||
for i := 0; i < total; i++ {
|
||||
fmt.Fprintf(f, ", T%d", i+1)
|
||||
}
|
||||
fmt.Fprintf(f, ", R any](f F) func(")
|
||||
for i, idx := range c {
|
||||
if i > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "T%d", idx)
|
||||
}
|
||||
fmt.Fprintf(f, ") func(")
|
||||
for i, idx := range rem {
|
||||
if i > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "T%d", idx)
|
||||
}
|
||||
fmt.Fprintf(f, ") R {\n")
|
||||
|
||||
fmt.Fprintf(f, " return func(")
|
||||
|
||||
for i, idx := range c {
|
||||
if i > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "t%d T%d", idx, idx)
|
||||
}
|
||||
fmt.Fprintf(f, ") func(")
|
||||
for i, idx := range rem {
|
||||
if i > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "T%d", idx)
|
||||
}
|
||||
fmt.Fprintf(f, ") R {\n")
|
||||
|
||||
fmt.Fprintf(f, " return func(")
|
||||
for i, idx := range rem {
|
||||
if i > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "t%d T%d", idx, idx)
|
||||
}
|
||||
fmt.Fprintf(f, ") R {\n")
|
||||
|
||||
fmt.Fprintf(f, " return f(")
|
||||
for i := 1; i <= total; i++ {
|
||||
if i > 1 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "t%d", i)
|
||||
}
|
||||
fmt.Fprintf(f, ")\n")
|
||||
|
||||
fmt.Fprintf(f, " }\n")
|
||||
fmt.Fprintf(f, " }\n")
|
||||
fmt.Fprintf(f, "}\n")
|
||||
|
||||
// ignore function
|
||||
fmt.Fprintf(f, "\n// Ignore")
|
||||
for _, idx := range c {
|
||||
fmt.Fprintf(f, "%d", idx)
|
||||
}
|
||||
fmt.Fprintf(f, "of%d takes a function with %d parameters and returns a new function with %d parameters that will ignore the values at positions [", total, len(rem), total)
|
||||
for i, idx := range c {
|
||||
if i > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "%d", idx)
|
||||
}
|
||||
fmt.Fprintf(f, "] and pass the remaining %d parameters to the original function\n", len(rem))
|
||||
fmt.Fprintf(f, "func Ignore")
|
||||
for _, idx := range c {
|
||||
fmt.Fprintf(f, "%d", idx)
|
||||
}
|
||||
fmt.Fprintf(f, "of%d[", total)
|
||||
// start with the undefined parameters
|
||||
for i, idx := range c {
|
||||
if i > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "T%d", idx)
|
||||
}
|
||||
if len(c) > 0 {
|
||||
fmt.Fprintf(f, " any, ")
|
||||
}
|
||||
fmt.Fprintf(f, "F ~func(")
|
||||
for i, idx := range rem {
|
||||
if i > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "T%d", idx)
|
||||
}
|
||||
fmt.Fprintf(f, ") R")
|
||||
for _, idx := range rem {
|
||||
fmt.Fprintf(f, ", T%d", idx)
|
||||
}
|
||||
fmt.Fprintf(f, ", R any](f F) func(")
|
||||
for i := 0; i < total; i++ {
|
||||
if i > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "T%d", i+1)
|
||||
}
|
||||
fmt.Fprintf(f, ") R {\n")
|
||||
|
||||
fmt.Fprintf(f, " return func(")
|
||||
for i := 0; i < total; i++ {
|
||||
if i > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "t%d T%d", i+1, i+1)
|
||||
}
|
||||
fmt.Fprintf(f, ") R {\n")
|
||||
fmt.Fprintf(f, " return f(")
|
||||
for i, idx := range rem {
|
||||
if i > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "t%d", idx)
|
||||
}
|
||||
fmt.Fprintf(f, ")\n")
|
||||
|
||||
fmt.Fprintf(f, " }\n")
|
||||
fmt.Fprintf(f, "}\n")
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func generateSingleBind(f *os.File, total int) {
|
||||
|
||||
fmt.Fprintf(f, "// Combinations for a total of %d arguments\n", total)
|
||||
|
||||
// construct the indexes
|
||||
all := make([]int, total)
|
||||
for i := 0; i < total; i++ {
|
||||
all[i] = i + 1
|
||||
}
|
||||
// for all permutations of a certain length
|
||||
for j := 0; j < total; j++ {
|
||||
// get combinations of that size
|
||||
comb := createCombinations(j+1, all, []int{})
|
||||
generateCombSingleBind(f, comb, total)
|
||||
}
|
||||
}
|
||||
|
||||
func generateBind(f *os.File, i int) {
|
||||
for j := 1; j < i; j++ {
|
||||
generateSingleBind(f, j)
|
||||
}
|
||||
}
|
||||
|
||||
func generateBindHelpers(filename string, count int) error {
|
||||
dir, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
absDir, err := filepath.Abs(dir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pkg := filepath.Base(absDir)
|
||||
f, err := os.Create(filepath.Clean(filename))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
// log
|
||||
log.Printf("Generating code in [%s] for package [%s] with [%d] repetitions ...", filename, pkg, count)
|
||||
|
||||
// some header
|
||||
fmt.Fprintln(f, "// Code generated by go generate; DO NOT EDIT.")
|
||||
fmt.Fprintln(f, "// This file was generated by robots at")
|
||||
fmt.Fprintf(f, "// %s\n", time.Now())
|
||||
|
||||
fmt.Fprintf(f, "package %s\n", pkg)
|
||||
|
||||
generateBind(f, count)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func BindCommand() *C.Command {
|
||||
return &C.Command{
|
||||
Name: "bind",
|
||||
Usage: "generate code for binder functions etc",
|
||||
Description: "Code generation for bind, etc",
|
||||
Flags: []C.Flag{
|
||||
flagCount,
|
||||
flagFilename,
|
||||
},
|
||||
Action: func(ctx *C.Context) error {
|
||||
return generateBindHelpers(
|
||||
ctx.String(keyFilename),
|
||||
ctx.Int(keyCount),
|
||||
)
|
||||
},
|
||||
}
|
||||
}
|
20
cli/commands.go
Normal file
20
cli/commands.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
C "github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func Commands() []*C.Command {
|
||||
return []*C.Command{
|
||||
PipeCommand(),
|
||||
IdentityCommand(),
|
||||
OptionCommand(),
|
||||
EitherCommand(),
|
||||
TupleCommand(),
|
||||
BindCommand(),
|
||||
ApplyCommand(),
|
||||
ContextReaderIOEitherCommand(),
|
||||
ReaderIOEitherCommand(),
|
||||
ReaderCommand(),
|
||||
}
|
||||
}
|
24
cli/common.go
Normal file
24
cli/common.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
C "github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
const (
|
||||
keyFilename = "filename"
|
||||
keyCount = "count"
|
||||
)
|
||||
|
||||
var (
|
||||
flagFilename = &C.StringFlag{
|
||||
Name: keyFilename,
|
||||
Value: "gen.go",
|
||||
Usage: "Name of the generated file",
|
||||
}
|
||||
|
||||
flagCount = &C.IntFlag{
|
||||
Name: keyCount,
|
||||
Value: 20,
|
||||
Usage: "Number of variations to create",
|
||||
}
|
||||
)
|
143
cli/contextreaderioeither.go
Normal file
143
cli/contextreaderioeither.go
Normal file
@@ -0,0 +1,143 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
C "github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func generateContextReaderIOEitherEitherize(f, fg *os.File, i int) {
|
||||
// non generic version
|
||||
fmt.Fprintf(f, "\n// Eitherize%d converts a function with %d parameters returning a tuple into a function with %d parameters returning a [ReaderIOEither[R]]\n// The inverse function is [Uneitherize%d]\n", i, i, i, i)
|
||||
fmt.Fprintf(f, "func Eitherize%d[F ~func(context.Context", i)
|
||||
for j := 0; j < i; j++ {
|
||||
fmt.Fprintf(f, ", T%d", j)
|
||||
}
|
||||
fmt.Fprintf(f, ") (R, error)")
|
||||
for j := 0; j < i; j++ {
|
||||
fmt.Fprintf(f, ", T%d", j)
|
||||
}
|
||||
fmt.Fprintf(f, ", R any](f F) func(")
|
||||
for j := 0; j < i; j++ {
|
||||
if j > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "T%d", j)
|
||||
}
|
||||
fmt.Fprintf(f, ") ReaderIOEither[R] {\n")
|
||||
fmt.Fprintf(f, " return G.Eitherize%d[ReaderIOEither[R]](f)\n", i)
|
||||
fmt.Fprintln(f, "}")
|
||||
|
||||
// generic version
|
||||
fmt.Fprintf(fg, "\n// Eitherize%d converts a function with %d parameters returning a tuple into a function with %d parameters returning a [GRA]\n// The inverse function is [Uneitherize%d]\n", i, i, i, i)
|
||||
fmt.Fprintf(fg, "func Eitherize%d[GRA ~func(context.Context) GIOA, F ~func(context.Context", i)
|
||||
for j := 0; j < i; j++ {
|
||||
fmt.Fprintf(fg, ", T%d", j)
|
||||
}
|
||||
fmt.Fprintf(fg, ") (R, error), GIOA ~func() E.Either[error, R]")
|
||||
for j := 0; j < i; j++ {
|
||||
fmt.Fprintf(fg, ", T%d", j)
|
||||
}
|
||||
fmt.Fprintf(fg, ", R any](f F) func(")
|
||||
for j := 0; j < i; j++ {
|
||||
if j > 0 {
|
||||
fmt.Fprintf(fg, ", ")
|
||||
}
|
||||
fmt.Fprintf(fg, "T%d", j)
|
||||
}
|
||||
fmt.Fprintf(fg, ") GRA {\n")
|
||||
fmt.Fprintf(fg, " return RE.Eitherize%d[GRA](f)\n", i)
|
||||
fmt.Fprintln(fg, "}")
|
||||
}
|
||||
|
||||
func generateContextReaderIOEitherHelpers(filename string, count int) error {
|
||||
dir, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
absDir, err := filepath.Abs(dir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pkg := filepath.Base(absDir)
|
||||
f, err := os.Create(filepath.Clean(filename))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
// construct subdirectory
|
||||
genFilename := filepath.Join("generic", filename)
|
||||
err = os.MkdirAll("generic", os.ModePerm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fg, err := os.Create(filepath.Clean(genFilename))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer fg.Close()
|
||||
|
||||
// log
|
||||
log.Printf("Generating code in [%s] for package [%s] with [%d] repetitions ...", filename, pkg, count)
|
||||
|
||||
// some header
|
||||
fmt.Fprintln(f, "// Code generated by go generate; DO NOT EDIT.")
|
||||
fmt.Fprintln(f, "// This file was generated by robots at")
|
||||
fmt.Fprintf(f, "// %s\n", time.Now())
|
||||
|
||||
fmt.Fprintf(f, "package %s\n\n", pkg)
|
||||
|
||||
fmt.Fprintf(f, `
|
||||
import (
|
||||
"context"
|
||||
|
||||
G "github.com/ibm/fp-go/context/%s/generic"
|
||||
)
|
||||
`, pkg)
|
||||
|
||||
// some header
|
||||
fmt.Fprintln(fg, "// Code generated by go generate; DO NOT EDIT.")
|
||||
fmt.Fprintln(fg, "// This file was generated by robots at")
|
||||
fmt.Fprintf(fg, "// %s\n", time.Now())
|
||||
|
||||
fmt.Fprintf(fg, "package generic\n\n")
|
||||
|
||||
fmt.Fprintf(fg, `
|
||||
import (
|
||||
"context"
|
||||
|
||||
E "github.com/ibm/fp-go/either"
|
||||
RE "github.com/ibm/fp-go/readerioeither/generic"
|
||||
)
|
||||
`)
|
||||
|
||||
generateContextReaderIOEitherEitherize(f, fg, 0)
|
||||
|
||||
for i := 1; i <= count; i++ {
|
||||
// eitherize
|
||||
generateContextReaderIOEitherEitherize(f, fg, i)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func ContextReaderIOEitherCommand() *C.Command {
|
||||
return &C.Command{
|
||||
Name: "contextreaderioeither",
|
||||
Usage: "generate code for ContextReaderIOEither",
|
||||
Flags: []C.Flag{
|
||||
flagCount,
|
||||
flagFilename,
|
||||
},
|
||||
Action: func(ctx *C.Context) error {
|
||||
return generateContextReaderIOEitherHelpers(
|
||||
ctx.String(keyFilename),
|
||||
ctx.Int(keyCount),
|
||||
)
|
||||
},
|
||||
}
|
||||
}
|
187
cli/either.go
Normal file
187
cli/either.go
Normal file
@@ -0,0 +1,187 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
C "github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func eitherHKT(typeE string) func(typeA string) string {
|
||||
return func(typeA string) string {
|
||||
return fmt.Sprintf("Either[%s, %s]", typeE, typeA)
|
||||
}
|
||||
}
|
||||
|
||||
func generateEitherTraverseTuple(f *os.File, i int) {
|
||||
generateTraverseTuple1(eitherHKT("E"), "E")(f, i)
|
||||
}
|
||||
|
||||
func generateEitherSequenceTuple(f *os.File, i int) {
|
||||
generateSequenceTuple1(eitherHKT("E"), "E")(f, i)
|
||||
}
|
||||
|
||||
func generateEitherSequenceT(f *os.File, i int) {
|
||||
generateSequenceT1(eitherHKT("E"), "E")(f, i)
|
||||
}
|
||||
|
||||
func generateUneitherize(f *os.File, i int) {
|
||||
// Create the optionize version
|
||||
fmt.Fprintf(f, "\n// Uneitherize%d converts a function with %d parameters returning an Either into a function with %d parameters returning a tuple\n// The inverse function is [Eitherize%d]\n", i, i, i, i)
|
||||
fmt.Fprintf(f, "func Uneitherize%d[F ~func(", i)
|
||||
for j := 0; j < i; j++ {
|
||||
if j > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "T%d", j)
|
||||
}
|
||||
fmt.Fprintf(f, ") Either[error, R]")
|
||||
for j := 0; j < i; j++ {
|
||||
fmt.Fprintf(f, ", T%d", j)
|
||||
}
|
||||
fmt.Fprintf(f, ", R any](f F) func(")
|
||||
for j := 0; j < i; j++ {
|
||||
if j > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "T%d", j)
|
||||
}
|
||||
fmt.Fprintf(f, ") (R, error) {\n")
|
||||
fmt.Fprintf(f, " return func(")
|
||||
for j := 0; j < i; j++ {
|
||||
if j > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "t%d T%d", j, j)
|
||||
}
|
||||
fmt.Fprintf(f, ") (R, error) {\n")
|
||||
fmt.Fprintf(f, " return UnwrapError(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, "}")
|
||||
}
|
||||
|
||||
func generateEitherize(f *os.File, i int) {
|
||||
// Create the optionize version
|
||||
fmt.Fprintf(f, "\n// Eitherize%d converts a function with %d parameters returning a tuple into a function with %d parameters returning an Either\n// The inverse function is [Uneitherize%d]\n", i, i, i, i)
|
||||
fmt.Fprintf(f, "func Eitherize%d[F ~func(", i)
|
||||
for j := 0; j < i; j++ {
|
||||
if j > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "T%d", j)
|
||||
}
|
||||
fmt.Fprintf(f, ") (R, error)")
|
||||
for j := 0; j < i; j++ {
|
||||
fmt.Fprintf(f, ", T%d", j)
|
||||
}
|
||||
fmt.Fprintf(f, ", R any](f F) func(")
|
||||
for j := 0; j < i; j++ {
|
||||
if j > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "T%d", j)
|
||||
}
|
||||
fmt.Fprintf(f, ") Either[error, R] {\n")
|
||||
fmt.Fprintf(f, " return func(")
|
||||
for j := 0; j < i; j++ {
|
||||
if j > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
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(")
|
||||
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, "}")
|
||||
}
|
||||
|
||||
func generateEitherHelpers(filename string, count int) error {
|
||||
dir, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
absDir, err := filepath.Abs(dir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pkg := filepath.Base(absDir)
|
||||
f, err := os.Create(filepath.Clean(filename))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
// log
|
||||
log.Printf("Generating code in [%s] for package [%s] with [%d] repetitions ...", filename, pkg, count)
|
||||
|
||||
// some header
|
||||
fmt.Fprintln(f, "// Code generated by go generate; DO NOT EDIT.")
|
||||
fmt.Fprintln(f, "// This file was generated by robots at")
|
||||
fmt.Fprintf(f, "// %s\n", time.Now())
|
||||
|
||||
fmt.Fprintf(f, "package %s\n\n", pkg)
|
||||
|
||||
fmt.Fprintf(f, `
|
||||
import (
|
||||
A "github.com/ibm/fp-go/internal/apply"
|
||||
T "github.com/ibm/fp-go/tuple"
|
||||
)
|
||||
`)
|
||||
|
||||
// zero level functions
|
||||
|
||||
// optionize
|
||||
generateEitherize(f, 0)
|
||||
// unoptionize
|
||||
generateUneitherize(f, 0)
|
||||
|
||||
for i := 1; i <= count; i++ {
|
||||
// optionize
|
||||
generateEitherize(f, i)
|
||||
// unoptionize
|
||||
generateUneitherize(f, i)
|
||||
// sequenceT
|
||||
generateEitherSequenceT(f, i)
|
||||
// sequenceTuple
|
||||
generateEitherSequenceTuple(f, i)
|
||||
// traverseTuple
|
||||
generateEitherTraverseTuple(f, i)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func EitherCommand() *C.Command {
|
||||
return &C.Command{
|
||||
Name: "either",
|
||||
Usage: "generate code for Either",
|
||||
Flags: []C.Flag{
|
||||
flagCount,
|
||||
flagFilename,
|
||||
},
|
||||
Action: func(ctx *C.Context) error {
|
||||
return generateEitherHelpers(
|
||||
ctx.String(keyFilename),
|
||||
ctx.Int(keyCount),
|
||||
)
|
||||
},
|
||||
}
|
||||
}
|
88
cli/identity.go
Normal file
88
cli/identity.go
Normal file
@@ -0,0 +1,88 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
C "github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func identityHKT(typeA string) string {
|
||||
return typeA
|
||||
}
|
||||
|
||||
func generateIdentityTraverseTuple(f *os.File, i int) {
|
||||
generateTraverseTuple1(identityHKT, "")(f, i)
|
||||
}
|
||||
|
||||
func generateIdentitySequenceTuple(f *os.File, i int) {
|
||||
generateSequenceTuple1(identityHKT, "")(f, i)
|
||||
}
|
||||
|
||||
func generateIdentitySequenceT(f *os.File, i int) {
|
||||
generateSequenceT1(identityHKT, "")(f, i)
|
||||
}
|
||||
|
||||
func generateIdentityHelpers(filename string, count int) error {
|
||||
dir, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
absDir, err := filepath.Abs(dir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pkg := filepath.Base(absDir)
|
||||
f, err := os.Create(filepath.Clean(filename))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
// log
|
||||
log.Printf("Generating code in [%s] for package [%s] with [%d] repetitions ...", filename, pkg, count)
|
||||
|
||||
// some header
|
||||
fmt.Fprintln(f, "// Code generated by go generate; DO NOT EDIT.")
|
||||
fmt.Fprintln(f, "// This file was generated by robots at")
|
||||
fmt.Fprintf(f, "// %s\n", time.Now())
|
||||
|
||||
fmt.Fprintf(f, "package %s\n\n", pkg)
|
||||
|
||||
fmt.Fprintf(f, `
|
||||
import (
|
||||
A "github.com/ibm/fp-go/internal/apply"
|
||||
T "github.com/ibm/fp-go/tuple"
|
||||
)
|
||||
`)
|
||||
|
||||
for i := 1; i <= count; i++ {
|
||||
// sequenceT
|
||||
generateIdentitySequenceT(f, i)
|
||||
// sequenceTuple
|
||||
generateIdentitySequenceTuple(f, i)
|
||||
// traverseTuple
|
||||
generateIdentityTraverseTuple(f, i)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func IdentityCommand() *C.Command {
|
||||
return &C.Command{
|
||||
Name: "identity",
|
||||
Usage: "generate code for Identity",
|
||||
Flags: []C.Flag{
|
||||
flagCount,
|
||||
flagFilename,
|
||||
},
|
||||
Action: func(ctx *C.Context) error {
|
||||
return generateIdentityHelpers(
|
||||
ctx.String(keyFilename),
|
||||
ctx.Int(keyCount),
|
||||
)
|
||||
},
|
||||
}
|
||||
}
|
342
cli/monad.go
Normal file
342
cli/monad.go
Normal file
@@ -0,0 +1,342 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func tupleType(i int) string {
|
||||
var buf strings.Builder
|
||||
buf.WriteString(fmt.Sprintf("T.Tuple%d[", i))
|
||||
for j := 0; j < i; j++ {
|
||||
if j > 0 {
|
||||
buf.WriteString(", ")
|
||||
}
|
||||
buf.WriteString(fmt.Sprintf("T%d", j+1))
|
||||
}
|
||||
buf.WriteString("]")
|
||||
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func monadGenerateSequenceTNonGeneric(
|
||||
hkt func(string) string,
|
||||
fmap func(string, string) string,
|
||||
fap func(string, string) string,
|
||||
) func(f *os.File, i int) {
|
||||
return func(f *os.File, i int) {
|
||||
|
||||
tuple := tupleType(i)
|
||||
|
||||
fmt.Fprintf(f, "SequenceT%d[", i)
|
||||
for j := 0; j < i; j++ {
|
||||
if j > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "T%d", j+1)
|
||||
}
|
||||
fmt.Fprintf(f, "](")
|
||||
for j := 0; j < i; j++ {
|
||||
if j > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "t%d %s", j+1, hkt(fmt.Sprintf("T%d", j+1)))
|
||||
}
|
||||
fmt.Fprintf(f, ") %s {", hkt(tuple))
|
||||
// the actual apply callback
|
||||
fmt.Fprintf(f, " return apply.SequenceT%d(\n", i)
|
||||
// map callback
|
||||
|
||||
curried := func(count int) string {
|
||||
var buf strings.Builder
|
||||
for j := count; j < i; j++ {
|
||||
buf.WriteString(fmt.Sprintf("func(T%d)", j+1))
|
||||
}
|
||||
buf.WriteString(tuple)
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
fmt.Fprintf(f, " %s,\n", fmap("T1", curried(1)))
|
||||
for j := 1; j < i; j++ {
|
||||
fmt.Fprintf(f, " %s,\n", fap(curried(j+1), fmt.Sprintf("T%d", j)))
|
||||
}
|
||||
|
||||
for j := 0; j < i; j++ {
|
||||
fmt.Fprintf(f, " T%d,\n", j+1)
|
||||
}
|
||||
|
||||
fmt.Fprintf(f, " )\n")
|
||||
|
||||
fmt.Fprintf(f, "}\n")
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func monadGenerateSequenceTGeneric(
|
||||
hkt func(string) string,
|
||||
fmap func(string, string) string,
|
||||
fap func(string, string) string,
|
||||
) func(f *os.File, i int) {
|
||||
return func(f *os.File, i int) {
|
||||
|
||||
tuple := tupleType(i)
|
||||
|
||||
fmt.Fprintf(f, "SequenceT%d[", i)
|
||||
for j := 0; j < i; j++ {
|
||||
if j > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "T%d", j+1)
|
||||
}
|
||||
fmt.Fprintf(f, "](")
|
||||
for j := 0; j < i; j++ {
|
||||
if j > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "t%d %s", j+1, hkt(fmt.Sprintf("T%d", j+1)))
|
||||
}
|
||||
fmt.Fprintf(f, ") %s {", hkt(tuple))
|
||||
// the actual apply callback
|
||||
fmt.Fprintf(f, " return apply.SequenceT%d(\n", i)
|
||||
// map callback
|
||||
|
||||
curried := func(count int) string {
|
||||
var buf strings.Builder
|
||||
for j := count; j < i; j++ {
|
||||
buf.WriteString(fmt.Sprintf("func(T%d)", j+1))
|
||||
}
|
||||
buf.WriteString(tuple)
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
fmt.Fprintf(f, " %s,\n", fmap("T1", curried(1)))
|
||||
for j := 1; j < i; j++ {
|
||||
fmt.Fprintf(f, " %s,\n", fap(curried(j+1), fmt.Sprintf("T%d", j)))
|
||||
}
|
||||
|
||||
for j := 0; j < i; j++ {
|
||||
fmt.Fprintf(f, " T%d,\n", j+1)
|
||||
}
|
||||
|
||||
fmt.Fprintf(f, " )\n")
|
||||
|
||||
fmt.Fprintf(f, "}\n")
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func generateTraverseTuple1(
|
||||
hkt func(string) string,
|
||||
infix string) func(f *os.File, i int) {
|
||||
|
||||
return func(f *os.File, i int) {
|
||||
tuple := tupleType(i)
|
||||
|
||||
fmt.Fprintf(f, "\n// TraverseTuple%d converts a [Tuple%d] of [A] via transformation functions transforming [A] to [%s] into a [%s].\n", i, i, hkt("A"), hkt(fmt.Sprintf("Tuple%d", i)))
|
||||
fmt.Fprintf(f, "func TraverseTuple%d[", i)
|
||||
// functions
|
||||
for j := 0; j < i; j++ {
|
||||
if j > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "F%d ~func(A%d) %s", j+1, j+1, hkt(fmt.Sprintf("T%d", j+1)))
|
||||
}
|
||||
if infix != "" {
|
||||
fmt.Fprintf(f, ", %s", infix)
|
||||
}
|
||||
// types
|
||||
for j := 0; j < i; j++ {
|
||||
fmt.Fprintf(f, ", A%d, T%d", j+1, j+1)
|
||||
}
|
||||
fmt.Fprintf(f, " any](")
|
||||
for j := 0; j < i; j++ {
|
||||
if j > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "f%d F%d", j+1, j+1)
|
||||
}
|
||||
fmt.Fprintf(f, ") func (T.Tuple%d[", i)
|
||||
for j := 0; j < i; j++ {
|
||||
if j > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "A%d", j+1)
|
||||
}
|
||||
fmt.Fprintf(f, "]) %s {\n", hkt(tuple))
|
||||
fmt.Fprintf(f, " return func(t T.Tuple%d[", i)
|
||||
for j := 0; j < i; j++ {
|
||||
if j > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "A%d", j+1)
|
||||
}
|
||||
fmt.Fprintf(f, "]) %s {\n", hkt(tuple))
|
||||
fmt.Fprintf(f, " return A.TraverseTuple%d(\n", i)
|
||||
// map
|
||||
fmt.Fprintf(f, " Map[")
|
||||
if infix != "" {
|
||||
fmt.Fprintf(f, "%s, T1,", infix)
|
||||
} else {
|
||||
fmt.Fprintf(f, "T1,")
|
||||
}
|
||||
for j := 1; j < i; j++ {
|
||||
fmt.Fprintf(f, " func(T%d)", j+1)
|
||||
}
|
||||
fmt.Fprintf(f, " %s],\n", tuple)
|
||||
// applicatives
|
||||
for j := 1; j < i; j++ {
|
||||
fmt.Fprintf(f, " Ap[")
|
||||
for k := j + 1; k < i; k++ {
|
||||
if k > j+1 {
|
||||
fmt.Fprintf(f, " ")
|
||||
}
|
||||
fmt.Fprintf(f, "func(T%d)", k+1)
|
||||
}
|
||||
if j < i-1 {
|
||||
fmt.Fprintf(f, " ")
|
||||
}
|
||||
fmt.Fprintf(f, "%s", tuple)
|
||||
if infix != "" {
|
||||
fmt.Fprintf(f, ", %s", infix)
|
||||
}
|
||||
fmt.Fprintf(f, ", T%d],\n", j+1)
|
||||
}
|
||||
for j := 0; j < i; j++ {
|
||||
fmt.Fprintf(f, " f%d,\n", j+1)
|
||||
}
|
||||
fmt.Fprintf(f, " t,\n")
|
||||
fmt.Fprintf(f, " )\n")
|
||||
fmt.Fprintf(f, " }\n")
|
||||
fmt.Fprintf(f, "}\n")
|
||||
}
|
||||
}
|
||||
|
||||
func generateSequenceTuple1(
|
||||
hkt func(string) string,
|
||||
infix string) func(f *os.File, i int) {
|
||||
|
||||
return func(f *os.File, i int) {
|
||||
|
||||
tuple := tupleType(i)
|
||||
|
||||
fmt.Fprintf(f, "\n// SequenceTuple%d converts a [Tuple%d] of [%s] into an [%s].\n", i, i, hkt("T"), hkt(fmt.Sprintf("Tuple%d", i)))
|
||||
fmt.Fprintf(f, "func SequenceTuple%d[", i)
|
||||
if infix != "" {
|
||||
fmt.Fprintf(f, "%s", infix)
|
||||
}
|
||||
for j := 0; j < i; j++ {
|
||||
if infix != "" || j > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "T%d", j+1)
|
||||
}
|
||||
fmt.Fprintf(f, " any](t T.Tuple%d[", i)
|
||||
for j := 0; j < i; j++ {
|
||||
if j > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "%s", hkt(fmt.Sprintf("T%d", j+1)))
|
||||
}
|
||||
fmt.Fprintf(f, "]) %s {\n", hkt(tuple))
|
||||
fmt.Fprintf(f, " return A.SequenceTuple%d(\n", i)
|
||||
// map
|
||||
fmt.Fprintf(f, " Map[")
|
||||
if infix != "" {
|
||||
fmt.Fprintf(f, "%s, T1,", infix)
|
||||
} else {
|
||||
fmt.Fprintf(f, "T1,")
|
||||
}
|
||||
for j := 1; j < i; j++ {
|
||||
fmt.Fprintf(f, " func(T%d)", j+1)
|
||||
}
|
||||
fmt.Fprintf(f, " %s],\n", tuple)
|
||||
// applicatives
|
||||
for j := 1; j < i; j++ {
|
||||
fmt.Fprintf(f, " Ap[")
|
||||
for k := j + 1; k < i; k++ {
|
||||
if k > j+1 {
|
||||
fmt.Fprintf(f, " ")
|
||||
}
|
||||
fmt.Fprintf(f, "func(T%d)", k+1)
|
||||
}
|
||||
if j < i-1 {
|
||||
fmt.Fprintf(f, " ")
|
||||
}
|
||||
fmt.Fprintf(f, "%s", tuple)
|
||||
if infix != "" {
|
||||
fmt.Fprintf(f, ", %s", infix)
|
||||
}
|
||||
fmt.Fprintf(f, ", T%d],\n", j+1)
|
||||
}
|
||||
fmt.Fprintf(f, " t,\n")
|
||||
fmt.Fprintf(f, " )\n")
|
||||
fmt.Fprintf(f, "}\n")
|
||||
}
|
||||
}
|
||||
|
||||
func generateSequenceT1(
|
||||
hkt func(string) string,
|
||||
infix string) func(f *os.File, i int) {
|
||||
|
||||
return func(f *os.File, i int) {
|
||||
|
||||
tuple := tupleType(i)
|
||||
|
||||
fmt.Fprintf(f, "\n// SequenceT%d converts %d parameters of [%s] into a [%s].\n", i, i, hkt("T"), hkt(fmt.Sprintf("Tuple%d", i)))
|
||||
fmt.Fprintf(f, "func SequenceT%d[", i)
|
||||
if infix != "" {
|
||||
fmt.Fprintf(f, "%s", infix)
|
||||
}
|
||||
for j := 0; j < i; j++ {
|
||||
if infix != "" || j > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "T%d", j+1)
|
||||
}
|
||||
fmt.Fprintf(f, " any](")
|
||||
for j := 0; j < i; j++ {
|
||||
if j > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "t%d %s", j+1, hkt(fmt.Sprintf("T%d", j+1)))
|
||||
}
|
||||
fmt.Fprintf(f, ") %s {\n", hkt(tuple))
|
||||
fmt.Fprintf(f, " return A.SequenceT%d(\n", i)
|
||||
// map
|
||||
fmt.Fprintf(f, " Map[")
|
||||
if infix != "" {
|
||||
fmt.Fprintf(f, "%s, T1,", infix)
|
||||
} else {
|
||||
fmt.Fprintf(f, "T1,")
|
||||
}
|
||||
for j := 1; j < i; j++ {
|
||||
fmt.Fprintf(f, " func(T%d)", j+1)
|
||||
}
|
||||
fmt.Fprintf(f, " %s],\n", tuple)
|
||||
// applicatives
|
||||
for j := 1; j < i; j++ {
|
||||
fmt.Fprintf(f, " Ap[")
|
||||
for k := j + 1; k < i; k++ {
|
||||
if k > j+1 {
|
||||
fmt.Fprintf(f, " ")
|
||||
}
|
||||
fmt.Fprintf(f, "func(T%d)", k+1)
|
||||
}
|
||||
if j < i-1 {
|
||||
fmt.Fprintf(f, " ")
|
||||
}
|
||||
fmt.Fprintf(f, "%s", tuple)
|
||||
if infix != "" {
|
||||
fmt.Fprintf(f, ", %s", infix)
|
||||
}
|
||||
fmt.Fprintf(f, ", T%d],\n", j+1)
|
||||
}
|
||||
for j := 0; j < i; j++ {
|
||||
fmt.Fprintf(f, " t%d,\n", j+1)
|
||||
}
|
||||
fmt.Fprintf(f, " )\n")
|
||||
fmt.Fprintf(f, "}\n")
|
||||
}
|
||||
|
||||
}
|
195
cli/option.go
Normal file
195
cli/option.go
Normal file
@@ -0,0 +1,195 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
C "github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func optionHKT(typeA string) string {
|
||||
return fmt.Sprintf("Option[%s]", typeA)
|
||||
}
|
||||
|
||||
func generateOptionTraverseTuple(f *os.File, i int) {
|
||||
generateTraverseTuple1(optionHKT, "")(f, i)
|
||||
}
|
||||
|
||||
func generateOptionSequenceTuple(f *os.File, i int) {
|
||||
generateSequenceTuple1(optionHKT, "")(f, i)
|
||||
}
|
||||
|
||||
func generateOptionSequenceT(f *os.File, i int) {
|
||||
generateSequenceT1(optionHKT, "")(f, i)
|
||||
}
|
||||
|
||||
func generateOptionize(f *os.File, i int) {
|
||||
// Create the optionize version
|
||||
fmt.Fprintf(f, "\n// Optionize%d converts a function with %d parameters returning a tuple of a return value R and a boolean into a function with %d parameters returning an Option[R]\n", i, i, i)
|
||||
fmt.Fprintf(f, "func Optionize%d[F ~func(", i)
|
||||
for j := 0; j < i; j++ {
|
||||
if j > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "T%d", j)
|
||||
}
|
||||
fmt.Fprintf(f, ") (R, bool)")
|
||||
for j := 0; j < i; j++ {
|
||||
fmt.Fprintf(f, ", T%d", j)
|
||||
}
|
||||
fmt.Fprintf(f, ", R any](f F) func(")
|
||||
for j := 0; j < i; j++ {
|
||||
if j > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "T%d", j)
|
||||
}
|
||||
fmt.Fprintf(f, ") Option[R] {\n")
|
||||
fmt.Fprintf(f, " return func(")
|
||||
for j := 0; j < i; j++ {
|
||||
if j > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "t%d T%d", j, j)
|
||||
}
|
||||
fmt.Fprintf(f, ") Option[R] {\n")
|
||||
fmt.Fprintf(f, " return optionize(func() (R, bool) {\n")
|
||||
fmt.Fprintf(f, " return 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, "}")
|
||||
}
|
||||
|
||||
func generateUnoptionize(f *os.File, i int) {
|
||||
// Create the optionize version
|
||||
fmt.Fprintf(f, "\n// Unoptionize%d converts a function with %d parameters returning a tuple of a return value R and a boolean into a function with %d parameters returning an Option[R]\n", i, i, i)
|
||||
fmt.Fprintf(f, "func Unoptionize%d[F ~func(", i)
|
||||
for j := 0; j < i; j++ {
|
||||
if j > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "T%d", j)
|
||||
}
|
||||
fmt.Fprintf(f, ") Option[R]")
|
||||
for j := 0; j < i; j++ {
|
||||
fmt.Fprintf(f, ", T%d", j)
|
||||
}
|
||||
fmt.Fprintf(f, ", R any](f F) func(")
|
||||
for j := 0; j < i; j++ {
|
||||
if j > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "T%d", j)
|
||||
}
|
||||
fmt.Fprintf(f, ") (R, bool) {\n")
|
||||
fmt.Fprintf(f, " return func(")
|
||||
for j := 0; j < i; j++ {
|
||||
if j > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "t%d T%d", j, j)
|
||||
}
|
||||
fmt.Fprintf(f, ") (R, bool) {\n")
|
||||
fmt.Fprintf(f, " return Unwrap(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, "}")
|
||||
}
|
||||
|
||||
func generateOptionHelpers(filename string, count int) error {
|
||||
dir, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
absDir, err := filepath.Abs(dir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pkg := filepath.Base(absDir)
|
||||
f, err := os.Create(filepath.Clean(filename))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
// log
|
||||
log.Printf("Generating code in [%s] for package [%s] with [%d] repetitions ...", filename, pkg, count)
|
||||
|
||||
// some header
|
||||
fmt.Fprintln(f, "// Code generated by go generate; DO NOT EDIT.")
|
||||
fmt.Fprintln(f, "// This file was generated by robots at")
|
||||
fmt.Fprintf(f, "// %s\n", time.Now())
|
||||
|
||||
fmt.Fprintf(f, "package %s\n\n", pkg)
|
||||
|
||||
fmt.Fprintf(f, `
|
||||
import (
|
||||
A "github.com/ibm/fp-go/internal/apply"
|
||||
T "github.com/ibm/fp-go/tuple"
|
||||
)
|
||||
`)
|
||||
|
||||
// print out some helpers
|
||||
fmt.Fprintf(f, `// optionize converts a nullary function to an option
|
||||
func optionize[R any](f func() (R, bool)) Option[R] {
|
||||
if r, ok := f(); ok {
|
||||
return Some(r)
|
||||
}
|
||||
return None[R]()
|
||||
}
|
||||
`)
|
||||
|
||||
// zero level functions
|
||||
|
||||
// optionize
|
||||
generateOptionize(f, 0)
|
||||
// unoptionize
|
||||
generateUnoptionize(f, 0)
|
||||
|
||||
for i := 1; i <= count; i++ {
|
||||
// optionize
|
||||
generateOptionize(f, i)
|
||||
// unoptionize
|
||||
generateUnoptionize(f, i)
|
||||
// sequenceT
|
||||
generateOptionSequenceT(f, i)
|
||||
// sequenceTuple
|
||||
generateOptionSequenceTuple(f, i)
|
||||
// traverseTuple
|
||||
generateOptionTraverseTuple(f, i)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func OptionCommand() *C.Command {
|
||||
return &C.Command{
|
||||
Name: "option",
|
||||
Usage: "generate code for Option",
|
||||
Flags: []C.Flag{
|
||||
flagCount,
|
||||
flagFilename,
|
||||
},
|
||||
Action: func(ctx *C.Context) error {
|
||||
return generateOptionHelpers(
|
||||
ctx.String(keyFilename),
|
||||
ctx.Int(keyCount),
|
||||
)
|
||||
},
|
||||
}
|
||||
}
|
373
cli/pipe.go
Normal file
373
cli/pipe.go
Normal file
@@ -0,0 +1,373 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
C "github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
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)
|
||||
fmt.Fprintf(f, "func Variadic%d[", i)
|
||||
for j := 1; j <= i; j++ {
|
||||
if j > 1 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "T%d", j)
|
||||
}
|
||||
if i > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "V, R any](f func(")
|
||||
for j := 1; j <= i; j++ {
|
||||
if j > 1 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "T%d", j)
|
||||
}
|
||||
if i > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "[]V) R) func(")
|
||||
for j := 1; j <= i; j++ {
|
||||
if j > 1 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "T%d", j)
|
||||
}
|
||||
if i > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "...V) R {\n")
|
||||
fmt.Fprintf(f, " return func(")
|
||||
for j := 1; j <= i; j++ {
|
||||
if j > 1 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "t%d T%d", j, j)
|
||||
}
|
||||
if i > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "v ...V) R {\n")
|
||||
fmt.Fprintf(f, " return f(")
|
||||
for j := 1; j <= i; j++ {
|
||||
if j > 1 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "t%d", j)
|
||||
}
|
||||
if i > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "v)\n")
|
||||
fmt.Fprintf(f, " }\n")
|
||||
|
||||
fmt.Fprintf(f, "}")
|
||||
}
|
||||
|
||||
func generateUnvariadic(f *os.File, i int) {
|
||||
// Create the nullary version
|
||||
fmt.Fprintf(f, "\n// Unvariadic%d converts a function taking %d parameters and a final variadic argument into a function with %d parameters but a final slice argument\n", i, i, i)
|
||||
fmt.Fprintf(f, "func Unvariadic%d[", i)
|
||||
for j := 1; j <= i; j++ {
|
||||
if j > 1 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "T%d", j)
|
||||
}
|
||||
if i > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "V, R any](f func(")
|
||||
for j := 1; j <= i; j++ {
|
||||
if j > 1 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "T%d", j)
|
||||
}
|
||||
if i > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "...V) R) func(")
|
||||
for j := 1; j <= i; j++ {
|
||||
if j > 1 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "T%d", j)
|
||||
}
|
||||
if i > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "[]V) R {\n")
|
||||
fmt.Fprintf(f, " return func(")
|
||||
for j := 1; j <= i; j++ {
|
||||
if j > 1 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "t%d T%d", j, j)
|
||||
}
|
||||
if i > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "v []V) R {\n")
|
||||
fmt.Fprintf(f, " return f(")
|
||||
for j := 1; j <= i; j++ {
|
||||
if j > 1 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "t%d", j)
|
||||
}
|
||||
if i > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "v...)\n")
|
||||
fmt.Fprintf(f, " }\n")
|
||||
|
||||
fmt.Fprintf(f, "}")
|
||||
}
|
||||
|
||||
func generateNullary(f *os.File, i int) {
|
||||
// Create the nullary version
|
||||
fmt.Fprintf(f, "\n// Nullary%d creates a parameter less function from a parameter less function and %d functions. When executed the first parameter less function gets executed and then the result is piped through the remaining functions\n", i, i-1)
|
||||
fmt.Fprintf(f, "func Nullary%d[F1 ~func() T1", i)
|
||||
for j := 2; j <= i; j++ {
|
||||
fmt.Fprintf(f, ", F%d ~func(T%d) T%d", j, j-1, j)
|
||||
}
|
||||
for j := 1; j <= i; j++ {
|
||||
fmt.Fprintf(f, ", T%d", j)
|
||||
}
|
||||
fmt.Fprintf(f, " any](f1 F1")
|
||||
for j := 2; j <= i; j++ {
|
||||
fmt.Fprintf(f, ", f%d F%d", j, j)
|
||||
}
|
||||
fmt.Fprintf(f, ") func() T%d {\n", i)
|
||||
fmt.Fprintf(f, " return func() T%d {\n", i)
|
||||
fmt.Fprintf(f, " return Pipe%d(f1()", i-1)
|
||||
for j := 2; j <= i; j++ {
|
||||
fmt.Fprintf(f, ", f%d", j)
|
||||
}
|
||||
fmt.Fprintf(f, ")\n")
|
||||
fmt.Fprintln(f, " }")
|
||||
|
||||
fmt.Fprintln(f, "}")
|
||||
}
|
||||
|
||||
func generateFlow(f *os.File, i int) {
|
||||
// Create the flow version
|
||||
fmt.Fprintf(f, "\n// Flow%d creates a function that takes an initial value t0 and successively applies %d functions where the input of a function is the return value of the previous function\n// The final return value is the result of the last function application\n", i, i)
|
||||
fmt.Fprintf(f, "func Flow%d[", i)
|
||||
for j := 1; j <= i; j++ {
|
||||
if j > 1 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "F%d ~func(T%d) T%d", j, j-1, j)
|
||||
}
|
||||
for j := 0; j <= i; j++ {
|
||||
fmt.Fprintf(f, ", T%d", j)
|
||||
}
|
||||
fmt.Fprintf(f, " any](")
|
||||
for j := 1; j <= i; j++ {
|
||||
if j > 1 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "f%d F%d", j, j)
|
||||
}
|
||||
fmt.Fprintf(f, ") func(T0) T%d {\n", i)
|
||||
fmt.Fprintf(f, " return func(t0 T0) T%d {\n", i)
|
||||
fmt.Fprintf(f, " return Pipe%d(t0", i)
|
||||
for j := 1; j <= i; j++ {
|
||||
fmt.Fprintf(f, ", f%d", j)
|
||||
}
|
||||
fmt.Fprintf(f, ")\n")
|
||||
fmt.Fprintln(f, " }")
|
||||
|
||||
fmt.Fprintln(f, "}")
|
||||
|
||||
}
|
||||
|
||||
func generatePipe(f *os.File, i int) {
|
||||
// Create the pipe version
|
||||
fmt.Fprintf(f, "\n// Pipe%d takes an initial value t0 and successively applies %d functions where the input of a function is the return value of the previous function\n// The final return value is the result of the last function application\n", i, i)
|
||||
fmt.Fprintf(f, "func Pipe%d[", i)
|
||||
for j := 1; j <= i; j++ {
|
||||
if j > 1 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "F%d ~func(T%d) T%d", j, j-1, j)
|
||||
}
|
||||
if i > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
for j := 0; j <= i; j++ {
|
||||
if j > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "T%d", j)
|
||||
}
|
||||
|
||||
fmt.Fprintf(f, " any](t0 T0")
|
||||
for j := 1; j <= i; j++ {
|
||||
fmt.Fprintf(f, ", f%d F%d", j, j)
|
||||
}
|
||||
fmt.Fprintf(f, ") T%d {\n", i)
|
||||
for j := 1; j <= i; j++ {
|
||||
fmt.Fprintf(f, " t%d := f%d(t%d)\n", j, j, j-1)
|
||||
}
|
||||
fmt.Fprintf(f, " return t%d\n", i)
|
||||
fmt.Fprintln(f, "}")
|
||||
}
|
||||
|
||||
func recurseCurry(f *os.File, indent string, total, count int) {
|
||||
if count == 1 {
|
||||
fmt.Fprintf(f, "%sreturn func(t%d T%d) T%d {\n", indent, total-1, total-1, total)
|
||||
fmt.Fprintf(f, "%s return f(t0", indent)
|
||||
for i := 1; i < total; i++ {
|
||||
fmt.Fprintf(f, ", t%d", i)
|
||||
}
|
||||
fmt.Fprintf(f, ")\n")
|
||||
fmt.Fprintf(f, "%s}\n", indent)
|
||||
} else {
|
||||
fmt.Fprintf(f, "%sreturn", indent)
|
||||
for i := total - count + 1; i <= total; i++ {
|
||||
fmt.Fprintf(f, " func(t%d T%d)", i-1, i-1)
|
||||
}
|
||||
fmt.Fprintf(f, " T%d {\n", total)
|
||||
recurseCurry(f, fmt.Sprintf(" %s", indent), total, count-1)
|
||||
fmt.Fprintf(f, "%s}\n", indent)
|
||||
}
|
||||
}
|
||||
|
||||
func generateCurry(f *os.File, i int) {
|
||||
// Create the curry version
|
||||
fmt.Fprintf(f, "\n// Curry%d takes a function with %d parameters and returns a cascade of functions each taking only one parameter.\n// The inverse function is [Uncurry%d]\n", i, i, i)
|
||||
fmt.Fprintf(f, "func Curry%d[T0", i)
|
||||
for j := 1; j <= i; j++ {
|
||||
fmt.Fprintf(f, ", T%d", j)
|
||||
}
|
||||
fmt.Fprintf(f, " any](f func(T0")
|
||||
for j := 2; j <= i; j++ {
|
||||
fmt.Fprintf(f, ", T%d", j-1)
|
||||
}
|
||||
fmt.Fprintf(f, ") T%d) func(T0)", i)
|
||||
for j := 2; j <= i; j++ {
|
||||
fmt.Fprintf(f, " func(T%d)", j-1)
|
||||
}
|
||||
fmt.Fprintf(f, " T%d {\n", i)
|
||||
recurseCurry(f, " ", i, i)
|
||||
fmt.Fprintf(f, "}\n")
|
||||
}
|
||||
|
||||
func generateUncurry(f *os.File, i int) {
|
||||
// Create the uncurry version
|
||||
fmt.Fprintf(f, "\n// Uncurry%d takes a cascade of %d functions each taking only one parameter and returns a function with %d parameters .\n// The inverse function is [Curry%d]\n", i, i, i, i)
|
||||
fmt.Fprintf(f, "func Uncurry%d[T0", i)
|
||||
for j := 1; j <= i; j++ {
|
||||
fmt.Fprintf(f, ", T%d", j)
|
||||
}
|
||||
fmt.Fprintf(f, " any](f")
|
||||
for j := 1; j <= i; j++ {
|
||||
fmt.Fprintf(f, " func(T%d)", j-1)
|
||||
}
|
||||
fmt.Fprintf(f, " T%d) func(", i)
|
||||
for j := 1; j <= i; j++ {
|
||||
if j > 1 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "T%d", j-1)
|
||||
}
|
||||
fmt.Fprintf(f, ") T%d {\n", i)
|
||||
fmt.Fprintf(f, " return func(")
|
||||
for j := 1; j <= i; j++ {
|
||||
if j > 1 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "t%d T%d", j-1, j-1)
|
||||
}
|
||||
fmt.Fprintf(f, ") T%d {\n", i)
|
||||
fmt.Fprintf(f, " return f")
|
||||
for j := 1; j <= i; j++ {
|
||||
fmt.Fprintf(f, "(t%d)", j-1)
|
||||
}
|
||||
fmt.Fprintln(f)
|
||||
|
||||
fmt.Fprintf(f, " }\n")
|
||||
|
||||
fmt.Fprintf(f, "}\n")
|
||||
}
|
||||
|
||||
func generatePipeHelpers(filename string, count int) error {
|
||||
dir, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
absDir, err := filepath.Abs(dir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pkg := filepath.Base(absDir)
|
||||
f, err := os.Create(filepath.Clean(filename))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
// log
|
||||
log.Printf("Generating code in [%s] for package [%s] with [%d] repetitions ...", filename, pkg, count)
|
||||
|
||||
// some header
|
||||
fmt.Fprintln(f, "// Code generated by go generate; DO NOT EDIT.")
|
||||
fmt.Fprintln(f, "// This file was generated by robots at")
|
||||
fmt.Fprintf(f, "// %s\n", time.Now())
|
||||
|
||||
fmt.Fprintf(f, "package %s\n", pkg)
|
||||
|
||||
// pipe
|
||||
generatePipe(f, 0)
|
||||
// variadic
|
||||
generateVariadic(f, 0)
|
||||
// unvariadic
|
||||
generateUnvariadic(f, 0)
|
||||
|
||||
for i := 1; i <= count; i++ {
|
||||
|
||||
// pipe
|
||||
generatePipe(f, i)
|
||||
// flow
|
||||
generateFlow(f, i)
|
||||
// nullary
|
||||
generateNullary(f, i)
|
||||
// curry
|
||||
generateCurry(f, i)
|
||||
// uncurry
|
||||
generateUncurry(f, i)
|
||||
// variadic
|
||||
generateVariadic(f, i)
|
||||
// unvariadic
|
||||
generateUnvariadic(f, i)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func PipeCommand() *C.Command {
|
||||
return &C.Command{
|
||||
Name: "pipe",
|
||||
Usage: "generate code for pipe, flow, curry, etc",
|
||||
Description: "Code generation for pipe, flow, curry, etc",
|
||||
Flags: []C.Flag{
|
||||
flagCount,
|
||||
flagFilename,
|
||||
},
|
||||
Action: func(ctx *C.Context) error {
|
||||
return generatePipeHelpers(
|
||||
ctx.String(keyFilename),
|
||||
ctx.Int(keyCount),
|
||||
)
|
||||
},
|
||||
}
|
||||
}
|
163
cli/reader.go
Normal file
163
cli/reader.go
Normal file
@@ -0,0 +1,163 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
C "github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func generateReaderFrom(f, fg *os.File, i int) {
|
||||
// non generic version
|
||||
fmt.Fprintf(f, "\n// From%d converts a function with %d parameters returning a [R] into a function with %d parameters returning a [Reader[C, R]]\n// The first parameter is considered to be the context [C] of the reader\n", i, i+1, i)
|
||||
fmt.Fprintf(f, "func From%d[F ~func(C", i)
|
||||
for j := 0; j < i; j++ {
|
||||
fmt.Fprintf(f, ", T%d", j)
|
||||
}
|
||||
fmt.Fprintf(f, ") R")
|
||||
for j := 0; j < i; j++ {
|
||||
fmt.Fprintf(f, ", T%d", j)
|
||||
}
|
||||
fmt.Fprintf(f, ", C, R any](f F) func(")
|
||||
for j := 0; j < i; j++ {
|
||||
if j > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "T%d", j)
|
||||
}
|
||||
fmt.Fprintf(f, ") Reader[C, R] {\n")
|
||||
fmt.Fprintf(f, " return G.From%d[Reader[C, R]](f)\n", i)
|
||||
fmt.Fprintln(f, "}")
|
||||
|
||||
// generic version
|
||||
fmt.Fprintf(fg, "\n// From%d converts a function with %d parameters returning a [R] into a function with %d parameters returning a [GRA]\n// The first parameter is considered to be the context [C].\n", i, i+1, i)
|
||||
fmt.Fprintf(fg, "func From%d[GRA ~func(C) R, F ~func(C", i)
|
||||
for j := 0; j < i; j++ {
|
||||
fmt.Fprintf(fg, ", T%d", j)
|
||||
}
|
||||
fmt.Fprintf(fg, ") R")
|
||||
for j := 0; j < i; j++ {
|
||||
fmt.Fprintf(fg, ", T%d", j)
|
||||
}
|
||||
fmt.Fprintf(fg, ", C, R any](f F) func(")
|
||||
for j := 0; j < i; j++ {
|
||||
if j > 0 {
|
||||
fmt.Fprintf(fg, ", ")
|
||||
}
|
||||
fmt.Fprintf(fg, "T%d", j)
|
||||
}
|
||||
fmt.Fprintf(fg, ") GRA {\n")
|
||||
|
||||
fmt.Fprintf(fg, " return func(")
|
||||
for j := 0; j < i; j++ {
|
||||
if j > 0 {
|
||||
fmt.Fprintf(fg, ", ")
|
||||
}
|
||||
fmt.Fprintf(fg, "t%d T%d", j, j)
|
||||
}
|
||||
fmt.Fprintf(fg, ") GRA {\n")
|
||||
fmt.Fprintf(fg, " return MakeReader[GRA](func(r C) R {\n")
|
||||
fmt.Fprintf(fg, " return f(r")
|
||||
for j := 0; j < i; j++ {
|
||||
fmt.Fprintf(fg, ", t%d", j)
|
||||
}
|
||||
fmt.Fprintf(fg, ")\n")
|
||||
fmt.Fprintf(fg, " })\n")
|
||||
fmt.Fprintf(fg, " }\n")
|
||||
fmt.Fprintf(fg, "}\n")
|
||||
}
|
||||
|
||||
func generateReaderHelpers(filename string, count int) error {
|
||||
dir, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
absDir, err := filepath.Abs(dir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pkg := filepath.Base(absDir)
|
||||
f, err := os.Create(filepath.Clean(filename))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
// construct subdirectory
|
||||
genFilename := filepath.Join("generic", filename)
|
||||
err = os.MkdirAll("generic", os.ModePerm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fg, err := os.Create(filepath.Clean(genFilename))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer fg.Close()
|
||||
|
||||
// log
|
||||
log.Printf("Generating code in [%s] for package [%s] with [%d] repetitions ...", filename, pkg, count)
|
||||
|
||||
// some header
|
||||
fmt.Fprintln(f, "// Code generated by go generate; DO NOT EDIT.")
|
||||
fmt.Fprintln(f, "// This file was generated by robots at")
|
||||
fmt.Fprintf(f, "// %s\n", time.Now())
|
||||
|
||||
fmt.Fprintf(f, "package %s\n\n", pkg)
|
||||
|
||||
fmt.Fprintf(f, `
|
||||
import (
|
||||
G "github.com/ibm/fp-go/%s/generic"
|
||||
)
|
||||
`, pkg)
|
||||
|
||||
// some header
|
||||
fmt.Fprintln(fg, "// Code generated by go generate; DO NOT EDIT.")
|
||||
fmt.Fprintln(fg, "// This file was generated by robots at")
|
||||
fmt.Fprintf(fg, "// %s\n", time.Now())
|
||||
|
||||
fmt.Fprintf(fg, "package generic\n\n")
|
||||
|
||||
// from
|
||||
generateReaderFrom(f, fg, 0)
|
||||
|
||||
for i := 1; i <= count; i++ {
|
||||
// from
|
||||
generateReaderFrom(f, fg, i)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func ReaderCommand() *C.Command {
|
||||
return &C.Command{
|
||||
Name: "reader",
|
||||
Usage: "generate code for Reader",
|
||||
Flags: []C.Flag{
|
||||
flagCount,
|
||||
flagFilename,
|
||||
},
|
||||
Action: func(ctx *C.Context) error {
|
||||
return generateReaderHelpers(
|
||||
ctx.String(keyFilename),
|
||||
ctx.Int(keyCount),
|
||||
)
|
||||
},
|
||||
}
|
||||
}
|
211
cli/readerioeither.go
Normal file
211
cli/readerioeither.go
Normal file
@@ -0,0 +1,211 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
C "github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func generateReaderIOEitherFrom(f, fg *os.File, i int) {
|
||||
// non generic version
|
||||
fmt.Fprintf(f, "\n// From%d converts a function with %d parameters returning a tuple into a function with %d parameters returning a [ReaderIOEither[R]]\n// The first parameter is considered to be the context [C].\n", i, i+1, i)
|
||||
fmt.Fprintf(f, "func From%d[F ~func(C", i)
|
||||
for j := 0; j < i; j++ {
|
||||
fmt.Fprintf(f, ", T%d", j)
|
||||
}
|
||||
fmt.Fprintf(f, ") func() (R, error)")
|
||||
for j := 0; j < i; j++ {
|
||||
fmt.Fprintf(f, ", T%d", j)
|
||||
}
|
||||
fmt.Fprintf(f, ", C, R any](f F) func(")
|
||||
for j := 0; j < i; j++ {
|
||||
if j > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "T%d", j)
|
||||
}
|
||||
fmt.Fprintf(f, ") ReaderIOEither[C, error, R] {\n")
|
||||
fmt.Fprintf(f, " return G.From%d[ReaderIOEither[C, error, R]](f)\n", i)
|
||||
fmt.Fprintln(f, "}")
|
||||
|
||||
// generic version
|
||||
fmt.Fprintf(fg, "\n// From%d converts a function with %d parameters returning a tuple into a function with %d parameters returning a [GRA]\n// The first parameter is considerd to be the context [C].\n", i, i+1, i)
|
||||
fmt.Fprintf(fg, "func From%d[GRA ~func(C) GIOA, F ~func(C", i)
|
||||
for j := 0; j < i; j++ {
|
||||
fmt.Fprintf(fg, ", T%d", j)
|
||||
}
|
||||
fmt.Fprintf(fg, ") func() (R, error), GIOA ~func() E.Either[error, R]")
|
||||
for j := 0; j < i; j++ {
|
||||
fmt.Fprintf(fg, ", T%d", j)
|
||||
}
|
||||
fmt.Fprintf(fg, ", C, R any](f F) func(")
|
||||
for j := 0; j < i; j++ {
|
||||
if j > 0 {
|
||||
fmt.Fprintf(fg, ", ")
|
||||
}
|
||||
fmt.Fprintf(fg, "T%d", j)
|
||||
}
|
||||
fmt.Fprintf(fg, ") GRA {\n")
|
||||
|
||||
fmt.Fprintf(fg, " return RD.From%d[GRA](func(r C", i)
|
||||
for j := 0; j < i; j++ {
|
||||
fmt.Fprintf(fg, ", t%d T%d", j, j)
|
||||
}
|
||||
fmt.Fprintf(fg, ") GIOA {\n")
|
||||
fmt.Fprintf(fg, " return E.Eitherize0(f(r")
|
||||
for j := 0; j < i; j++ {
|
||||
fmt.Fprintf(fg, ", t%d", j)
|
||||
}
|
||||
fmt.Fprintf(fg, "))\n")
|
||||
fmt.Fprintf(fg, " })\n")
|
||||
fmt.Fprintf(fg, "}\n")
|
||||
}
|
||||
|
||||
func generateReaderIOEitherEitherize(f, fg *os.File, i int) {
|
||||
// non generic version
|
||||
fmt.Fprintf(f, "\n// Eitherize%d converts a function with %d parameters returning a tuple into a function with %d parameters returning a [ReaderIOEither[C, error, R]]\n// The first parameter is considered to be the context [C].\n", i, i+1, i)
|
||||
fmt.Fprintf(f, "func Eitherize%d[F ~func(C", i)
|
||||
for j := 0; j < i; j++ {
|
||||
fmt.Fprintf(f, ", T%d", j)
|
||||
}
|
||||
fmt.Fprintf(f, ") (R, error)")
|
||||
for j := 0; j < i; j++ {
|
||||
fmt.Fprintf(f, ", T%d", j)
|
||||
}
|
||||
fmt.Fprintf(f, ", C, R any](f F) func(")
|
||||
for j := 0; j < i; j++ {
|
||||
if j > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "T%d", j)
|
||||
}
|
||||
fmt.Fprintf(f, ") ReaderIOEither[C, error, R] {\n")
|
||||
fmt.Fprintf(f, " return G.Eitherize%d[ReaderIOEither[C, error, R]](f)\n", i)
|
||||
fmt.Fprintln(f, "}")
|
||||
|
||||
// generic version
|
||||
fmt.Fprintf(fg, "\n// Eitherize%d converts a function with %d parameters returning a tuple into a function with %d parameters returning a [GRA]\n// The first parameter is considered to be the context [C].\n", i, i, i)
|
||||
fmt.Fprintf(fg, "func Eitherize%d[GRA ~func(C) GIOA, F ~func(C", i)
|
||||
for j := 0; j < i; j++ {
|
||||
fmt.Fprintf(fg, ", T%d", j)
|
||||
}
|
||||
fmt.Fprintf(fg, ") (R, error), GIOA ~func() E.Either[error, R]")
|
||||
for j := 0; j < i; j++ {
|
||||
fmt.Fprintf(fg, ", T%d", j)
|
||||
}
|
||||
fmt.Fprintf(fg, ", C, R any](f F) func(")
|
||||
for j := 0; j < i; j++ {
|
||||
if j > 0 {
|
||||
fmt.Fprintf(fg, ", ")
|
||||
}
|
||||
fmt.Fprintf(fg, "T%d", j)
|
||||
}
|
||||
fmt.Fprintf(fg, ") GRA {\n")
|
||||
|
||||
fmt.Fprintf(fg, " return From%d[GRA](func(r C", i)
|
||||
for j := 0; j < i; j++ {
|
||||
fmt.Fprintf(fg, ", t%d T%d", j, j)
|
||||
}
|
||||
fmt.Fprintf(fg, ") func() (R, error) {\n")
|
||||
fmt.Fprintf(fg, " return func() (R, error) {\n")
|
||||
fmt.Fprintf(fg, " return f(r")
|
||||
for j := 0; j < i; j++ {
|
||||
fmt.Fprintf(fg, ", t%d", j)
|
||||
}
|
||||
fmt.Fprintf(fg, ")\n")
|
||||
fmt.Fprintf(fg, " }})\n")
|
||||
fmt.Fprintf(fg, "}\n")
|
||||
}
|
||||
|
||||
func generateReaderIOEitherHelpers(filename string, count int) error {
|
||||
dir, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
absDir, err := filepath.Abs(dir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pkg := filepath.Base(absDir)
|
||||
f, err := os.Create(filepath.Clean(filename))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
// construct subdirectory
|
||||
genFilename := filepath.Join("generic", filename)
|
||||
err = os.MkdirAll("generic", os.ModePerm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fg, err := os.Create(filepath.Clean(genFilename))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer fg.Close()
|
||||
|
||||
// log
|
||||
log.Printf("Generating code in [%s] for package [%s] with [%d] repetitions ...", filename, pkg, count)
|
||||
|
||||
// some header
|
||||
fmt.Fprintln(f, "// Code generated by go generate; DO NOT EDIT.")
|
||||
fmt.Fprintln(f, "// This file was generated by robots at")
|
||||
fmt.Fprintf(f, "// %s\n", time.Now())
|
||||
|
||||
fmt.Fprintf(f, "package %s\n\n", pkg)
|
||||
|
||||
fmt.Fprintf(f, `
|
||||
import (
|
||||
G "github.com/ibm/fp-go/%s/generic"
|
||||
)
|
||||
`, pkg)
|
||||
|
||||
// some header
|
||||
fmt.Fprintln(fg, "// Code generated by go generate; DO NOT EDIT.")
|
||||
fmt.Fprintln(fg, "// This file was generated by robots at")
|
||||
fmt.Fprintf(fg, "// %s\n", time.Now())
|
||||
|
||||
fmt.Fprintf(fg, "package generic\n\n")
|
||||
|
||||
fmt.Fprintf(fg, `
|
||||
import (
|
||||
E "github.com/ibm/fp-go/either"
|
||||
RD "github.com/ibm/fp-go/reader/generic"
|
||||
)
|
||||
`)
|
||||
|
||||
// from
|
||||
generateReaderIOEitherFrom(f, fg, 0)
|
||||
// eitherize
|
||||
generateReaderIOEitherEitherize(f, fg, 0)
|
||||
|
||||
for i := 1; i <= count; i++ {
|
||||
// from
|
||||
generateReaderIOEitherFrom(f, fg, i)
|
||||
// eitherize
|
||||
generateReaderIOEitherEitherize(f, fg, i)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func ReaderIOEitherCommand() *C.Command {
|
||||
return &C.Command{
|
||||
Name: "readerioeither",
|
||||
Usage: "generate code for ReaderIOEither",
|
||||
Flags: []C.Flag{
|
||||
flagCount,
|
||||
flagFilename,
|
||||
},
|
||||
Action: func(ctx *C.Context) error {
|
||||
return generateReaderIOEitherHelpers(
|
||||
ctx.String(keyFilename),
|
||||
ctx.Int(keyCount),
|
||||
)
|
||||
},
|
||||
}
|
||||
}
|
394
cli/tuple.go
Normal file
394
cli/tuple.go
Normal file
@@ -0,0 +1,394 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
C "github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func writeTupleType(f *os.File, symbol string, i int) {
|
||||
fmt.Fprintf(f, "Tuple%d[", i)
|
||||
for j := 1; j <= i; j++ {
|
||||
if j > 1 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "%s%d", symbol, j)
|
||||
}
|
||||
fmt.Fprintf(f, "]")
|
||||
}
|
||||
|
||||
func generateReplicate(f *os.File, i int) {
|
||||
// Create the optionize 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++ {
|
||||
if j > 1 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "T")
|
||||
}
|
||||
fmt.Fprintf(f, "] {\n")
|
||||
// execute the mapping
|
||||
fmt.Fprintf(f, " return MakeTuple%d(", i)
|
||||
for j := 1; j <= i; j++ {
|
||||
if j > 1 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "t")
|
||||
}
|
||||
fmt.Fprintf(f, ")\n")
|
||||
fmt.Fprintf(f, "}\n")
|
||||
}
|
||||
|
||||
func generateMap(f *os.File, i int) {
|
||||
// Create the optionize version
|
||||
fmt.Fprintf(f, "\n// Map%d maps each value of a [Tuple%d] via a mapping function\n", i, i)
|
||||
fmt.Fprintf(f, "func Map%d[", i)
|
||||
// function prototypes
|
||||
for j := 1; j <= i; j++ {
|
||||
if j > 1 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "F%d ~func(T%d) R%d", j, j, j)
|
||||
}
|
||||
for j := 1; j <= i; j++ {
|
||||
fmt.Fprintf(f, ", T%d, R%d", j, j)
|
||||
}
|
||||
fmt.Fprintf(f, " any](")
|
||||
for j := 1; j <= i; j++ {
|
||||
if j > 1 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "f%d F%d", j, j)
|
||||
}
|
||||
fmt.Fprintf(f, ") func(")
|
||||
writeTupleType(f, "T", i)
|
||||
fmt.Fprintf(f, ") ")
|
||||
writeTupleType(f, "R", i)
|
||||
fmt.Fprintf(f, " {\n")
|
||||
|
||||
fmt.Fprintf(f, " return func(t ")
|
||||
writeTupleType(f, "T", i)
|
||||
fmt.Fprintf(f, ") ")
|
||||
writeTupleType(f, "R", i)
|
||||
fmt.Fprintf(f, " {\n")
|
||||
|
||||
// execute the mapping
|
||||
fmt.Fprintf(f, " return MakeTuple%d(\n", i)
|
||||
for j := 1; j <= i; j++ {
|
||||
fmt.Fprintf(f, " f%d(t.F%d),\n", j, j)
|
||||
}
|
||||
fmt.Fprintf(f, " )\n")
|
||||
|
||||
fmt.Fprintf(f, " }\n")
|
||||
fmt.Fprintf(f, "}\n")
|
||||
}
|
||||
|
||||
func generateMonoid(f *os.File, i int) {
|
||||
// Create the optionize version
|
||||
fmt.Fprintf(f, "\n// Monoid%d creates a [Monoid] for a [Tuple%d] based on %d monoids for the contained types\n", i, i, i)
|
||||
fmt.Fprintf(f, "func Monoid%d[", i)
|
||||
for j := 1; j <= i; j++ {
|
||||
if j > 1 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "T%d", j)
|
||||
}
|
||||
fmt.Fprintf(f, " any](")
|
||||
for j := 1; j <= i; j++ {
|
||||
if j > 1 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "m%d M.Monoid[T%d]", j, j)
|
||||
}
|
||||
fmt.Fprintf(f, ") M.Monoid[")
|
||||
writeTupleType(f, "T", i)
|
||||
fmt.Fprintf(f, "] {\n")
|
||||
|
||||
fmt.Fprintf(f, " return M.MakeMonoid(func(l, r ")
|
||||
writeTupleType(f, "T", i)
|
||||
fmt.Fprintf(f, ") ")
|
||||
writeTupleType(f, "T", i)
|
||||
fmt.Fprintf(f, "{\n")
|
||||
|
||||
fmt.Fprintf(f, " return MakeTuple%d(", i)
|
||||
for j := 1; j <= i; j++ {
|
||||
if j > 1 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "m%d.Concat(l.F%d, r.F%d)", j, j, j)
|
||||
}
|
||||
fmt.Fprintf(f, ")\n")
|
||||
fmt.Fprintf(f, " }, MakeTuple%d(", i)
|
||||
for j := 1; j <= i; j++ {
|
||||
if j > 1 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "m%d.Empty()", j)
|
||||
}
|
||||
fmt.Fprintf(f, "))\n")
|
||||
|
||||
fmt.Fprintf(f, "}\n")
|
||||
}
|
||||
|
||||
func generateOrd(f *os.File, i int) {
|
||||
// Create the optionize version
|
||||
fmt.Fprintf(f, "\n// Ord%d creates n [Ord] for a [Tuple%d] based on %d [Ord]s for the contained types\n", i, i, i)
|
||||
fmt.Fprintf(f, "func Ord%d[", i)
|
||||
for j := 1; j <= i; j++ {
|
||||
if j > 1 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "T%d", j)
|
||||
}
|
||||
fmt.Fprintf(f, " any](")
|
||||
for j := 1; j <= i; j++ {
|
||||
if j > 1 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "o%d O.Ord[T%d]", j, j)
|
||||
}
|
||||
fmt.Fprintf(f, ") O.Ord[")
|
||||
writeTupleType(f, "T", i)
|
||||
fmt.Fprintf(f, "] {\n")
|
||||
|
||||
fmt.Fprintf(f, " return O.MakeOrd(func(l, r ")
|
||||
writeTupleType(f, "T", i)
|
||||
fmt.Fprintf(f, ") int {\n")
|
||||
|
||||
for j := 1; j <= i; j++ {
|
||||
fmt.Fprintf(f, " if c:= o%d.Compare(l.F%d, r.F%d); c != 0 {return c}\n", j, j, j)
|
||||
}
|
||||
fmt.Fprintf(f, " return 0\n")
|
||||
fmt.Fprintf(f, " }, func(l, r ")
|
||||
writeTupleType(f, "T", i)
|
||||
fmt.Fprintf(f, ") bool {\n")
|
||||
fmt.Fprintf(f, " return ")
|
||||
for j := 1; j <= i; j++ {
|
||||
if j > 1 {
|
||||
fmt.Fprintf(f, " && ")
|
||||
}
|
||||
fmt.Fprintf(f, "o%d.Equals(l.F%d, r.F%d)", j, j, j)
|
||||
}
|
||||
fmt.Fprintf(f, "\n")
|
||||
fmt.Fprintf(f, " })\n")
|
||||
|
||||
fmt.Fprintf(f, "}\n")
|
||||
}
|
||||
|
||||
func generateTupleType(f *os.File, i int) {
|
||||
// Create the optionize version
|
||||
fmt.Fprintf(f, "\n// Tuple%d is a struct that carries %d independently typed values\n", i, i)
|
||||
fmt.Fprintf(f, "type Tuple%d[", i)
|
||||
for j := 1; j <= i; j++ {
|
||||
if j > 1 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "T%d", j)
|
||||
}
|
||||
fmt.Fprintf(f, " any] struct {\n")
|
||||
for j := 1; j <= i; j++ {
|
||||
fmt.Fprintf(f, " F%d T%d\n", j, j)
|
||||
}
|
||||
fmt.Fprintf(f, "}\n")
|
||||
}
|
||||
|
||||
func generateMakeTupleType(f *os.File, i int) {
|
||||
// Create the optionize version
|
||||
fmt.Fprintf(f, "\n// MakeTuple%d is a function that converts its %d parameters into a [Tuple%d]\n", i, i, i)
|
||||
fmt.Fprintf(f, "func MakeTuple%d[", i)
|
||||
for j := 1; j <= i; j++ {
|
||||
if j > 1 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "T%d", j)
|
||||
}
|
||||
fmt.Fprintf(f, " any](")
|
||||
for j := 1; j <= i; j++ {
|
||||
if j > 1 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "t%d T%d", j, j)
|
||||
}
|
||||
fmt.Fprintf(f, ") ")
|
||||
writeTupleType(f, "T", i)
|
||||
fmt.Fprintf(f, " {\n")
|
||||
fmt.Fprintf(f, " return Tuple%d[", i)
|
||||
for j := 1; j <= i; j++ {
|
||||
if j > 1 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "T%d", j)
|
||||
}
|
||||
fmt.Fprintf(f, "]{")
|
||||
for j := 1; j <= i; j++ {
|
||||
if j > 1 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "t%d", j)
|
||||
}
|
||||
fmt.Fprintf(f, "}\n")
|
||||
fmt.Fprintf(f, "}\n")
|
||||
}
|
||||
|
||||
func generateUntupled(f *os.File, i int) {
|
||||
// Create the optionize version
|
||||
fmt.Fprintf(f, "\n// Untupled%d converts a function with a [Tuple%d] parameter into a function with %d parameters\n// The inverse function is [Tupled%d]\n", i, i, i, i)
|
||||
fmt.Fprintf(f, "func Untupled%d[F ~func(Tuple%d[", i, i)
|
||||
for j := 0; j < i; j++ {
|
||||
if j > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "T%d", j+1)
|
||||
}
|
||||
fmt.Fprintf(f, "]) R")
|
||||
for j := 0; j < i; j++ {
|
||||
fmt.Fprintf(f, ", T%d", j+1)
|
||||
}
|
||||
fmt.Fprintf(f, ", R any](f F) func(")
|
||||
for j := 0; j < i; j++ {
|
||||
if j > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "T%d", j+1)
|
||||
}
|
||||
fmt.Fprintf(f, ") R {\n")
|
||||
fmt.Fprintf(f, " return func(")
|
||||
for j := 0; j < i; j++ {
|
||||
if j > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "t%d T%d", j+1, j+1)
|
||||
}
|
||||
fmt.Fprintf(f, ") R {\n")
|
||||
fmt.Fprintf(f, " return f(MakeTuple%d(", i)
|
||||
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 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, "func Tupled%d[F ~func(", i)
|
||||
for j := 0; j < i; j++ {
|
||||
if j > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "T%d", j+1)
|
||||
}
|
||||
fmt.Fprintf(f, ") R")
|
||||
for j := 0; j < i; j++ {
|
||||
fmt.Fprintf(f, ", T%d", j+1)
|
||||
}
|
||||
fmt.Fprintf(f, ", R any](f F) func(Tuple%d[", i)
|
||||
for j := 0; j < i; j++ {
|
||||
if j > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "T%d", j+1)
|
||||
}
|
||||
fmt.Fprintf(f, "]) R {\n")
|
||||
fmt.Fprintf(f, " return func(t Tuple%d[", i)
|
||||
for j := 0; j < i; j++ {
|
||||
if j > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "T%d", j+1)
|
||||
}
|
||||
fmt.Fprintf(f, "]) R {\n")
|
||||
fmt.Fprintf(f, " return f(")
|
||||
for j := 0; j < i; j++ {
|
||||
if j > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "t.F%d", j+1)
|
||||
}
|
||||
fmt.Fprintf(f, ")\n")
|
||||
fmt.Fprintf(f, " }\n")
|
||||
fmt.Fprintln(f, "}")
|
||||
}
|
||||
|
||||
func generateTupleHelpers(filename string, count int) error {
|
||||
dir, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
absDir, err := filepath.Abs(dir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pkg := filepath.Base(absDir)
|
||||
f, err := os.Create(filepath.Clean(filename))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
// log
|
||||
log.Printf("Generating code in [%s] for package [%s] with [%d] repetitions ...", filename, pkg, count)
|
||||
|
||||
// some header
|
||||
fmt.Fprintln(f, "// Code generated by go generate; DO NOT EDIT.")
|
||||
fmt.Fprintln(f, "// This file was generated by robots at")
|
||||
fmt.Fprintf(f, "// %s\n", time.Now())
|
||||
|
||||
fmt.Fprintf(f, "package %s\n\n", pkg)
|
||||
|
||||
fmt.Fprintf(f, `
|
||||
import (
|
||||
M "github.com/ibm/fp-go/monoid"
|
||||
O "github.com/ibm/fp-go/ord"
|
||||
)
|
||||
`)
|
||||
|
||||
for i := 1; i <= count; i++ {
|
||||
// tuple type
|
||||
generateTupleType(f, i)
|
||||
}
|
||||
|
||||
for i := 1; i <= count; i++ {
|
||||
// tuple generator
|
||||
generateMakeTupleType(f, i)
|
||||
// tupled wrapper
|
||||
generateTupled(f, i)
|
||||
// untupled wrapper
|
||||
generateUntupled(f, i)
|
||||
// monoid
|
||||
generateMonoid(f, i)
|
||||
// generate order
|
||||
generateOrd(f, i)
|
||||
// generate map
|
||||
generateMap(f, i)
|
||||
// generate replicate
|
||||
generateReplicate(f, i)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func TupleCommand() *C.Command {
|
||||
return &C.Command{
|
||||
Name: "tuple",
|
||||
Usage: "generate code for Tuple",
|
||||
Flags: []C.Flag{
|
||||
flagCount,
|
||||
flagFilename,
|
||||
},
|
||||
Action: func(ctx *C.Context) error {
|
||||
return generateTupleHelpers(
|
||||
ctx.String(keyFilename),
|
||||
ctx.Int(keyCount),
|
||||
)
|
||||
},
|
||||
}
|
||||
}
|
21
constraints/constraints.go
Normal file
21
constraints/constraints.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package constraints
|
||||
|
||||
type Ordered interface {
|
||||
Integer | Float | ~string
|
||||
}
|
||||
|
||||
type Signed interface {
|
||||
~int | ~int8 | ~int16 | ~int32 | ~int64
|
||||
}
|
||||
|
||||
type Unsigned interface {
|
||||
~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr
|
||||
}
|
||||
|
||||
type Integer interface {
|
||||
Signed | Unsigned
|
||||
}
|
||||
|
||||
type Float interface {
|
||||
~float32 | ~float64
|
||||
}
|
15
context/doc.go
Normal file
15
context/doc.go
Normal file
@@ -0,0 +1,15 @@
|
||||
// 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 context
|
19
context/ioeither/generic/ioeither.go
Normal file
19
context/ioeither/generic/ioeither.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package generic
|
||||
|
||||
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
|
||||
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 ma()
|
||||
})
|
||||
}
|
13
context/ioeither/ioeither.go
Normal file
13
context/ioeither/ioeither.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package ioeither
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
G "github.com/ibm/fp-go/context/ioeither/generic"
|
||||
IOE "github.com/ibm/fp-go/ioeither"
|
||||
)
|
||||
|
||||
// withContext wraps an existing IOEither and performs a context check for cancellation before delegating
|
||||
func WithContext[A any](ctx context.Context, ma IOE.IOEither[error, A]) IOE.IOEither[error, A] {
|
||||
return G.WithContext(ctx, ma)
|
||||
}
|
30
context/reader/array.go
Normal file
30
context/reader/array.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 reader
|
||||
|
||||
import (
|
||||
R "github.com/ibm/fp-go/reader/generic"
|
||||
)
|
||||
|
||||
// TraverseArray transforms an array
|
||||
func TraverseArray[A, B any](f func(A) Reader[B]) func([]A) Reader[[]B] {
|
||||
return R.TraverseArray[Reader[B], Reader[[]B], []A](f)
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
53
context/reader/curry.go
Normal file
53
context/reader/curry.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 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)
|
||||
}
|
26
context/reader/from.go
Normal file
26
context/reader/from.go
Normal file
@@ -0,0 +1,26 @@
|
||||
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)
|
||||
}
|
43
context/reader/reader.go
Normal file
43
context/reader/reader.go
Normal file
@@ -0,0 +1,43 @@
|
||||
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)
|
||||
}
|
60
context/reader/reader_test.go
Normal file
60
context/reader/reader_test.go
Normal file
@@ -0,0 +1,60 @@
|
||||
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")
|
||||
|
||||
}
|
42
context/reader/sequence.go
Normal file
42
context/reader/sequence.go
Normal file
@@ -0,0 +1,42 @@
|
||||
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)
|
||||
}
|
11
context/reader/type.go
Normal file
11
context/reader/type.go
Normal file
@@ -0,0 +1,11 @@
|
||||
// Package reader implements a specialization of the Reader monad assuming a golang context as the context of the monad
|
||||
package reader
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
R "github.com/ibm/fp-go/reader"
|
||||
)
|
||||
|
||||
// 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]
|
15
context/readereither/array.go
Normal file
15
context/readereither/array.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package readereither
|
||||
|
||||
import (
|
||||
RE "github.com/ibm/fp-go/readereither/generic"
|
||||
)
|
||||
|
||||
// TraverseArray transforms an array
|
||||
func TraverseArray[A, B any](f func(A) ReaderEither[B]) func([]A) ReaderEither[[]B] {
|
||||
return RE.TraverseArray[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)
|
||||
}
|
17
context/readereither/cancel.go
Normal file
17
context/readereither/cancel.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package readereither
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
E "github.com/ibm/fp-go/either"
|
||||
)
|
||||
|
||||
// withContext wraps an existing ReaderEither and performs a context check for cancellation before deletating
|
||||
func WithContext[A any](ma ReaderEither[A]) ReaderEither[A] {
|
||||
return func(ctx context.Context) E.Either[error, A] {
|
||||
if err := context.Cause(ctx); err != nil {
|
||||
return E.Left[A](err)
|
||||
}
|
||||
return ma(ctx)
|
||||
}
|
||||
}
|
38
context/readereither/curry.go
Normal file
38
context/readereither/curry.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package readereither
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
RE "github.com/ibm/fp-go/readereither/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, error)) ReaderEither[A] {
|
||||
return RE.Curry0[ReaderEither[A]](f)
|
||||
}
|
||||
|
||||
func Curry1[T1, A any](f func(context.Context, T1) (A, error)) func(T1) ReaderEither[A] {
|
||||
return RE.Curry1[ReaderEither[A]](f)
|
||||
}
|
||||
|
||||
func Curry2[T1, T2, A any](f func(context.Context, T1, T2) (A, error)) func(T1) func(T2) ReaderEither[A] {
|
||||
return RE.Curry2[ReaderEither[A]](f)
|
||||
}
|
||||
|
||||
func Curry3[T1, T2, T3, A any](f func(context.Context, T1, T2, T3) (A, error)) func(T1) func(T2) func(T3) ReaderEither[A] {
|
||||
return RE.Curry3[ReaderEither[A]](f)
|
||||
}
|
||||
|
||||
func Uncurry1[T1, A any](f func(T1) ReaderEither[A]) func(context.Context, T1) (A, error) {
|
||||
return RE.Uncurry1(f)
|
||||
}
|
||||
|
||||
func Uncurry2[T1, T2, A any](f func(T1) func(T2) ReaderEither[A]) func(context.Context, T1, T2) (A, error) {
|
||||
return RE.Uncurry2(f)
|
||||
}
|
||||
|
||||
func Uncurry3[T1, T2, T3, A any](f func(T1) func(T2) func(T3) ReaderEither[A]) func(context.Context, T1, T2, T3) (A, error) {
|
||||
return RE.Uncurry3(f)
|
||||
}
|
26
context/readereither/exec/exec.go
Normal file
26
context/readereither/exec/exec.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package exec
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
RE "github.com/ibm/fp-go/context/readereither"
|
||||
E "github.com/ibm/fp-go/either"
|
||||
"github.com/ibm/fp-go/exec"
|
||||
F "github.com/ibm/fp-go/function"
|
||||
GE "github.com/ibm/fp-go/internal/exec"
|
||||
)
|
||||
|
||||
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 ReaderIOEither version of the command
|
||||
Command = F.Curry3(command)
|
||||
)
|
||||
|
||||
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)
|
||||
})
|
||||
}
|
||||
}
|
26
context/readereither/from.go
Normal file
26
context/readereither/from.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package readereither
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
RE "github.com/ibm/fp-go/readereither/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, error)) func() ReaderEither[A] {
|
||||
return RE.From0[ReaderEither[A]](f)
|
||||
}
|
||||
|
||||
func From1[T1, A any](f func(context.Context, T1) (A, error)) func(T1) ReaderEither[A] {
|
||||
return RE.From1[ReaderEither[A]](f)
|
||||
}
|
||||
|
||||
func From2[T1, T2, A any](f func(context.Context, T1, T2) (A, error)) func(T1, T2) ReaderEither[A] {
|
||||
return RE.From2[ReaderEither[A]](f)
|
||||
}
|
||||
|
||||
func From3[T1, T2, T3, A any](f func(context.Context, T1, T2, T3) (A, error)) func(T1, T2, T3) ReaderEither[A] {
|
||||
return RE.From3[ReaderEither[A]](f)
|
||||
}
|
106
context/readereither/reader.go
Normal file
106
context/readereither/reader.go
Normal file
@@ -0,0 +1,106 @@
|
||||
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"
|
||||
)
|
||||
|
||||
func MakeReaderEither[A any](f func(context.Context) ET.Either[error, A]) ReaderEither[A] {
|
||||
return RE.MakeReaderEither[ReaderEither[A]](f)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
func Map[A, B any](f func(A) B) func(ReaderEither[A]) ReaderEither[B] {
|
||||
return RE.Map[ReaderEither[A], ReaderEither[B]](f)
|
||||
}
|
||||
|
||||
func MonadChain[A, B any](ma ReaderEither[A], f func(A) ReaderEither[B]) ReaderEither[B] {
|
||||
return RE.MonadChain(ma, f)
|
||||
}
|
||||
|
||||
func Chain[A, B any](f func(A) ReaderEither[B]) func(ReaderEither[A]) ReaderEither[B] {
|
||||
return RE.Chain[ReaderEither[A]](f)
|
||||
}
|
||||
|
||||
func Of[A any](a A) ReaderEither[A] {
|
||||
return RE.Of[ReaderEither[A]](a)
|
||||
}
|
||||
|
||||
func MonadAp[A, B any](fab ReaderEither[func(A) B], fa ReaderEither[A]) ReaderEither[B] {
|
||||
return RE.MonadAp[ReaderEither[A], ReaderEither[B]](fab, fa)
|
||||
}
|
||||
|
||||
func Ap[A, B any](fa ReaderEither[A]) func(ReaderEither[func(A) B]) ReaderEither[B] {
|
||||
return RE.Ap[ReaderEither[A], ReaderEither[B], ReaderEither[func(A) B]](fa)
|
||||
}
|
||||
|
||||
func FromPredicate[A any](pred func(A) bool, onFalse func(A) error) func(A) ReaderEither[A] {
|
||||
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)
|
||||
}
|
||||
|
||||
func ChainEitherK[A, B any](f func(A) ET.Either[error, B]) func(ma ReaderEither[A]) ReaderEither[B] {
|
||||
return RE.ChainEitherK[ReaderEither[A], ReaderEither[B]](f)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
42
context/readereither/sequence.go
Normal file
42
context/readereither/sequence.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package readereither
|
||||
|
||||
import (
|
||||
RE "github.com/ibm/fp-go/readereither/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 ReaderEither[A]) ReaderEither[T.Tuple1[A]] {
|
||||
return RE.SequenceT1[
|
||||
ReaderEither[A],
|
||||
ReaderEither[T.Tuple1[A]],
|
||||
](a)
|
||||
}
|
||||
|
||||
func SequenceT2[A, B any](a ReaderEither[A], b ReaderEither[B]) ReaderEither[T.Tuple2[A, B]] {
|
||||
return RE.SequenceT2[
|
||||
ReaderEither[A],
|
||||
ReaderEither[B],
|
||||
ReaderEither[T.Tuple2[A, B]],
|
||||
](a, b)
|
||||
}
|
||||
|
||||
func SequenceT3[A, B, C any](a ReaderEither[A], b ReaderEither[B], c ReaderEither[C]) ReaderEither[T.Tuple3[A, B, C]] {
|
||||
return RE.SequenceT3[
|
||||
ReaderEither[A],
|
||||
ReaderEither[B],
|
||||
ReaderEither[C],
|
||||
ReaderEither[T.Tuple3[A, B, C]],
|
||||
](a, b, c)
|
||||
}
|
||||
|
||||
func SequenceT4[A, B, C, D any](a ReaderEither[A], b ReaderEither[B], c ReaderEither[C], d ReaderEither[D]) ReaderEither[T.Tuple4[A, B, C, D]] {
|
||||
return RE.SequenceT4[
|
||||
ReaderEither[A],
|
||||
ReaderEither[B],
|
||||
ReaderEither[C],
|
||||
ReaderEither[D],
|
||||
ReaderEither[T.Tuple4[A, B, C, D]],
|
||||
](a, b, c, d)
|
||||
}
|
11
context/readereither/type.go
Normal file
11
context/readereither/type.go
Normal file
@@ -0,0 +1,11 @@
|
||||
// Package readereither implements a specialization of the Reader monad assuming a golang context as the context of the monad and a standard golang error
|
||||
package readereither
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
RE "github.com/ibm/fp-go/readereither"
|
||||
)
|
||||
|
||||
// ReaderEither is a specialization of the Reader monad for the typical golang scenario
|
||||
type ReaderEither[A any] RE.ReaderEither[context.Context, error, A]
|
16
context/readerio/array.go
Normal file
16
context/readerio/array.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package readerio
|
||||
|
||||
import (
|
||||
IO "github.com/ibm/fp-go/io"
|
||||
R "github.com/ibm/fp-go/readerio/generic"
|
||||
)
|
||||
|
||||
// TraverseArray transforms an array
|
||||
func TraverseArray[A, B any](f func(A) ReaderIO[B]) func([]A) ReaderIO[[]B] {
|
||||
return R.TraverseArray[ReaderIO[B], ReaderIO[[]B], IO.IO[B], IO.IO[[]B], []A](f)
|
||||
}
|
||||
|
||||
// SequenceArray converts a homogeneous sequence of either into an either of sequence
|
||||
func SequenceArray[A any](ma []ReaderIO[A]) ReaderIO[[]A] {
|
||||
return R.SequenceArray[ReaderIO[A], ReaderIO[[]A]](ma)
|
||||
}
|
27
context/readerio/from.go
Normal file
27
context/readerio/from.go
Normal file
@@ -0,0 +1,27 @@
|
||||
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)
|
||||
}
|
44
context/readerio/reader.go
Normal file
44
context/readerio/reader.go
Normal file
@@ -0,0 +1,44 @@
|
||||
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)
|
||||
}
|
65
context/readerio/reader_test.go
Normal file
65
context/readerio/reader_test.go
Normal file
@@ -0,0 +1,65 @@
|
||||
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")
|
||||
|
||||
}
|
42
context/readerio/sequence.go
Normal file
42
context/readerio/sequence.go
Normal file
@@ -0,0 +1,42 @@
|
||||
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)
|
||||
}
|
11
context/readerio/type.go
Normal file
11
context/readerio/type.go
Normal file
@@ -0,0 +1,11 @@
|
||||
// Package readerio implements a specialization of the ReaderIO monad assuming a golang context as the context of the monad
|
||||
package readerio
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
R "github.com/ibm/fp-go/readerio"
|
||||
)
|
||||
|
||||
// ReaderIO is a specialization of the ReaderIO monad assuming a golang context as the context of the monad
|
||||
type ReaderIO[A any] R.ReaderIO[context.Context, A]
|
10
context/readerioeither/cancel.go
Normal file
10
context/readerioeither/cancel.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package readerioeither
|
||||
|
||||
import (
|
||||
G "github.com/ibm/fp-go/context/readerioeither/generic"
|
||||
)
|
||||
|
||||
// WithContext wraps an existing ReaderIOEither and performs a context check for cancellation before delegating
|
||||
func WithContext[A any](ma ReaderIOEither[A]) ReaderIOEither[A] {
|
||||
return G.WithContext(ma)
|
||||
}
|
3
context/readerioeither/doc.go
Normal file
3
context/readerioeither/doc.go
Normal file
@@ -0,0 +1,3 @@
|
||||
package readerioeither
|
||||
|
||||
//go:generate go run ../.. contextreaderioeither --count 10 --filename gen.go
|
14
context/readerioeither/eq.go
Normal file
14
context/readerioeither/eq.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package readerioeither
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
G "github.com/ibm/fp-go/context/readerioeither/generic"
|
||||
ET "github.com/ibm/fp-go/either"
|
||||
EQ "github.com/ibm/fp-go/eq"
|
||||
)
|
||||
|
||||
// Eq implements the equals predicate for values contained in the IOEither monad
|
||||
func Eq[A any](eq EQ.Eq[ET.Either[error, A]]) func(context.Context) EQ.Eq[ReaderIOEither[A]] {
|
||||
return G.Eq[ReaderIOEither[A]](eq)
|
||||
}
|
24
context/readerioeither/exec/exec.go
Normal file
24
context/readerioeither/exec/exec.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package exec
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
RIOE "github.com/ibm/fp-go/context/readerioeither"
|
||||
"github.com/ibm/fp-go/exec"
|
||||
F "github.com/ibm/fp-go/function"
|
||||
GE "github.com/ibm/fp-go/internal/exec"
|
||||
IOE "github.com/ibm/fp-go/ioeither"
|
||||
)
|
||||
|
||||
var (
|
||||
// Command executes a cancelable command
|
||||
Command = F.Curry3(command)
|
||||
)
|
||||
|
||||
func command(name string, args []string, in []byte) RIOE.ReaderIOEither[exec.CommandOutput] {
|
||||
return func(ctx context.Context) IOE.IOEither[error, exec.CommandOutput] {
|
||||
return IOE.TryCatchError(func() (exec.CommandOutput, error) {
|
||||
return GE.Exec(ctx, name, args, in)
|
||||
})
|
||||
}
|
||||
}
|
43
context/readerioeither/file/file.go
Normal file
43
context/readerioeither/file/file.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package file
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
RIOE "github.com/ibm/fp-go/context/readerioeither"
|
||||
ET "github.com/ibm/fp-go/either"
|
||||
F "github.com/ibm/fp-go/function"
|
||||
"github.com/ibm/fp-go/internal/file"
|
||||
IOE "github.com/ibm/fp-go/ioeither"
|
||||
)
|
||||
|
||||
var (
|
||||
openIOE = IOE.Eitherize1(os.Open)
|
||||
// Open opens a file for reading within the given context
|
||||
Open = F.Flow3(
|
||||
openIOE,
|
||||
RIOE.FromIOEither[*os.File],
|
||||
RIOE.WithContext[*os.File],
|
||||
)
|
||||
)
|
||||
|
||||
// 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()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// ReadFile reads a file in the scope of a context
|
||||
func ReadFile(path string) RIOE.ReaderIOEither[[]byte] {
|
||||
return RIOE.WithResource[*os.File, []byte](Open(path), Close[*os.File])(func(r *os.File) RIOE.ReaderIOEither[[]byte] {
|
||||
return func(ctx context.Context) IOE.IOEither[error, []byte] {
|
||||
return IOE.MakeIO(func() ET.Either[error, []byte] {
|
||||
return file.ReadAll(ctx, r)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
77
context/readerioeither/gen.go
Normal file
77
context/readerioeither/gen.go
Normal file
@@ -0,0 +1,77 @@
|
||||
// Code generated by go generate; DO NOT EDIT.
|
||||
// This file was generated by robots at
|
||||
// 2023-07-18 15:21:14.8906482 +0200 CEST m=+0.127356001
|
||||
package readerioeither
|
||||
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
G "github.com/ibm/fp-go/context/readerioeither/generic"
|
||||
)
|
||||
|
||||
// Eitherize0 converts a function with 0 parameters returning a tuple into a function with 0 parameters returning a [ReaderIOEither[R]]
|
||||
// The inverse function is [Uneitherize0]
|
||||
func Eitherize0[F ~func(context.Context) (R, error), R any](f F) func() ReaderIOEither[R] {
|
||||
return G.Eitherize0[ReaderIOEither[R]](f)
|
||||
}
|
||||
|
||||
// Eitherize1 converts a function with 1 parameters returning a tuple into a function with 1 parameters returning a [ReaderIOEither[R]]
|
||||
// The inverse function is [Uneitherize1]
|
||||
func Eitherize1[F ~func(context.Context, T0) (R, error), T0, R any](f F) func(T0) ReaderIOEither[R] {
|
||||
return G.Eitherize1[ReaderIOEither[R]](f)
|
||||
}
|
||||
|
||||
// Eitherize2 converts a function with 2 parameters returning a tuple into a function with 2 parameters returning a [ReaderIOEither[R]]
|
||||
// The inverse function is [Uneitherize2]
|
||||
func Eitherize2[F ~func(context.Context, T0, T1) (R, error), T0, T1, R any](f F) func(T0, T1) ReaderIOEither[R] {
|
||||
return G.Eitherize2[ReaderIOEither[R]](f)
|
||||
}
|
||||
|
||||
// Eitherize3 converts a function with 3 parameters returning a tuple into a function with 3 parameters returning a [ReaderIOEither[R]]
|
||||
// The inverse function is [Uneitherize3]
|
||||
func Eitherize3[F ~func(context.Context, T0, T1, T2) (R, error), T0, T1, T2, R any](f F) func(T0, T1, T2) ReaderIOEither[R] {
|
||||
return G.Eitherize3[ReaderIOEither[R]](f)
|
||||
}
|
||||
|
||||
// Eitherize4 converts a function with 4 parameters returning a tuple into a function with 4 parameters returning a [ReaderIOEither[R]]
|
||||
// The inverse function is [Uneitherize4]
|
||||
func Eitherize4[F ~func(context.Context, T0, T1, T2, T3) (R, error), T0, T1, T2, T3, R any](f F) func(T0, T1, T2, T3) ReaderIOEither[R] {
|
||||
return G.Eitherize4[ReaderIOEither[R]](f)
|
||||
}
|
||||
|
||||
// Eitherize5 converts a function with 5 parameters returning a tuple into a function with 5 parameters returning a [ReaderIOEither[R]]
|
||||
// The inverse function is [Uneitherize5]
|
||||
func Eitherize5[F ~func(context.Context, T0, T1, T2, T3, T4) (R, error), T0, T1, T2, T3, T4, R any](f F) func(T0, T1, T2, T3, T4) ReaderIOEither[R] {
|
||||
return G.Eitherize5[ReaderIOEither[R]](f)
|
||||
}
|
||||
|
||||
// Eitherize6 converts a function with 6 parameters returning a tuple into a function with 6 parameters returning a [ReaderIOEither[R]]
|
||||
// The inverse function is [Uneitherize6]
|
||||
func Eitherize6[F ~func(context.Context, 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) ReaderIOEither[R] {
|
||||
return G.Eitherize6[ReaderIOEither[R]](f)
|
||||
}
|
||||
|
||||
// Eitherize7 converts a function with 7 parameters returning a tuple into a function with 7 parameters returning a [ReaderIOEither[R]]
|
||||
// The inverse function is [Uneitherize7]
|
||||
func Eitherize7[F ~func(context.Context, 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) ReaderIOEither[R] {
|
||||
return G.Eitherize7[ReaderIOEither[R]](f)
|
||||
}
|
||||
|
||||
// Eitherize8 converts a function with 8 parameters returning a tuple into a function with 8 parameters returning a [ReaderIOEither[R]]
|
||||
// The inverse function is [Uneitherize8]
|
||||
func Eitherize8[F ~func(context.Context, 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) ReaderIOEither[R] {
|
||||
return G.Eitherize8[ReaderIOEither[R]](f)
|
||||
}
|
||||
|
||||
// Eitherize9 converts a function with 9 parameters returning a tuple into a function with 9 parameters returning a [ReaderIOEither[R]]
|
||||
// The inverse function is [Uneitherize9]
|
||||
func Eitherize9[F ~func(context.Context, 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) ReaderIOEither[R] {
|
||||
return G.Eitherize9[ReaderIOEither[R]](f)
|
||||
}
|
||||
|
||||
// Eitherize10 converts a function with 10 parameters returning a tuple into a function with 10 parameters returning a [ReaderIOEither[R]]
|
||||
// The inverse function is [Uneitherize10]
|
||||
func Eitherize10[F ~func(context.Context, 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) ReaderIOEither[R] {
|
||||
return G.Eitherize10[ReaderIOEither[R]](f)
|
||||
}
|
19
context/readerioeither/generic/cancel.go
Normal file
19
context/readerioeither/generic/cancel.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package generic
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
CIOE "github.com/ibm/fp-go/context/ioeither/generic"
|
||||
E "github.com/ibm/fp-go/either"
|
||||
IOE "github.com/ibm/fp-go/ioeither/generic"
|
||||
)
|
||||
|
||||
// withContext wraps an existing ReaderIOEither and performs a context check for cancellation before delegating
|
||||
func WithContext[GRA ~func(context.Context) GIOA, GIOA ~func() E.Either[error, A], A any](ma GRA) GRA {
|
||||
return func(ctx context.Context) GIOA {
|
||||
if err := context.Cause(ctx); err != nil {
|
||||
return IOE.Left[GIOA](err)
|
||||
}
|
||||
return CIOE.WithContext(ctx, ma(ctx))
|
||||
}
|
||||
}
|
15
context/readerioeither/generic/eq.go
Normal file
15
context/readerioeither/generic/eq.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package generic
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
E "github.com/ibm/fp-go/either"
|
||||
ET "github.com/ibm/fp-go/either"
|
||||
EQ "github.com/ibm/fp-go/eq"
|
||||
G "github.com/ibm/fp-go/readerioeither/generic"
|
||||
)
|
||||
|
||||
// Eq implements the equals predicate for values contained in the IOEither monad
|
||||
func Eq[GRA ~func(context.Context) GIOA, GIOA ~func() E.Either[error, A], A any](eq EQ.Eq[ET.Either[error, A]]) func(context.Context) EQ.Eq[GRA] {
|
||||
return G.Eq[GRA](eq)
|
||||
}
|
78
context/readerioeither/generic/gen.go
Normal file
78
context/readerioeither/generic/gen.go
Normal file
@@ -0,0 +1,78 @@
|
||||
// Code generated by go generate; DO NOT EDIT.
|
||||
// This file was generated by robots at
|
||||
// 2023-07-18 15:21:14.8906482 +0200 CEST m=+0.127356001
|
||||
package generic
|
||||
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
E "github.com/ibm/fp-go/either"
|
||||
RE "github.com/ibm/fp-go/readerioeither/generic"
|
||||
)
|
||||
|
||||
// Eitherize0 converts a function with 0 parameters returning a tuple into a function with 0 parameters returning a [GRA]
|
||||
// The inverse function is [Uneitherize0]
|
||||
func Eitherize0[GRA ~func(context.Context) GIOA, F ~func(context.Context) (R, error), GIOA ~func() E.Either[error, R], R any](f F) func() GRA {
|
||||
return RE.Eitherize0[GRA](f)
|
||||
}
|
||||
|
||||
// Eitherize1 converts a function with 1 parameters returning a tuple into a function with 1 parameters returning a [GRA]
|
||||
// The inverse function is [Uneitherize1]
|
||||
func Eitherize1[GRA ~func(context.Context) GIOA, F ~func(context.Context, T0) (R, error), GIOA ~func() E.Either[error, R], T0, R any](f F) func(T0) GRA {
|
||||
return RE.Eitherize1[GRA](f)
|
||||
}
|
||||
|
||||
// Eitherize2 converts a function with 2 parameters returning a tuple into a function with 2 parameters returning a [GRA]
|
||||
// The inverse function is [Uneitherize2]
|
||||
func Eitherize2[GRA ~func(context.Context) GIOA, F ~func(context.Context, T0, T1) (R, error), GIOA ~func() E.Either[error, R], T0, T1, R any](f F) func(T0, T1) GRA {
|
||||
return RE.Eitherize2[GRA](f)
|
||||
}
|
||||
|
||||
// Eitherize3 converts a function with 3 parameters returning a tuple into a function with 3 parameters returning a [GRA]
|
||||
// The inverse function is [Uneitherize3]
|
||||
func Eitherize3[GRA ~func(context.Context) GIOA, F ~func(context.Context, T0, T1, T2) (R, error), GIOA ~func() E.Either[error, R], T0, T1, T2, R any](f F) func(T0, T1, T2) GRA {
|
||||
return RE.Eitherize3[GRA](f)
|
||||
}
|
||||
|
||||
// Eitherize4 converts a function with 4 parameters returning a tuple into a function with 4 parameters returning a [GRA]
|
||||
// The inverse function is [Uneitherize4]
|
||||
func Eitherize4[GRA ~func(context.Context) GIOA, F ~func(context.Context, T0, T1, T2, T3) (R, error), GIOA ~func() E.Either[error, R], T0, T1, T2, T3, R any](f F) func(T0, T1, T2, T3) GRA {
|
||||
return RE.Eitherize4[GRA](f)
|
||||
}
|
||||
|
||||
// Eitherize5 converts a function with 5 parameters returning a tuple into a function with 5 parameters returning a [GRA]
|
||||
// The inverse function is [Uneitherize5]
|
||||
func Eitherize5[GRA ~func(context.Context) GIOA, F ~func(context.Context, T0, T1, T2, T3, T4) (R, error), GIOA ~func() E.Either[error, R], T0, T1, T2, T3, T4, R any](f F) func(T0, T1, T2, T3, T4) GRA {
|
||||
return RE.Eitherize5[GRA](f)
|
||||
}
|
||||
|
||||
// Eitherize6 converts a function with 6 parameters returning a tuple into a function with 6 parameters returning a [GRA]
|
||||
// The inverse function is [Uneitherize6]
|
||||
func Eitherize6[GRA ~func(context.Context) GIOA, F ~func(context.Context, T0, T1, T2, T3, T4, T5) (R, error), GIOA ~func() E.Either[error, R], T0, T1, T2, T3, T4, T5, R any](f F) func(T0, T1, T2, T3, T4, T5) GRA {
|
||||
return RE.Eitherize6[GRA](f)
|
||||
}
|
||||
|
||||
// Eitherize7 converts a function with 7 parameters returning a tuple into a function with 7 parameters returning a [GRA]
|
||||
// The inverse function is [Uneitherize7]
|
||||
func Eitherize7[GRA ~func(context.Context) GIOA, F ~func(context.Context, T0, T1, T2, T3, T4, T5, T6) (R, error), GIOA ~func() E.Either[error, R], T0, T1, T2, T3, T4, T5, T6, R any](f F) func(T0, T1, T2, T3, T4, T5, T6) GRA {
|
||||
return RE.Eitherize7[GRA](f)
|
||||
}
|
||||
|
||||
// Eitherize8 converts a function with 8 parameters returning a tuple into a function with 8 parameters returning a [GRA]
|
||||
// The inverse function is [Uneitherize8]
|
||||
func Eitherize8[GRA ~func(context.Context) GIOA, F ~func(context.Context, T0, T1, T2, T3, T4, T5, T6, T7) (R, error), GIOA ~func() E.Either[error, R], T0, T1, T2, T3, T4, T5, T6, T7, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7) GRA {
|
||||
return RE.Eitherize8[GRA](f)
|
||||
}
|
||||
|
||||
// Eitherize9 converts a function with 9 parameters returning a tuple into a function with 9 parameters returning a [GRA]
|
||||
// The inverse function is [Uneitherize9]
|
||||
func Eitherize9[GRA ~func(context.Context) GIOA, F ~func(context.Context, T0, T1, T2, T3, T4, T5, T6, T7, T8) (R, error), GIOA ~func() E.Either[error, R], T0, T1, T2, T3, T4, T5, T6, T7, T8, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7, T8) GRA {
|
||||
return RE.Eitherize9[GRA](f)
|
||||
}
|
||||
|
||||
// Eitherize10 converts a function with 10 parameters returning a tuple into a function with 10 parameters returning a [GRA]
|
||||
// The inverse function is [Uneitherize10]
|
||||
func Eitherize10[GRA ~func(context.Context) GIOA, F ~func(context.Context, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) (R, error), GIOA ~func() E.Either[error, R], T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) GRA {
|
||||
return RE.Eitherize10[GRA](f)
|
||||
}
|
464
context/readerioeither/generic/reader.go
Normal file
464
context/readerioeither/generic/reader.go
Normal file
@@ -0,0 +1,464 @@
|
||||
package generic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
E "github.com/ibm/fp-go/either"
|
||||
ER "github.com/ibm/fp-go/errors"
|
||||
F "github.com/ibm/fp-go/function"
|
||||
IO "github.com/ibm/fp-go/io/generic"
|
||||
IOE "github.com/ibm/fp-go/ioeither/generic"
|
||||
O "github.com/ibm/fp-go/option"
|
||||
RIE "github.com/ibm/fp-go/readerioeither/generic"
|
||||
)
|
||||
|
||||
func FromEither[
|
||||
GRA ~func(context.Context) GIOA,
|
||||
GIOA ~func() E.Either[error, A],
|
||||
A any](e E.Either[error, A]) GRA {
|
||||
return RIE.FromEither[GRA](e)
|
||||
}
|
||||
|
||||
func RightReader[
|
||||
GRA ~func(context.Context) GIOA,
|
||||
GR ~func(context.Context) A,
|
||||
GIOA ~func() E.Either[error, A],
|
||||
A any](r GR) GRA {
|
||||
return RIE.RightReader[GR, GRA](r)
|
||||
}
|
||||
|
||||
func LeftReader[
|
||||
GRA ~func(context.Context) GIOA,
|
||||
GR ~func(context.Context) error,
|
||||
GIOA ~func() E.Either[error, A],
|
||||
A any](l GR) GRA {
|
||||
return RIE.LeftReader[GR, GRA](l)
|
||||
}
|
||||
|
||||
func Left[
|
||||
GRA ~func(context.Context) GIOA,
|
||||
GIOA ~func() E.Either[error, A],
|
||||
A any](l error) GRA {
|
||||
return RIE.Left[GRA](l)
|
||||
}
|
||||
|
||||
func Right[
|
||||
GRA ~func(context.Context) GIOA,
|
||||
GIOA ~func() E.Either[error, A],
|
||||
A any](r A) GRA {
|
||||
return RIE.Right[GRA](r)
|
||||
}
|
||||
|
||||
func FromReader[
|
||||
GRA ~func(context.Context) GIOA,
|
||||
GR ~func(context.Context) A,
|
||||
GIOA ~func() E.Either[error, A],
|
||||
A any](r GR) GRA {
|
||||
return RIE.FromReader[GR, GRA](r)
|
||||
}
|
||||
|
||||
func MonadMap[
|
||||
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, f func(A) B) GRB {
|
||||
return RIE.MonadMap[GRA, GRB](fa, f)
|
||||
}
|
||||
|
||||
func Map[
|
||||
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](f func(A) B) func(GRA) GRB {
|
||||
return RIE.Map[GRA, GRB](f)
|
||||
}
|
||||
|
||||
func MonadChain[
|
||||
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](ma GRA, f func(A) GRB) GRB {
|
||||
return RIE.MonadChain(ma, f)
|
||||
}
|
||||
|
||||
func Chain[
|
||||
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](f func(A) GRB) func(GRA) GRB {
|
||||
return RIE.Chain[GRA](f)
|
||||
}
|
||||
|
||||
func MonadChainFirst[
|
||||
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](ma GRA, f func(A) GRB) GRA {
|
||||
return RIE.MonadChainFirst(ma, f)
|
||||
}
|
||||
|
||||
func ChainFirst[
|
||||
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](f func(A) GRB) func(GRA) GRA {
|
||||
return RIE.ChainFirst[GRA](f)
|
||||
}
|
||||
|
||||
func Of[
|
||||
GRA ~func(context.Context) GIOA,
|
||||
GIOA ~func() E.Either[error, A],
|
||||
|
||||
A any](a A) GRA {
|
||||
return RIE.Of[GRA](a)
|
||||
}
|
||||
|
||||
// withCancelCauseFunc wraps an IOEither such that in case of an error the cancel function is invoked
|
||||
func withCancelCauseFunc[
|
||||
GIOA ~func() E.Either[error, A],
|
||||
A any](cancel context.CancelCauseFunc, ma GIOA) GIOA {
|
||||
return F.Pipe3(
|
||||
ma,
|
||||
IOE.Swap[GIOA, func() E.Either[A, error]],
|
||||
IOE.ChainFirstIOK[func() E.Either[A, error], func() any](func(err error) func() any {
|
||||
return IO.MakeIO[func() any](func() any {
|
||||
cancel(err)
|
||||
return nil
|
||||
})
|
||||
}),
|
||||
IOE.Swap[func() E.Either[A, error], GIOA],
|
||||
)
|
||||
}
|
||||
|
||||
// MonadAp implements the `Ap` function for a reader with context. It creates a sub-context that will
|
||||
// be canceled if any of the input operations errors out or
|
||||
func MonadAp[
|
||||
GRB ~func(context.Context) GIOB,
|
||||
GRA ~func(context.Context) GIOA,
|
||||
GRAB ~func(context.Context) GIOAB,
|
||||
|
||||
GIOA ~func() E.Either[error, A],
|
||||
GIOB ~func() E.Either[error, B],
|
||||
GIOAB ~func() E.Either[error, func(A) B],
|
||||
|
||||
A, B any](fab GRAB, fa GRA) GRB {
|
||||
// context sensitive input
|
||||
cfab := WithContext(fab)
|
||||
cfa := WithContext(fa)
|
||||
|
||||
return func(ctx context.Context) GIOB {
|
||||
// quick check for cancellation
|
||||
if err := context.Cause(ctx); err != nil {
|
||||
return IOE.Left[GIOB](err)
|
||||
}
|
||||
|
||||
return func() E.Either[error, B] {
|
||||
// quick check for cancellation
|
||||
if err := context.Cause(ctx); err != nil {
|
||||
return E.Left[B](err)
|
||||
}
|
||||
|
||||
// create sub-contexts for fa and fab, so they can cancel one other
|
||||
ctxSub, cancelSub := context.WithCancelCause(ctx)
|
||||
defer cancelSub(nil) // cancel has to be called in all paths
|
||||
|
||||
fabIOE := withCancelCauseFunc(cancelSub, cfab(ctxSub))
|
||||
faIOE := withCancelCauseFunc(cancelSub, cfa(ctxSub))
|
||||
|
||||
return IOE.MonadAp[GIOA, GIOB, GIOAB](fabIOE, faIOE)()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Ap[
|
||||
GRB ~func(context.Context) GIOB,
|
||||
GRAB ~func(context.Context) GIOAB,
|
||||
GRA ~func(context.Context) GIOA,
|
||||
|
||||
GIOA ~func() E.Either[error, A],
|
||||
GIOB ~func() E.Either[error, B],
|
||||
GIOAB ~func() E.Either[error, func(A) B],
|
||||
|
||||
A, B any](fa GRA) func(GRAB) GRB {
|
||||
return F.Bind2nd(MonadAp[GRB, GRA, GRAB], fa)
|
||||
}
|
||||
|
||||
func FromPredicate[
|
||||
GRA ~func(context.Context) GIOA,
|
||||
GIOA ~func() E.Either[error, A],
|
||||
|
||||
A any](pred func(A) bool, onFalse func(A) error) func(A) GRA {
|
||||
return RIE.FromPredicate[GRA](pred, onFalse)
|
||||
}
|
||||
|
||||
func Fold[
|
||||
GRB ~func(context.Context) GIOB,
|
||||
GRA ~func(context.Context) GIOA,
|
||||
|
||||
GIOA ~func() E.Either[error, A],
|
||||
GIOB ~func() B,
|
||||
|
||||
A, B any](onLeft func(error) GRB, onRight func(A) GRB) func(GRA) GRB {
|
||||
return RIE.Fold[GRB, GRA](onLeft, onRight)
|
||||
}
|
||||
|
||||
func GetOrElse[
|
||||
GRB ~func(context.Context) GIOB,
|
||||
GRA ~func(context.Context) GIOA,
|
||||
GIOA ~func() E.Either[error, A],
|
||||
|
||||
GIOB ~func() A,
|
||||
|
||||
A any](onLeft func(error) GRB) func(GRA) GRB {
|
||||
return RIE.GetOrElse[GRB, GRA](onLeft)
|
||||
}
|
||||
|
||||
func OrElse[
|
||||
GRA ~func(context.Context) GIOA,
|
||||
GIOA ~func() E.Either[error, A],
|
||||
|
||||
A any](onLeft func(error) GRA) func(GRA) GRA {
|
||||
return RIE.OrElse[GRA](onLeft)
|
||||
}
|
||||
|
||||
func OrLeft[
|
||||
GRA ~func(context.Context) GIOA,
|
||||
GRB ~func(context.Context) GIOB,
|
||||
GIOA ~func() E.Either[error, A],
|
||||
|
||||
GIOB ~func() error,
|
||||
|
||||
A any](onLeft func(error) GRB) func(GRA) GRA {
|
||||
return RIE.OrLeft[GRA, GRB, GRA](onLeft)
|
||||
}
|
||||
|
||||
func Ask[
|
||||
GRA ~func(context.Context) GIOA,
|
||||
GIOA ~func() E.Either[error, context.Context],
|
||||
|
||||
]() GRA {
|
||||
return RIE.Ask[GRA]()
|
||||
}
|
||||
|
||||
func Asks[
|
||||
GRA ~func(context.Context) GIOA,
|
||||
GRB ~func(context.Context) A,
|
||||
GIOA ~func() E.Either[error, A],
|
||||
|
||||
A any](r GRB) GRA {
|
||||
|
||||
return RIE.Asks[GRB, GRA](r)
|
||||
}
|
||||
|
||||
func MonadChainEitherK[
|
||||
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](ma GRA, f func(A) E.Either[error, B]) GRB {
|
||||
return RIE.MonadChainEitherK[GRA, GRB](ma, f)
|
||||
}
|
||||
|
||||
func ChainEitherK[
|
||||
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](f func(A) E.Either[error, B]) func(ma GRA) GRB {
|
||||
return RIE.ChainEitherK[GRA, GRB](f)
|
||||
}
|
||||
|
||||
func MonadChainFirstEitherK[
|
||||
GRA ~func(context.Context) GIOA,
|
||||
GIOA ~func() E.Either[error, A],
|
||||
|
||||
A, B any](ma GRA, f func(A) E.Either[error, B]) GRA {
|
||||
return RIE.MonadChainFirstEitherK[GRA](ma, f)
|
||||
}
|
||||
|
||||
func ChainFirstEitherK[
|
||||
GRA ~func(context.Context) GIOA,
|
||||
GIOA ~func() E.Either[error, A],
|
||||
|
||||
A, B any](f func(A) E.Either[error, B]) func(ma GRA) GRA {
|
||||
return RIE.ChainFirstEitherK[GRA](f)
|
||||
}
|
||||
|
||||
func ChainOptionK[
|
||||
GRA ~func(context.Context) GIOA,
|
||||
GRB ~func(context.Context) GIOB,
|
||||
|
||||
GIOB ~func() E.Either[error, B],
|
||||
|
||||
GIOA ~func() E.Either[error, A],
|
||||
|
||||
A, B any](onNone func() error) func(func(A) O.Option[B]) func(GRA) GRB {
|
||||
return RIE.ChainOptionK[GRA, GRB](onNone)
|
||||
}
|
||||
|
||||
func FromIOEither[
|
||||
GRA ~func(context.Context) GIOA,
|
||||
GIOA ~func() E.Either[error, A],
|
||||
|
||||
A any](t GIOA) GRA {
|
||||
return RIE.FromIOEither[GRA](t)
|
||||
}
|
||||
|
||||
func FromIO[
|
||||
GRA ~func(context.Context) GIOA,
|
||||
GIOA ~func() E.Either[error, A],
|
||||
|
||||
GIOB ~func() A,
|
||||
|
||||
A any](t GIOB) GRA {
|
||||
return RIE.FromIO[GRA](t)
|
||||
}
|
||||
|
||||
// Never returns a 'ReaderIOEither' that never returns, except if its context gets canceled
|
||||
func Never[
|
||||
GRA ~func(context.Context) GIOA,
|
||||
GIOA ~func() E.Either[error, A],
|
||||
|
||||
A any]() GRA {
|
||||
return func(ctx context.Context) GIOA {
|
||||
return IOE.MakeIO(func() E.Either[error, A] {
|
||||
<-ctx.Done()
|
||||
return E.Left[A](context.Cause(ctx))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func MonadChainIOK[
|
||||
GRB ~func(context.Context) GIOB,
|
||||
GRA ~func(context.Context) GIOA,
|
||||
GIOA ~func() E.Either[error, A],
|
||||
|
||||
GIOB ~func() E.Either[error, B],
|
||||
|
||||
GIO ~func() B,
|
||||
|
||||
A, B any](ma GRA, f func(A) GIO) GRB {
|
||||
return RIE.MonadChainIOK[GRA, GRB](ma, f)
|
||||
}
|
||||
|
||||
func ChainIOK[
|
||||
GRB ~func(context.Context) GIOB,
|
||||
GRA ~func(context.Context) GIOA,
|
||||
GIOA ~func() E.Either[error, A],
|
||||
|
||||
GIOB ~func() E.Either[error, B],
|
||||
|
||||
GIO ~func() B,
|
||||
|
||||
A, B any](f func(A) GIO) func(ma GRA) GRB {
|
||||
return RIE.ChainIOK[GRA, GRB](f)
|
||||
}
|
||||
|
||||
func MonadChainFirstIOK[
|
||||
GRA ~func(context.Context) GIOA,
|
||||
GIOA ~func() E.Either[error, A],
|
||||
|
||||
GIO ~func() B,
|
||||
|
||||
A, B any](ma GRA, f func(A) GIO) GRA {
|
||||
return RIE.MonadChainFirstIOK[GRA](ma, f)
|
||||
}
|
||||
|
||||
func ChainFirstIOK[
|
||||
GRA ~func(context.Context) GIOA,
|
||||
GIOA ~func() E.Either[error, A],
|
||||
|
||||
GIO ~func() B,
|
||||
|
||||
A, B any](f func(A) GIO) func(ma GRA) GRA {
|
||||
return RIE.ChainFirstIOK[GRA](f)
|
||||
}
|
||||
|
||||
func ChainIOEitherK[
|
||||
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](f func(A) GIOB) func(ma GRA) GRB {
|
||||
return RIE.ChainIOEitherK[GRA, GRB](f)
|
||||
}
|
||||
|
||||
// Delay creates an operation that passes in the value after some delay
|
||||
func Delay[
|
||||
GRA ~func(context.Context) GIOA,
|
||||
GIOA ~func() E.Either[error, A],
|
||||
|
||||
A any](delay time.Duration) func(ma GRA) GRA {
|
||||
return func(ma GRA) GRA {
|
||||
return func(ctx context.Context) GIOA {
|
||||
return IOE.MakeIO(func() E.Either[error, A] {
|
||||
// manage the timeout
|
||||
timeoutCtx, cancelTimeout := context.WithTimeout(ctx, delay)
|
||||
defer cancelTimeout()
|
||||
// whatever comes first
|
||||
select {
|
||||
case <-timeoutCtx.Done():
|
||||
return ma(ctx)()
|
||||
case <-ctx.Done():
|
||||
return E.Left[A](context.Cause(ctx))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Timer will return the current time after an initial delay
|
||||
func Timer[
|
||||
GRA ~func(context.Context) GIOA,
|
||||
GIOA ~func() E.Either[error, time.Time],
|
||||
|
||||
](delay time.Duration) GRA {
|
||||
return F.Pipe2(
|
||||
IO.Now[func() time.Time](),
|
||||
FromIO[GRA, GIOA, func() time.Time],
|
||||
Delay[GRA](delay),
|
||||
)
|
||||
}
|
||||
|
||||
// Defer creates an IO by creating a brand new IO via a generator function, each time
|
||||
func Defer[
|
||||
GRA ~func(context.Context) GIOA,
|
||||
GIOA ~func() E.Either[error, A],
|
||||
|
||||
A any](gen func() GRA) GRA {
|
||||
return RIE.Defer[GRA](gen)
|
||||
}
|
||||
|
||||
// TryCatch wraps a reader returning a tuple as an error into ReaderIOEither
|
||||
func TryCatch[
|
||||
GRA ~func(context.Context) GIOA,
|
||||
GIOA ~func() E.Either[error, A],
|
||||
|
||||
A any](f func(context.Context) func() (A, error)) GRA {
|
||||
return RIE.TryCatch[GRA](f, ER.IdentityError)
|
||||
}
|
25
context/readerioeither/generic/resource.go
Normal file
25
context/readerioeither/generic/resource.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package generic
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
E "github.com/ibm/fp-go/either"
|
||||
F "github.com/ibm/fp-go/function"
|
||||
RIE "github.com/ibm/fp-go/readerioeither/generic"
|
||||
)
|
||||
|
||||
// WithResource constructs a function that creates a resource, then operates on it and then releases the resource
|
||||
func WithResource[
|
||||
GRA ~func(context.Context) GIOA,
|
||||
GRR ~func(context.Context) GIOR,
|
||||
GRANY ~func(context.Context) GIOANY,
|
||||
GIOR ~func() E.Either[error, R],
|
||||
GIOA ~func() E.Either[error, A],
|
||||
GIOANY ~func() E.Either[error, ANY],
|
||||
R, A, ANY any](onCreate GRR, onRelease func(R) GRANY) func(func(R) GRA) GRA {
|
||||
// wraps the callback functions with a context check
|
||||
return F.Flow2(
|
||||
F.Bind2nd(F.Flow2[func(R) GRA, func(GRA) GRA, R, GRA, GRA], WithContext[GRA]),
|
||||
RIE.WithResource[GRA](WithContext(onCreate), onRelease),
|
||||
)
|
||||
}
|
85
context/readerioeither/generic/sequence.go
Normal file
85
context/readerioeither/generic/sequence.go
Normal file
@@ -0,0 +1,85 @@
|
||||
package generic
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
E "github.com/ibm/fp-go/either"
|
||||
RE "github.com/ibm/fp-go/readerioeither/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[
|
||||
GRT ~func(context.Context) GIOT,
|
||||
GRA ~func(context.Context) GIOA,
|
||||
|
||||
GIOA ~func() E.Either[error, A],
|
||||
GIOT ~func() E.Either[error, T.Tuple1[A]],
|
||||
|
||||
A any](a GRA) GRT {
|
||||
return RE.SequenceT1[
|
||||
GRA,
|
||||
GRT,
|
||||
](a)
|
||||
}
|
||||
|
||||
func SequenceT2[
|
||||
GRT ~func(context.Context) GIOT,
|
||||
GRA ~func(context.Context) GIOA,
|
||||
GRB ~func(context.Context) GIOB,
|
||||
|
||||
GIOA ~func() E.Either[error, A],
|
||||
GIOB ~func() E.Either[error, B],
|
||||
GIOT ~func() E.Either[error, T.Tuple2[A, B]],
|
||||
|
||||
A, B any](a GRA, b GRB) GRT {
|
||||
return RE.SequenceT2[
|
||||
GRA,
|
||||
GRB,
|
||||
GRT,
|
||||
](a, b)
|
||||
}
|
||||
|
||||
func SequenceT3[
|
||||
GRT ~func(context.Context) GIOT,
|
||||
GRA ~func(context.Context) GIOA,
|
||||
GRB ~func(context.Context) GIOB,
|
||||
GRC ~func(context.Context) GIOC,
|
||||
|
||||
GIOA ~func() E.Either[error, A],
|
||||
GIOB ~func() E.Either[error, B],
|
||||
GIOC ~func() E.Either[error, C],
|
||||
GIOT ~func() E.Either[error, T.Tuple3[A, B, C]],
|
||||
|
||||
A, B, C any](a GRA, b GRB, c GRC) GRT {
|
||||
return RE.SequenceT3[
|
||||
GRA,
|
||||
GRB,
|
||||
GRC,
|
||||
GRT,
|
||||
](a, b, c)
|
||||
}
|
||||
|
||||
func SequenceT4[
|
||||
GRT ~func(context.Context) GIOT,
|
||||
GRA ~func(context.Context) GIOA,
|
||||
GRB ~func(context.Context) GIOB,
|
||||
GRC ~func(context.Context) GIOC,
|
||||
GRD ~func(context.Context) GIOD,
|
||||
|
||||
GIOA ~func() E.Either[error, A],
|
||||
GIOB ~func() E.Either[error, B],
|
||||
GIOC ~func() E.Either[error, C],
|
||||
GIOD ~func() E.Either[error, D],
|
||||
GIOT ~func() E.Either[error, T.Tuple4[A, B, C, D]],
|
||||
|
||||
A, B, C, D any](a GRA, b GRB, c GRC, d GRD) GRT {
|
||||
return RE.SequenceT4[
|
||||
GRA,
|
||||
GRB,
|
||||
GRC,
|
||||
GRD,
|
||||
GRT,
|
||||
](a, b, c, d)
|
||||
}
|
57
context/readerioeither/generic/traverse.go
Normal file
57
context/readerioeither/generic/traverse.go
Normal file
@@ -0,0 +1,57 @@
|
||||
package generic
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
E "github.com/ibm/fp-go/either"
|
||||
RE "github.com/ibm/fp-go/readerioeither/generic"
|
||||
)
|
||||
|
||||
// TraverseArray transforms an array
|
||||
func TraverseArray[
|
||||
AS ~[]A,
|
||||
GRBS ~func(context.Context) GIOBS,
|
||||
GRB ~func(context.Context) GIOB,
|
||||
GIOBS ~func() E.Either[error, BS],
|
||||
GIOB ~func() E.Either[error, B],
|
||||
BS ~[]B,
|
||||
A, B any](f func(A) GRB) func(AS) GRBS {
|
||||
return RE.TraverseArray[GRB, GRBS, GIOB, GIOBS, AS](f)
|
||||
}
|
||||
|
||||
// SequenceArray converts a homogeneous sequence of either into an either of sequence
|
||||
func SequenceArray[
|
||||
AS ~[]A,
|
||||
GAS ~[]GRA,
|
||||
GRAS ~func(context.Context) GIOAS,
|
||||
GRA ~func(context.Context) GIOA,
|
||||
GIOAS ~func() E.Either[error, AS],
|
||||
GIOA ~func() E.Either[error, A],
|
||||
A any](ma GAS) GRAS {
|
||||
return RE.SequenceArray[GRA, GRAS](ma)
|
||||
}
|
||||
|
||||
// TraverseRecord transforms a record
|
||||
func TraverseRecord[K comparable,
|
||||
AS ~map[K]A,
|
||||
GRBS ~func(context.Context) GIOBS,
|
||||
GRB ~func(context.Context) GIOB,
|
||||
GIOBS ~func() E.Either[error, BS],
|
||||
GIOB ~func() E.Either[error, B],
|
||||
BS ~map[K]B,
|
||||
|
||||
A, B any](f func(A) GRB) func(AS) GRBS {
|
||||
return RE.TraverseRecord[GRB, GRBS, GIOB, GIOBS, AS](f)
|
||||
}
|
||||
|
||||
// SequenceRecord converts a homogeneous sequence of either into an either of sequence
|
||||
func SequenceRecord[K comparable,
|
||||
AS ~map[K]A,
|
||||
GAS ~map[K]GRA,
|
||||
GRAS ~func(context.Context) GIOAS,
|
||||
GRA ~func(context.Context) GIOA,
|
||||
GIOAS ~func() E.Either[error, AS],
|
||||
GIOA ~func() E.Either[error, A],
|
||||
A any](ma GAS) GRAS {
|
||||
return RE.SequenceRecord[GRA, GRAS](ma)
|
||||
}
|
103
context/readerioeither/http/request.go
Normal file
103
context/readerioeither/http/request.go
Normal file
@@ -0,0 +1,103 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
B "github.com/ibm/fp-go/bytes"
|
||||
RIOE "github.com/ibm/fp-go/context/readerioeither"
|
||||
F "github.com/ibm/fp-go/function"
|
||||
H "github.com/ibm/fp-go/http"
|
||||
IOE "github.com/ibm/fp-go/ioeither"
|
||||
IOEF "github.com/ibm/fp-go/ioeither/file"
|
||||
J "github.com/ibm/fp-go/json"
|
||||
T "github.com/ibm/fp-go/tuple"
|
||||
)
|
||||
|
||||
type (
|
||||
// Requester is a reader that constructs a request
|
||||
Requester = RIOE.ReaderIOEither[*http.Request]
|
||||
|
||||
Client interface {
|
||||
// Do can send an HTTP request considering a context
|
||||
Do(Requester) RIOE.ReaderIOEither[*http.Response]
|
||||
}
|
||||
|
||||
client struct {
|
||||
delegate *http.Client
|
||||
doIOE func(*http.Request) IOE.IOEither[error, *http.Response]
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
// MakeRequest is an eitherized version of [http.NewRequestWithContext]
|
||||
MakeRequest = RIOE.Eitherize3(http.NewRequestWithContext)
|
||||
makeRequest = F.Bind13of3(MakeRequest)
|
||||
|
||||
// specialize
|
||||
MakeGetRequest = makeRequest("GET", nil)
|
||||
)
|
||||
|
||||
func (client client) Do(req Requester) RIOE.ReaderIOEither[*http.Response] {
|
||||
return F.Pipe1(
|
||||
req,
|
||||
RIOE.ChainIOEitherK(client.doIOE),
|
||||
)
|
||||
}
|
||||
|
||||
// MakeClient creates an HTTP client proxy
|
||||
func MakeClient(httpClient *http.Client) Client {
|
||||
return client{delegate: httpClient, doIOE: IOE.Eitherize1(httpClient.Do)}
|
||||
}
|
||||
|
||||
// ReadFullResponse sends a request, reads the response as a byte array and represents the result as a tuple
|
||||
func ReadFullResponse(client Client) func(Requester) RIOE.ReaderIOEither[H.FullResponse] {
|
||||
return func(req Requester) RIOE.ReaderIOEither[H.FullResponse] {
|
||||
return F.Flow3(
|
||||
client.Do(req),
|
||||
IOE.ChainEitherK(H.ValidateResponse),
|
||||
IOE.Chain(func(resp *http.Response) IOE.IOEither[error, H.FullResponse] {
|
||||
return F.Pipe1(
|
||||
F.Pipe3(
|
||||
resp,
|
||||
H.GetBody,
|
||||
IOE.Of[error, io.ReadCloser],
|
||||
IOEF.ReadAll[io.ReadCloser],
|
||||
),
|
||||
IOE.Map[error](F.Bind1st(T.MakeTuple2[*http.Response, []byte], resp)),
|
||||
)
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// ReadAll sends a request and reads the response as bytes
|
||||
func ReadAll(client Client) func(Requester) RIOE.ReaderIOEither[[]byte] {
|
||||
return F.Flow2(
|
||||
ReadFullResponse(client),
|
||||
RIOE.Map(H.Body),
|
||||
)
|
||||
}
|
||||
|
||||
// ReadText sends a request, reads the response and represents the response as a text string
|
||||
func ReadText(client Client) func(Requester) RIOE.ReaderIOEither[string] {
|
||||
return F.Flow2(
|
||||
ReadAll(client),
|
||||
RIOE.Map(B.ToString),
|
||||
)
|
||||
}
|
||||
|
||||
// ReadJson sends a request, reads the response and parses the response as JSON
|
||||
func ReadJson[A any](client Client) func(Requester) RIOE.ReaderIOEither[A] {
|
||||
return F.Flow3(
|
||||
ReadFullResponse(client),
|
||||
RIOE.ChainFirstEitherK(F.Flow2(
|
||||
H.Response,
|
||||
H.ValidateJsonResponse,
|
||||
)),
|
||||
RIOE.ChainEitherK(F.Flow2(
|
||||
H.Body,
|
||||
J.Unmarshal[A],
|
||||
)),
|
||||
)
|
||||
}
|
31
context/readerioeither/http/request_test.go
Normal file
31
context/readerioeither/http/request_test.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
H "net/http"
|
||||
)
|
||||
|
||||
type PostItem struct {
|
||||
UserId uint `json:"userId"`
|
||||
Id uint `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Body string `json:"body"`
|
||||
}
|
||||
|
||||
func TestSendSingleRequest(t *testing.T) {
|
||||
|
||||
client := MakeClient(H.DefaultClient)
|
||||
|
||||
req1 := MakeGetRequest("https://jsonplaceholder.typicode.com/posts/1")
|
||||
|
||||
readItem := ReadJson[PostItem](client)
|
||||
|
||||
resp1 := readItem(req1)
|
||||
|
||||
resE := resp1(context.Background())()
|
||||
|
||||
fmt.Println(resE)
|
||||
}
|
177
context/readerioeither/reader.go
Normal file
177
context/readerioeither/reader.go
Normal file
@@ -0,0 +1,177 @@
|
||||
package readerioeither
|
||||
|
||||
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"
|
||||
O "github.com/ibm/fp-go/option"
|
||||
)
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
func Map[A, B any](f func(A) B) func(ReaderIOEither[A]) ReaderIOEither[B] {
|
||||
return G.Map[ReaderIOEither[A], ReaderIOEither[B]](f)
|
||||
}
|
||||
|
||||
func MonadChain[A, B any](ma ReaderIOEither[A], f func(A) ReaderIOEither[B]) ReaderIOEither[B] {
|
||||
return G.MonadChain(ma, f)
|
||||
}
|
||||
|
||||
func Chain[A, B any](f func(A) ReaderIOEither[B]) func(ReaderIOEither[A]) ReaderIOEither[B] {
|
||||
return G.Chain[ReaderIOEither[A]](f)
|
||||
}
|
||||
|
||||
func MonadChainFirst[A, B any](ma ReaderIOEither[A], f func(A) ReaderIOEither[B]) ReaderIOEither[A] {
|
||||
return G.MonadChainFirst(ma, f)
|
||||
}
|
||||
|
||||
func ChainFirst[A, B any](f func(A) ReaderIOEither[B]) func(ReaderIOEither[A]) ReaderIOEither[A] {
|
||||
return G.ChainFirst[ReaderIOEither[A]](f)
|
||||
}
|
||||
|
||||
func Of[A any](a A) ReaderIOEither[A] {
|
||||
return G.Of[ReaderIOEither[A]](a)
|
||||
}
|
||||
|
||||
// MonadAp implements the `Ap` function for a reader with context. It creates a sub-context that will
|
||||
// be canceled if any of the input operations errors out or
|
||||
func MonadAp[B, A any](fab ReaderIOEither[func(A) B], fa ReaderIOEither[A]) ReaderIOEither[B] {
|
||||
return G.MonadAp[ReaderIOEither[B]](fab, fa)
|
||||
}
|
||||
|
||||
func Ap[B, A any](fa ReaderIOEither[A]) func(ReaderIOEither[func(A) B]) ReaderIOEither[B] {
|
||||
return G.Ap[ReaderIOEither[B], ReaderIOEither[func(A) B]](fa)
|
||||
}
|
||||
|
||||
func FromPredicate[A any](pred func(A) bool, onFalse func(A) error) func(A) ReaderIOEither[A] {
|
||||
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)
|
||||
}
|
||||
|
||||
func ChainEitherK[A, B any](f func(A) ET.Either[error, B]) func(ma ReaderIOEither[A]) ReaderIOEither[B] {
|
||||
return G.ChainEitherK[ReaderIOEither[A], ReaderIOEither[B]](f)
|
||||
}
|
||||
|
||||
func MonadChainFirstEitherK[A, B any](ma ReaderIOEither[A], f func(A) ET.Either[error, B]) ReaderIOEither[A] {
|
||||
return G.MonadChainFirstEitherK[ReaderIOEither[A]](ma, f)
|
||||
}
|
||||
|
||||
func ChainFirstEitherK[A, B any](f func(A) ET.Either[error, B]) func(ma ReaderIOEither[A]) ReaderIOEither[A] {
|
||||
return G.ChainFirstEitherK[ReaderIOEither[A]](f)
|
||||
}
|
||||
|
||||
func ChainOptionK[A, B any](onNone func() error) func(func(A) O.Option[B]) func(ReaderIOEither[A]) ReaderIOEither[B] {
|
||||
return G.ChainOptionK[ReaderIOEither[A], ReaderIOEither[B]](onNone)
|
||||
}
|
||||
|
||||
func FromIOEither[A any](t IOE.IOEither[error, A]) ReaderIOEither[A] {
|
||||
return G.FromIOEither[ReaderIOEither[A]](t)
|
||||
}
|
||||
|
||||
func FromIO[A any](t IO.IO[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]]()
|
||||
}
|
||||
|
||||
func MonadChainIOK[A, B any](ma ReaderIOEither[A], f func(A) IO.IO[B]) ReaderIOEither[B] {
|
||||
return G.MonadChainIOK[ReaderIOEither[B], ReaderIOEither[A]](ma, f)
|
||||
}
|
||||
|
||||
func ChainIOK[A, B any](f func(A) IO.IO[B]) func(ma ReaderIOEither[A]) ReaderIOEither[B] {
|
||||
return G.ChainIOK[ReaderIOEither[B], ReaderIOEither[A]](f)
|
||||
}
|
||||
|
||||
func MonadChainFirstIOK[A, B any](ma ReaderIOEither[A], f func(A) IO.IO[B]) ReaderIOEither[A] {
|
||||
return G.MonadChainFirstIOK[ReaderIOEither[A]](ma, f)
|
||||
}
|
||||
|
||||
func ChainFirstIOK[A, B any](f func(A) IO.IO[B]) func(ma ReaderIOEither[A]) ReaderIOEither[A] {
|
||||
return G.ChainFirstIOK[ReaderIOEither[A]](f)
|
||||
}
|
||||
|
||||
func ChainIOEitherK[A, B any](f func(A) IOE.IOEither[error, B]) func(ma ReaderIOEither[A]) ReaderIOEither[B] {
|
||||
return G.ChainIOEitherK[ReaderIOEither[A], ReaderIOEither[B]](f)
|
||||
}
|
||||
|
||||
// Delay creates an operation that passes in the value after some delay
|
||||
func Delay[A any](delay time.Duration) func(ma ReaderIOEither[A]) ReaderIOEither[A] {
|
||||
return G.Delay[ReaderIOEither[A]](delay)
|
||||
}
|
||||
|
||||
// Timer will return the current time after an initial delay
|
||||
func Timer(delay time.Duration) ReaderIOEither[time.Time] {
|
||||
return G.Timer[ReaderIOEither[time.Time]](delay)
|
||||
}
|
||||
|
||||
// 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] {
|
||||
return G.Defer[ReaderIOEither[A]](gen)
|
||||
}
|
||||
|
||||
// TryCatch wraps a reader returning a tuple as an error into ReaderIOEither
|
||||
func TryCatch[A any](f func(context.Context) func() (A, error)) ReaderIOEither[A] {
|
||||
return G.TryCatch[ReaderIOEither[A]](f)
|
||||
}
|
149
context/readerioeither/reader_test.go
Normal file
149
context/readerioeither/reader_test.go
Normal file
@@ -0,0 +1,149 @@
|
||||
package readerioeither
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
E "github.com/ibm/fp-go/either"
|
||||
F "github.com/ibm/fp-go/function"
|
||||
"github.com/ibm/fp-go/internal/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestInnerContextCancelSemantics(t *testing.T) {
|
||||
// start with a simple context
|
||||
outer := context.Background()
|
||||
|
||||
parent, parentCancel := context.WithCancel(outer)
|
||||
defer parentCancel()
|
||||
|
||||
inner, innerCancel := context.WithCancel(parent)
|
||||
defer innerCancel()
|
||||
|
||||
assert.NoError(t, parent.Err())
|
||||
assert.NoError(t, inner.Err())
|
||||
|
||||
innerCancel()
|
||||
|
||||
assert.NoError(t, parent.Err())
|
||||
assert.Error(t, inner.Err())
|
||||
|
||||
}
|
||||
|
||||
func TestOuterContextCancelSemantics(t *testing.T) {
|
||||
// start with a simple context
|
||||
outer := context.Background()
|
||||
|
||||
parent, outerCancel := context.WithCancel(outer)
|
||||
defer outerCancel()
|
||||
|
||||
inner, innerCancel := context.WithCancel(parent)
|
||||
defer innerCancel()
|
||||
|
||||
assert.NoError(t, parent.Err())
|
||||
assert.NoError(t, inner.Err())
|
||||
|
||||
outerCancel()
|
||||
|
||||
assert.Error(t, parent.Err())
|
||||
assert.Error(t, inner.Err())
|
||||
|
||||
}
|
||||
|
||||
func TestOuterAndInnerContextCancelSemantics(t *testing.T) {
|
||||
// start with a simple context
|
||||
outer := context.Background()
|
||||
|
||||
parent, outerCancel := context.WithCancel(outer)
|
||||
defer outerCancel()
|
||||
|
||||
inner, innerCancel := context.WithCancel(parent)
|
||||
defer innerCancel()
|
||||
|
||||
assert.NoError(t, parent.Err())
|
||||
assert.NoError(t, inner.Err())
|
||||
|
||||
outerCancel()
|
||||
innerCancel()
|
||||
|
||||
assert.Error(t, parent.Err())
|
||||
assert.Error(t, inner.Err())
|
||||
|
||||
outerCancel()
|
||||
innerCancel()
|
||||
|
||||
assert.Error(t, parent.Err())
|
||||
assert.Error(t, inner.Err())
|
||||
}
|
||||
|
||||
func TestCancelCauseSemantics(t *testing.T) {
|
||||
// start with a simple context
|
||||
outer := context.Background()
|
||||
|
||||
parent, outerCancel := context.WithCancelCause(outer)
|
||||
defer outerCancel(nil)
|
||||
|
||||
inner := context.WithValue(parent, "key", "value")
|
||||
|
||||
assert.NoError(t, parent.Err())
|
||||
assert.NoError(t, inner.Err())
|
||||
|
||||
err := fmt.Errorf("test error")
|
||||
|
||||
outerCancel(err)
|
||||
|
||||
assert.Error(t, parent.Err())
|
||||
assert.Error(t, inner.Err())
|
||||
|
||||
assert.Equal(t, err, context.Cause(parent))
|
||||
assert.Equal(t, err, context.Cause(inner))
|
||||
}
|
||||
|
||||
func TestTimer(t *testing.T) {
|
||||
delta := 3 * time.Second
|
||||
timer := Timer(delta)
|
||||
ctx := context.Background()
|
||||
|
||||
t0 := time.Now()
|
||||
res := timer(ctx)()
|
||||
t1 := time.Now()
|
||||
|
||||
assert.WithinDuration(t, t0.Add(delta), t1, time.Second)
|
||||
assert.True(t, E.IsRight(res))
|
||||
}
|
||||
|
||||
func TestCanceledApply(t *testing.T) {
|
||||
// our error
|
||||
err := fmt.Errorf("TestCanceledApply")
|
||||
// the actual apply value errors out after some time
|
||||
errValue := F.Pipe1(
|
||||
Left[string](err),
|
||||
Delay[string](time.Second),
|
||||
)
|
||||
// function never resolves
|
||||
fct := Never[func(string) string]()
|
||||
// apply the values, we expect an error after 1s
|
||||
|
||||
applied := F.Pipe1(
|
||||
fct,
|
||||
Ap[string, string](errValue),
|
||||
)
|
||||
|
||||
res := applied(context.Background())()
|
||||
assert.Equal(t, E.Left[string](err), res)
|
||||
}
|
||||
|
||||
func TestRegularApply(t *testing.T) {
|
||||
value := Of("Carsten")
|
||||
fct := Of(utils.Upper)
|
||||
|
||||
applied := F.Pipe1(
|
||||
fct,
|
||||
Ap[string, string](value),
|
||||
)
|
||||
|
||||
res := applied(context.Background())()
|
||||
assert.Equal(t, E.Of[error]("CARSTEN"), res)
|
||||
}
|
11
context/readerioeither/resource.go
Normal file
11
context/readerioeither/resource.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package readerioeither
|
||||
|
||||
import (
|
||||
G "github.com/ibm/fp-go/context/readerioeither/generic"
|
||||
)
|
||||
|
||||
// WithResource constructs a function that creates a resource, then operates on it and then releases the resource
|
||||
func WithResource[R, A, ANY any](onCreate ReaderIOEither[R], onRelease func(R) ReaderIOEither[ANY]) func(func(R) ReaderIOEither[A]) ReaderIOEither[A] {
|
||||
// wraps the callback functions with a context check
|
||||
return G.WithResource[ReaderIOEither[A]](onCreate, onRelease)
|
||||
}
|
24
context/readerioeither/sequence.go
Normal file
24
context/readerioeither/sequence.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package readerioeither
|
||||
|
||||
import (
|
||||
G "github.com/ibm/fp-go/context/readerioeither/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 ReaderIOEither[A]) ReaderIOEither[T.Tuple1[A]] {
|
||||
return G.SequenceT1[ReaderIOEither[T.Tuple1[A]]](a)
|
||||
}
|
||||
|
||||
func SequenceT2[A, B any](a ReaderIOEither[A], b ReaderIOEither[B]) ReaderIOEither[T.Tuple2[A, B]] {
|
||||
return G.SequenceT2[ReaderIOEither[T.Tuple2[A, B]]](a, b)
|
||||
}
|
||||
|
||||
func SequenceT3[A, B, C any](a ReaderIOEither[A], b ReaderIOEither[B], c ReaderIOEither[C]) ReaderIOEither[T.Tuple3[A, B, C]] {
|
||||
return G.SequenceT3[ReaderIOEither[T.Tuple3[A, B, C]]](a, b, c)
|
||||
}
|
||||
|
||||
func SequenceT4[A, B, C, D any](a ReaderIOEither[A], b ReaderIOEither[B], c ReaderIOEither[C], d ReaderIOEither[D]) ReaderIOEither[T.Tuple4[A, B, C, D]] {
|
||||
return G.SequenceT4[ReaderIOEither[T.Tuple4[A, B, C, D]]](a, b, c, d)
|
||||
}
|
25
context/readerioeither/traverse.go
Normal file
25
context/readerioeither/traverse.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package readerioeither
|
||||
|
||||
import (
|
||||
G "github.com/ibm/fp-go/context/readerioeither/generic"
|
||||
)
|
||||
|
||||
// TraverseArray transforms an array
|
||||
func TraverseArray[A, B any](f func(A) ReaderIOEither[B]) func([]A) ReaderIOEither[[]B] {
|
||||
return G.TraverseArray[[]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)
|
||||
}
|
||||
|
||||
// TraverseRecord transforms a record
|
||||
func TraverseRecord[K comparable, A, B any](f func(A) ReaderIOEither[B]) func(map[K]A) ReaderIOEither[map[K]B] {
|
||||
return G.TraverseRecord[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)
|
||||
}
|
11
context/readerioeither/type.go
Normal file
11
context/readerioeither/type.go
Normal file
@@ -0,0 +1,11 @@
|
||||
// Package readerioeither implements a specialization of the Reader monad assuming a golang context as the context of the monad and a standard golang error
|
||||
package readerioeither
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
RE "github.com/ibm/fp-go/readerioeither"
|
||||
)
|
||||
|
||||
// ReaderIOEither is a specialization of the Reader monad for the typical golang scenario
|
||||
type ReaderIOEither[A any] RE.ReaderIOEither[context.Context, error, A]
|
14
either/apply.go
Normal file
14
either/apply.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package either
|
||||
|
||||
import (
|
||||
M "github.com/ibm/fp-go/monoid"
|
||||
S "github.com/ibm/fp-go/semigroup"
|
||||
)
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
48
either/apply_test.go
Normal file
48
either/apply_test.go
Normal file
@@ -0,0 +1,48 @@
|
||||
package either
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
M "github.com/ibm/fp-go/monoid/testing"
|
||||
N "github.com/ibm/fp-go/number"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestApplySemigroup(t *testing.T) {
|
||||
|
||||
sg := ApplySemigroup[string](N.SemigroupSum[int]())
|
||||
|
||||
la := Left[int]("a")
|
||||
lb := Left[int]("b")
|
||||
r1 := Right[string](1)
|
||||
r2 := Right[string](2)
|
||||
r3 := Right[string](3)
|
||||
|
||||
assert.Equal(t, la, sg.Concat(la, lb))
|
||||
assert.Equal(t, lb, sg.Concat(r1, lb))
|
||||
assert.Equal(t, la, sg.Concat(la, r2))
|
||||
assert.Equal(t, lb, sg.Concat(r1, lb))
|
||||
assert.Equal(t, r3, sg.Concat(r1, r2))
|
||||
}
|
||||
|
||||
func TestApplicativeMonoid(t *testing.T) {
|
||||
|
||||
m := ApplicativeMonoid[string](N.MonoidSum[int]())
|
||||
|
||||
la := Left[int]("a")
|
||||
lb := Left[int]("b")
|
||||
r1 := Right[string](1)
|
||||
r2 := Right[string](2)
|
||||
r3 := Right[string](3)
|
||||
|
||||
assert.Equal(t, la, m.Concat(la, m.Empty()))
|
||||
assert.Equal(t, lb, m.Concat(m.Empty(), lb))
|
||||
assert.Equal(t, r1, m.Concat(r1, m.Empty()))
|
||||
assert.Equal(t, r2, m.Concat(m.Empty(), r2))
|
||||
assert.Equal(t, r3, m.Concat(r1, r2))
|
||||
}
|
||||
|
||||
func TestApplicativeMonoidLaws(t *testing.T) {
|
||||
m := ApplicativeMonoid[string](N.MonoidSum[int]())
|
||||
M.AssertLaws(t, m)([]Either[string, int]{Left[int]("a"), Right[string](1)})
|
||||
}
|
65
either/core.go
Normal file
65
either/core.go
Normal file
@@ -0,0 +1,65 @@
|
||||
package either
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type (
|
||||
// Either defines a data structure that logically holds either an E or an A. The flag discriminates the cases
|
||||
Either[E, A any] struct {
|
||||
isLeft bool
|
||||
left E
|
||||
right A
|
||||
}
|
||||
)
|
||||
|
||||
// String prints some debug info for the object
|
||||
func (s Either[E, A]) String() string {
|
||||
if s.isLeft {
|
||||
return fmt.Sprintf("Left[%T, %T](%v)", s.left, s.right, s.left)
|
||||
}
|
||||
return fmt.Sprintf("Right[%T, %T](%v)", s.left, s.right, s.right)
|
||||
}
|
||||
|
||||
// Format prints some debug info for the object
|
||||
func (s Either[E, A]) Format(f fmt.State, c rune) {
|
||||
switch c {
|
||||
case 's':
|
||||
fmt.Fprint(f, s.String())
|
||||
default:
|
||||
fmt.Fprint(f, s.String())
|
||||
}
|
||||
}
|
||||
|
||||
// IsLeft tests if the either is a left value. Rather use [Fold] if you need to access the values. Inverse is [IsRight].
|
||||
func IsLeft[E, A any](val Either[E, A]) bool {
|
||||
return val.isLeft
|
||||
}
|
||||
|
||||
// IsLeft tests if the either is a right value. Rather use [Fold] if you need to access the values. Inverse is [IsLeft].
|
||||
func IsRight[E, A any](val Either[E, A]) bool {
|
||||
return !val.isLeft
|
||||
}
|
||||
|
||||
// Left creates a new instance of an [Either] representing the left value.
|
||||
func Left[A, E any](value E) Either[E, A] {
|
||||
return Either[E, A]{isLeft: true, left: value}
|
||||
}
|
||||
|
||||
// Right creates a new instance of an [Either] representing the right value.
|
||||
func Right[E, A any](value A) Either[E, A] {
|
||||
return Either[E, A]{isLeft: false, right: value}
|
||||
}
|
||||
|
||||
// MonadFold extracts the values from an [Either] by invoking the [onLeft] callback or the [onRight] callback depending on the case
|
||||
func MonadFold[E, A, B any](ma Either[E, A], onLeft func(e E) B, onRight func(a A) B) B {
|
||||
if ma.isLeft {
|
||||
return onLeft(ma.left)
|
||||
}
|
||||
return onRight(ma.right)
|
||||
}
|
||||
|
||||
// Unwrap converts an [Either] into the idiomatic tuple
|
||||
func Unwrap[E, A any](ma Either[E, A]) (A, E) {
|
||||
return ma.right, ma.left
|
||||
}
|
61
either/curry.go
Normal file
61
either/curry.go
Normal file
@@ -0,0 +1,61 @@
|
||||
package either
|
||||
|
||||
import (
|
||||
F "github.com/ibm/fp-go/function"
|
||||
)
|
||||
|
||||
// these function curry a golang function that returns an error into its curried version that returns an either
|
||||
|
||||
func Curry0[R any](f func() (R, error)) func() Either[error, R] {
|
||||
return Eitherize0(f)
|
||||
}
|
||||
|
||||
func Curry1[T1, R any](f func(T1) (R, error)) func(T1) Either[error, R] {
|
||||
return Eitherize1(f)
|
||||
}
|
||||
|
||||
func Curry2[T1, T2, R any](f func(T1, T2) (R, error)) func(T1) func(T2) Either[error, R] {
|
||||
return F.Curry2(Eitherize2(f))
|
||||
}
|
||||
|
||||
func Curry3[T1, T2, T3, R any](f func(T1, T2, T3) (R, error)) func(T1) func(T2) func(T3) Either[error, R] {
|
||||
return F.Curry3(Eitherize3(f))
|
||||
}
|
||||
|
||||
func Curry4[T1, T2, T3, T4, R any](f func(T1, T2, T3, T4) (R, error)) func(T1) func(T2) func(T3) func(T4) Either[error, R] {
|
||||
return F.Curry4(Eitherize4(f))
|
||||
}
|
||||
|
||||
func Uncurry0[R any](f func() Either[error, R]) func() (R, error) {
|
||||
return func() (R, error) {
|
||||
return UnwrapError(f())
|
||||
}
|
||||
}
|
||||
|
||||
func Uncurry1[T1, R any](f func(T1) Either[error, R]) func(T1) (R, error) {
|
||||
uc := F.Uncurry1(f)
|
||||
return func(t1 T1) (R, error) {
|
||||
return UnwrapError(uc(t1))
|
||||
}
|
||||
}
|
||||
|
||||
func Uncurry2[T1, T2, R any](f func(T1) func(T2) Either[error, R]) func(T1, T2) (R, error) {
|
||||
uc := F.Uncurry2(f)
|
||||
return func(t1 T1, t2 T2) (R, error) {
|
||||
return UnwrapError(uc(t1, t2))
|
||||
}
|
||||
}
|
||||
|
||||
func Uncurry3[T1, T2, T3, R any](f func(T1) func(T2) func(T3) Either[error, R]) func(T1, T2, T3) (R, error) {
|
||||
uc := F.Uncurry3(f)
|
||||
return func(t1 T1, t2 T2, t3 T3) (R, error) {
|
||||
return UnwrapError(uc(t1, t2, t3))
|
||||
}
|
||||
}
|
||||
|
||||
func Uncurry4[T1, T2, T3, T4, R any](f func(T1) func(T2) func(T3) func(T4) Either[error, R]) func(T1, T2, T3, T4) (R, error) {
|
||||
uc := F.Uncurry4(f)
|
||||
return func(t1 T1, t2 T2, t3 T3, t4 T4) (R, error) {
|
||||
return UnwrapError(uc(t1, t2, t3, t4))
|
||||
}
|
||||
}
|
3
either/doc.go
Normal file
3
either/doc.go
Normal file
@@ -0,0 +1,3 @@
|
||||
package either
|
||||
|
||||
//go:generate go run .. either --count 10 --filename gen.go
|
232
either/either.go
Normal file
232
either/either.go
Normal file
@@ -0,0 +1,232 @@
|
||||
// package either implements the Either monad
|
||||
//
|
||||
// A data type that can be of either of two types but not both. This is
|
||||
// typically used to carry an error or a return value
|
||||
package either
|
||||
|
||||
import (
|
||||
E "github.com/ibm/fp-go/errors"
|
||||
F "github.com/ibm/fp-go/function"
|
||||
O "github.com/ibm/fp-go/option"
|
||||
)
|
||||
|
||||
// Of is equivalent to [Right]
|
||||
func Of[E, A any](value A) Either[E, A] {
|
||||
return F.Pipe1(value, Right[E, A])
|
||||
}
|
||||
|
||||
func FromIO[E, IO ~func() A, A any](f IO) Either[E, A] {
|
||||
return F.Pipe1(f(), Right[E, A])
|
||||
}
|
||||
|
||||
func MonadAp[B, E, A any](fab Either[E, func(a A) B], fa Either[E, A]) Either[E, B] {
|
||||
return MonadFold(fab, Left[B, E], func(ab func(A) B) Either[E, B] {
|
||||
return MonadFold(fa, Left[B, E], F.Flow2(ab, Right[E, B]))
|
||||
})
|
||||
}
|
||||
|
||||
func Ap[B, E, A any](fa Either[E, A]) func(fab Either[E, func(a A) B]) Either[E, B] {
|
||||
return F.Bind2nd(MonadAp[B, E, A], fa)
|
||||
}
|
||||
|
||||
func MonadMap[E, A, B any](fa Either[E, A], f func(a A) B) Either[E, B] {
|
||||
return MonadChain(fa, F.Flow2(f, Right[E, B]))
|
||||
}
|
||||
|
||||
func MonadBiMap[E1, E2, A, B any](fa Either[E1, A], f func(E1) E2, g func(a A) B) Either[E2, B] {
|
||||
return MonadFold(fa, F.Flow2(f, Left[B, E2]), F.Flow2(g, Right[E2, B]))
|
||||
}
|
||||
|
||||
// BiMap maps a pair of functions over the two type arguments of the bifunctor.
|
||||
func BiMap[E1, E2, A, B any](f func(E1) E2, g func(a A) B) func(Either[E1, A]) Either[E2, B] {
|
||||
return Fold(F.Flow2(f, Left[B, E2]), F.Flow2(g, Right[E2, B]))
|
||||
}
|
||||
|
||||
func MonadMapTo[E, A, B any](fa Either[E, A], b B) Either[E, B] {
|
||||
return MonadMap(fa, F.Constant1[A](b))
|
||||
}
|
||||
|
||||
func MapTo[E, A, B any](b B) func(Either[E, A]) Either[E, B] {
|
||||
return F.Bind2nd(MonadMapTo[E, A, B], b)
|
||||
}
|
||||
|
||||
func MonadMapLeft[E, A, B any](fa Either[E, A], f func(E) B) Either[B, A] {
|
||||
return MonadFold(fa, F.Flow2(f, Left[A, B]), Right[B, A])
|
||||
}
|
||||
|
||||
func Map[E, A, B any](f func(a A) B) func(fa Either[E, A]) Either[E, B] {
|
||||
return Chain(F.Flow2(f, Right[E, B]))
|
||||
}
|
||||
|
||||
func MapLeft[E, A, B any](f func(E) B) func(fa Either[E, A]) Either[B, A] {
|
||||
return F.Bind2nd(MonadMapLeft[E, A, B], f)
|
||||
}
|
||||
|
||||
func MonadChain[E, A, B any](fa Either[E, A], f func(a A) Either[E, B]) Either[E, B] {
|
||||
return MonadFold(fa, Left[B, E], f)
|
||||
}
|
||||
|
||||
func MonadChainFirst[E, A, B any](ma Either[E, A], f func(a A) Either[E, B]) Either[E, A] {
|
||||
return MonadChain(ma, func(a A) Either[E, A] {
|
||||
return MonadMap(f(a), F.Constant1[B](a))
|
||||
})
|
||||
}
|
||||
|
||||
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 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)
|
||||
return func(f func(A) O.Option[B]) func(Either[E, A]) Either[E, B] {
|
||||
return Chain(F.Flow2(f, from))
|
||||
}
|
||||
}
|
||||
|
||||
func ChainTo[E, A, B any](mb Either[E, B]) func(Either[E, A]) Either[E, B] {
|
||||
return F.Bind2nd(MonadChainTo[E, A, B], mb)
|
||||
}
|
||||
|
||||
func Chain[E, A, B any](f func(a A) Either[E, B]) func(Either[E, A]) Either[E, B] {
|
||||
return F.Bind2nd(MonadChain[E, A, B], f)
|
||||
}
|
||||
|
||||
func ChainFirst[E, A, B any](f func(a A) Either[E, B]) func(Either[E, A]) Either[E, A] {
|
||||
return F.Bind2nd(MonadChainFirst[E, A, B], f)
|
||||
}
|
||||
|
||||
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()
|
||||
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 Sequence2[E, T1, T2, R any](f func(T1, T2) Either[E, R]) func(Either[E, T1], Either[E, T2]) Either[E, R] {
|
||||
return func(e1 Either[E, T1], e2 Either[E, T2]) Either[E, R] {
|
||||
return MonadSequence2(e1, e2, f)
|
||||
}
|
||||
}
|
||||
|
||||
func Sequence3[E, T1, T2, T3, R any](f func(T1, T2, T3) Either[E, R]) func(Either[E, T1], Either[E, T2], Either[E, T3]) Either[E, R] {
|
||||
return func(e1 Either[E, T1], e2 Either[E, T2], e3 Either[E, T3]) Either[E, R] {
|
||||
return MonadSequence3(e1, e2, e3, f)
|
||||
}
|
||||
}
|
||||
|
||||
func FromOption[E, A 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 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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func ToError[A any](e Either[error, A]) error {
|
||||
return MonadFold(e, E.IdentityError, F.Constant1[A, error](nil))
|
||||
}
|
||||
|
||||
func Fold[E, A, B any](onLeft func(E) B, onRight func(A) B) func(Either[E, A]) B {
|
||||
return func(ma Either[E, A]) B {
|
||||
return MonadFold(ma, onLeft, onRight)
|
||||
}
|
||||
}
|
||||
|
||||
// UnwrapError converts an Either into the idiomatic tuple
|
||||
func UnwrapError[A any](ma Either[error, A]) (A, error) {
|
||||
return Unwrap[error](ma)
|
||||
}
|
||||
|
||||
func FromPredicate[E, A any](pred func(A) bool, onFalse func(A) E) func(A) Either[E, A] {
|
||||
return func(a A) Either[E, A] {
|
||||
if pred(a) {
|
||||
return Right[E](a)
|
||||
}
|
||||
return Left[A, E](onFalse(a))
|
||||
}
|
||||
}
|
||||
|
||||
func FromNillable[E, A any](e E) func(*A) Either[E, *A] {
|
||||
return FromPredicate(F.IsNonNil[A], F.Constant1[*A](e))
|
||||
}
|
||||
|
||||
func GetOrElse[E, A any](onLeft func(E) A) func(Either[E, A]) A {
|
||||
return Fold(onLeft, F.Identity[A])
|
||||
}
|
||||
|
||||
func Reduce[E, A, B any](f func(B, A) B, initial B) func(Either[E, A]) B {
|
||||
return Fold(
|
||||
F.Constant1[E](initial),
|
||||
F.Bind1st(f, initial),
|
||||
)
|
||||
}
|
||||
|
||||
func AltW[E, E1, A any](that func() 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] {
|
||||
return AltW[E](that)
|
||||
}
|
||||
|
||||
func OrElse[E, A any](onLeft func(e E) Either[E, A]) func(Either[E, A]) Either[E, A] {
|
||||
return Fold(onLeft, Of[E, A])
|
||||
}
|
||||
|
||||
func ToType[E, A any](onError func(any) E) func(any) Either[E, A] {
|
||||
return func(value any) Either[E, A] {
|
||||
return F.Pipe2(
|
||||
value,
|
||||
O.ToType[A],
|
||||
O.Fold(F.Nullary3(F.Constant(value), onError, Left[A, E]), Right[E, A]),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func Memoize[E, A any](val Either[E, A]) Either[E, A] {
|
||||
return val
|
||||
}
|
||||
|
||||
func MonadSequence2[E, T1, T2, R any](e1 Either[E, T1], e2 Either[E, T2], f func(T1, T2) Either[E, R]) Either[E, R] {
|
||||
return MonadFold(e1, Left[R, E], func(t1 T1) Either[E, R] {
|
||||
return MonadFold(e2, Left[R, E], func(t2 T2) Either[E, R] {
|
||||
return f(t1, t2)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func MonadSequence3[E, T1, T2, T3, R any](e1 Either[E, T1], e2 Either[E, T2], e3 Either[E, T3], f func(T1, T2, T3) Either[E, R]) Either[E, R] {
|
||||
return MonadFold(e1, Left[R, E], func(t1 T1) Either[E, R] {
|
||||
return MonadFold(e2, Left[R, E], func(t2 T2) Either[E, R] {
|
||||
return MonadFold(e3, Left[R, E], func(t3 T3) Either[E, R] {
|
||||
return f(t1, t2, t3)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// Swap changes the order of type parameters
|
||||
func Swap[E, A any](val Either[E, A]) Either[A, E] {
|
||||
return MonadFold(val, Right[A, E], Left[E, A])
|
||||
}
|
102
either/either_test.go
Normal file
102
either/either_test.go
Normal file
@@ -0,0 +1,102 @@
|
||||
package either
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
F "github.com/ibm/fp-go/function"
|
||||
"github.com/ibm/fp-go/internal/utils"
|
||||
O "github.com/ibm/fp-go/option"
|
||||
S "github.com/ibm/fp-go/string"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestDefault(t *testing.T) {
|
||||
var e Either[error, string]
|
||||
|
||||
assert.Equal(t, Of[error](""), e)
|
||||
}
|
||||
|
||||
func TestIsLeft(t *testing.T) {
|
||||
err := errors.New("Some error")
|
||||
withError := Left[string](err)
|
||||
|
||||
assert.True(t, IsLeft(withError))
|
||||
assert.False(t, IsRight(withError))
|
||||
}
|
||||
|
||||
func TestIsRight(t *testing.T) {
|
||||
noError := Right[error]("Carsten")
|
||||
|
||||
assert.True(t, IsRight(noError))
|
||||
assert.False(t, IsLeft(noError))
|
||||
}
|
||||
|
||||
func TestMapEither(t *testing.T) {
|
||||
|
||||
assert.Equal(t, F.Pipe1(Right[error]("abc"), Map[error](utils.StringLen)), Right[error](3))
|
||||
|
||||
val2 := F.Pipe1(Left[string, string]("s"), Map[string](utils.StringLen))
|
||||
exp2 := Left[int]("s")
|
||||
|
||||
assert.Equal(t, val2, exp2)
|
||||
}
|
||||
|
||||
func TestUnwrapError(t *testing.T) {
|
||||
a := ""
|
||||
err := errors.New("Some error")
|
||||
withError := Left[string](err)
|
||||
|
||||
res, extracted := UnwrapError(withError)
|
||||
assert.Equal(t, a, res)
|
||||
assert.Equal(t, extracted, err)
|
||||
|
||||
}
|
||||
|
||||
func TestReduce(t *testing.T) {
|
||||
|
||||
s := S.Semigroup()
|
||||
|
||||
assert.Equal(t, "foobar", F.Pipe1(Right[string]("bar"), Reduce[string](s.Concat, "foo")))
|
||||
assert.Equal(t, "foo", F.Pipe1(Left[string, string]("bar"), Reduce[string](s.Concat, "foo")))
|
||||
|
||||
}
|
||||
|
||||
func TestAp(t *testing.T) {
|
||||
f := S.Size
|
||||
|
||||
assert.Equal(t, Right[string](3), F.Pipe1(Right[string](f), Ap[int, string, string](Right[string]("abc"))))
|
||||
assert.Equal(t, Left[int]("maError"), F.Pipe1(Right[string](f), Ap[int, string, string](Left[string, string]("maError"))))
|
||||
assert.Equal(t, Left[int]("mabError"), F.Pipe1(Left[func(string) int]("mabError"), Ap[int, string, string](Left[string, string]("maError"))))
|
||||
}
|
||||
|
||||
func TestAlt(t *testing.T) {
|
||||
assert.Equal(t, Right[string](1), F.Pipe1(Right[string](1), Alt(F.Constant(Right[string](2)))))
|
||||
assert.Equal(t, Right[string](1), F.Pipe1(Right[string](1), Alt(F.Constant(Left[int]("a")))))
|
||||
assert.Equal(t, Right[string](2), F.Pipe1(Left[int]("b"), Alt(F.Constant(Right[string](2)))))
|
||||
assert.Equal(t, Left[int]("b"), F.Pipe1(Left[int]("a"), Alt(F.Constant(Left[int]("b")))))
|
||||
}
|
||||
|
||||
func TestChainFirst(t *testing.T) {
|
||||
f := F.Flow2(S.Size, Right[string, int])
|
||||
|
||||
assert.Equal(t, Right[string]("abc"), F.Pipe1(Right[string]("abc"), ChainFirst(f)))
|
||||
assert.Equal(t, Left[string, string]("maError"), F.Pipe1(Left[string, string]("maError"), ChainFirst(f)))
|
||||
}
|
||||
|
||||
func TestChainOptionK(t *testing.T) {
|
||||
f := ChainOptionK[string, int, int](F.Constant("a"))(func(n int) O.Option[int] {
|
||||
if n > 0 {
|
||||
return O.Some(n)
|
||||
}
|
||||
return O.None[int]()
|
||||
})
|
||||
assert.Equal(t, Right[string](1), f(Right[string](1)))
|
||||
assert.Equal(t, Left[int]("a"), f(Right[string](-1)))
|
||||
assert.Equal(t, Left[int]("b"), f(Left[int]("b")))
|
||||
}
|
||||
|
||||
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)))
|
||||
}
|
25
either/eq.go
Normal file
25
either/eq.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package either
|
||||
|
||||
import (
|
||||
EQ "github.com/ibm/fp-go/eq"
|
||||
F "github.com/ibm/fp-go/function"
|
||||
)
|
||||
|
||||
// Constructs an equal predicate for an `Either`
|
||||
func Eq[E, A any](e EQ.Eq[E], a EQ.Eq[A]) EQ.Eq[Either[E, A]] {
|
||||
// some convenient shortcuts
|
||||
eqa := F.Curry2(a.Equals)
|
||||
eqe := F.Curry2(e.Equals)
|
||||
|
||||
fca := F.Bind2nd(Fold[E, A, bool], F.Constant1[A](false))
|
||||
fce := F.Bind1st(Fold[E, A, bool], F.Constant1[E](false))
|
||||
|
||||
fld := Fold(F.Flow2(eqe, fca), F.Flow2(eqa, fce))
|
||||
|
||||
return EQ.FromEquals(F.Uncurry2(fld))
|
||||
}
|
||||
|
||||
// FromStrictEquals constructs an `Eq` from the canonical comparison function
|
||||
func FromStrictEquals[E, A comparable]() EQ.Eq[Either[E, A]] {
|
||||
return Eq(EQ.FromStrictEquals[E](), EQ.FromStrictEquals[A]())
|
||||
}
|
30
either/eq_test.go
Normal file
30
either/eq_test.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package either
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestEq(t *testing.T) {
|
||||
|
||||
r1 := Of[string](1)
|
||||
r2 := Of[string](1)
|
||||
r3 := Of[string](2)
|
||||
|
||||
e1 := Left[int]("a")
|
||||
e2 := Left[int]("a")
|
||||
e3 := Left[int]("b")
|
||||
|
||||
eq := FromStrictEquals[string, int]()
|
||||
|
||||
assert.True(t, eq.Equals(r1, r1))
|
||||
assert.True(t, eq.Equals(r1, r2))
|
||||
assert.False(t, eq.Equals(r1, r3))
|
||||
assert.False(t, eq.Equals(r1, e1))
|
||||
|
||||
assert.True(t, eq.Equals(e1, e1))
|
||||
assert.True(t, eq.Equals(e1, e2))
|
||||
assert.False(t, eq.Equals(e1, e3))
|
||||
assert.False(t, eq.Equals(e2, r2))
|
||||
}
|
23
either/exec/exec.go
Normal file
23
either/exec/exec.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package exec
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
E "github.com/ibm/fp-go/either"
|
||||
"github.com/ibm/fp-go/exec"
|
||||
F "github.com/ibm/fp-go/function"
|
||||
GE "github.com/ibm/fp-go/internal/exec"
|
||||
)
|
||||
|
||||
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
|
||||
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)
|
||||
})
|
||||
}
|
703
either/gen.go
Normal file
703
either/gen.go
Normal file
@@ -0,0 +1,703 @@
|
||||
// Code generated by go generate; DO NOT EDIT.
|
||||
// This file was generated by robots at
|
||||
// 2023-07-18 15:21:17.0339772 +0200 CEST m=+0.084638001
|
||||
package either
|
||||
|
||||
|
||||
import (
|
||||
A "github.com/ibm/fp-go/internal/apply"
|
||||
T "github.com/ibm/fp-go/tuple"
|
||||
)
|
||||
|
||||
// Eitherize0 converts a function with 0 parameters returning a tuple into a function with 0 parameters returning an Either
|
||||
// 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()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Uneitherize0 converts a function with 0 parameters returning an Either into a function with 0 parameters returning a tuple
|
||||
// The inverse function is [Eitherize0]
|
||||
func Uneitherize0[F ~func() Either[error, R], R any](f F) func() (R, error) {
|
||||
return func() (R, error) {
|
||||
return UnwrapError(f())
|
||||
}
|
||||
}
|
||||
|
||||
// Eitherize1 converts a function with 1 parameters returning a tuple into a function with 1 parameters returning an Either
|
||||
// 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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Uneitherize1 converts a function with 1 parameters returning an Either into a function with 1 parameters returning a tuple
|
||||
// The inverse function is [Eitherize1]
|
||||
func Uneitherize1[F ~func(T0) Either[error, R], T0, R any](f F) func(T0) (R, error) {
|
||||
return func(t0 T0) (R, error) {
|
||||
return UnwrapError(f(t0))
|
||||
}
|
||||
}
|
||||
|
||||
// SequenceT1 converts 1 parameters of [Either[E, T]] into a [Either[E, Tuple1]].
|
||||
func SequenceT1[E, T1 any](t1 Either[E, T1]) Either[E, T.Tuple1[T1]] {
|
||||
return A.SequenceT1(
|
||||
Map[E, T1, T.Tuple1[T1]],
|
||||
t1,
|
||||
)
|
||||
}
|
||||
|
||||
// SequenceTuple1 converts a [Tuple1] of [Either[E, T]] into an [Either[E, Tuple1]].
|
||||
func SequenceTuple1[E, T1 any](t T.Tuple1[Either[E, T1]]) Either[E, T.Tuple1[T1]] {
|
||||
return A.SequenceTuple1(
|
||||
Map[E, T1, T.Tuple1[T1]],
|
||||
t,
|
||||
)
|
||||
}
|
||||
|
||||
// TraverseTuple1 converts a [Tuple1] of [A] via transformation functions transforming [A] to [Either[E, A]] into a [Either[E, Tuple1]].
|
||||
func TraverseTuple1[F1 ~func(A1) Either[E, T1], E, A1, T1 any](f1 F1) func (T.Tuple1[A1]) Either[E, T.Tuple1[T1]] {
|
||||
return func(t T.Tuple1[A1]) Either[E, T.Tuple1[T1]] {
|
||||
return A.TraverseTuple1(
|
||||
Map[E, T1, T.Tuple1[T1]],
|
||||
f1,
|
||||
t,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Eitherize2 converts a function with 2 parameters returning a tuple into a function with 2 parameters returning an Either
|
||||
// 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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Uneitherize2 converts a function with 2 parameters returning an Either into a function with 2 parameters returning a tuple
|
||||
// The inverse function is [Eitherize2]
|
||||
func Uneitherize2[F ~func(T0, T1) Either[error, R], T0, T1, R any](f F) func(T0, T1) (R, error) {
|
||||
return func(t0 T0, t1 T1) (R, error) {
|
||||
return UnwrapError(f(t0, t1))
|
||||
}
|
||||
}
|
||||
|
||||
// SequenceT2 converts 2 parameters of [Either[E, T]] into a [Either[E, Tuple2]].
|
||||
func SequenceT2[E, T1, T2 any](t1 Either[E, T1], t2 Either[E, T2]) Either[E, T.Tuple2[T1, T2]] {
|
||||
return A.SequenceT2(
|
||||
Map[E, T1, func(T2) T.Tuple2[T1, T2]],
|
||||
Ap[T.Tuple2[T1, T2], E, T2],
|
||||
t1,
|
||||
t2,
|
||||
)
|
||||
}
|
||||
|
||||
// SequenceTuple2 converts a [Tuple2] of [Either[E, T]] into an [Either[E, Tuple2]].
|
||||
func SequenceTuple2[E, T1, T2 any](t T.Tuple2[Either[E, T1], Either[E, T2]]) Either[E, T.Tuple2[T1, T2]] {
|
||||
return A.SequenceTuple2(
|
||||
Map[E, T1, func(T2) T.Tuple2[T1, T2]],
|
||||
Ap[T.Tuple2[T1, T2], E, T2],
|
||||
t,
|
||||
)
|
||||
}
|
||||
|
||||
// TraverseTuple2 converts a [Tuple2] of [A] via transformation functions transforming [A] to [Either[E, A]] into a [Either[E, Tuple2]].
|
||||
func TraverseTuple2[F1 ~func(A1) Either[E, T1], F2 ~func(A2) Either[E, T2], E, A1, T1, A2, T2 any](f1 F1, f2 F2) func (T.Tuple2[A1, A2]) Either[E, T.Tuple2[T1, T2]] {
|
||||
return func(t T.Tuple2[A1, A2]) Either[E, T.Tuple2[T1, T2]] {
|
||||
return A.TraverseTuple2(
|
||||
Map[E, T1, func(T2) T.Tuple2[T1, T2]],
|
||||
Ap[T.Tuple2[T1, T2], E, T2],
|
||||
f1,
|
||||
f2,
|
||||
t,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Eitherize3 converts a function with 3 parameters returning a tuple into a function with 3 parameters returning an Either
|
||||
// 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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Uneitherize3 converts a function with 3 parameters returning an Either into a function with 3 parameters returning a tuple
|
||||
// The inverse function is [Eitherize3]
|
||||
func Uneitherize3[F ~func(T0, T1, T2) Either[error, R], T0, T1, T2, R any](f F) func(T0, T1, T2) (R, error) {
|
||||
return func(t0 T0, t1 T1, t2 T2) (R, error) {
|
||||
return UnwrapError(f(t0, t1, t2))
|
||||
}
|
||||
}
|
||||
|
||||
// SequenceT3 converts 3 parameters of [Either[E, T]] into a [Either[E, Tuple3]].
|
||||
func SequenceT3[E, T1, T2, T3 any](t1 Either[E, T1], t2 Either[E, T2], t3 Either[E, T3]) Either[E, T.Tuple3[T1, T2, T3]] {
|
||||
return A.SequenceT3(
|
||||
Map[E, T1, func(T2) func(T3) T.Tuple3[T1, T2, T3]],
|
||||
Ap[func(T3) T.Tuple3[T1, T2, T3], E, T2],
|
||||
Ap[T.Tuple3[T1, T2, T3], E, T3],
|
||||
t1,
|
||||
t2,
|
||||
t3,
|
||||
)
|
||||
}
|
||||
|
||||
// SequenceTuple3 converts a [Tuple3] of [Either[E, T]] into an [Either[E, Tuple3]].
|
||||
func SequenceTuple3[E, T1, T2, T3 any](t T.Tuple3[Either[E, T1], Either[E, T2], Either[E, T3]]) Either[E, T.Tuple3[T1, T2, T3]] {
|
||||
return A.SequenceTuple3(
|
||||
Map[E, T1, func(T2) func(T3) T.Tuple3[T1, T2, T3]],
|
||||
Ap[func(T3) T.Tuple3[T1, T2, T3], E, T2],
|
||||
Ap[T.Tuple3[T1, T2, T3], E, T3],
|
||||
t,
|
||||
)
|
||||
}
|
||||
|
||||
// TraverseTuple3 converts a [Tuple3] of [A] via transformation functions transforming [A] to [Either[E, A]] into a [Either[E, Tuple3]].
|
||||
func TraverseTuple3[F1 ~func(A1) Either[E, T1], F2 ~func(A2) Either[E, T2], F3 ~func(A3) Either[E, T3], E, A1, T1, A2, T2, A3, T3 any](f1 F1, f2 F2, f3 F3) func (T.Tuple3[A1, A2, A3]) Either[E, T.Tuple3[T1, T2, T3]] {
|
||||
return func(t T.Tuple3[A1, A2, A3]) Either[E, T.Tuple3[T1, T2, T3]] {
|
||||
return A.TraverseTuple3(
|
||||
Map[E, T1, func(T2) func(T3) T.Tuple3[T1, T2, T3]],
|
||||
Ap[func(T3) T.Tuple3[T1, T2, T3], E, T2],
|
||||
Ap[T.Tuple3[T1, T2, T3], E, T3],
|
||||
f1,
|
||||
f2,
|
||||
f3,
|
||||
t,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Eitherize4 converts a function with 4 parameters returning a tuple into a function with 4 parameters returning an Either
|
||||
// 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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Uneitherize4 converts a function with 4 parameters returning an Either into a function with 4 parameters returning a tuple
|
||||
// The inverse function is [Eitherize4]
|
||||
func Uneitherize4[F ~func(T0, T1, T2, T3) Either[error, R], T0, T1, T2, T3, R any](f F) func(T0, T1, T2, T3) (R, error) {
|
||||
return func(t0 T0, t1 T1, t2 T2, t3 T3) (R, error) {
|
||||
return UnwrapError(f(t0, t1, t2, t3))
|
||||
}
|
||||
}
|
||||
|
||||
// SequenceT4 converts 4 parameters of [Either[E, T]] into a [Either[E, Tuple4]].
|
||||
func SequenceT4[E, T1, T2, T3, T4 any](t1 Either[E, T1], t2 Either[E, T2], t3 Either[E, T3], t4 Either[E, T4]) Either[E, T.Tuple4[T1, T2, T3, T4]] {
|
||||
return A.SequenceT4(
|
||||
Map[E, T1, func(T2) func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]],
|
||||
Ap[func(T3) func(T4) T.Tuple4[T1, T2, T3, T4], E, T2],
|
||||
Ap[func(T4) T.Tuple4[T1, T2, T3, T4], E, T3],
|
||||
Ap[T.Tuple4[T1, T2, T3, T4], E, T4],
|
||||
t1,
|
||||
t2,
|
||||
t3,
|
||||
t4,
|
||||
)
|
||||
}
|
||||
|
||||
// SequenceTuple4 converts a [Tuple4] of [Either[E, T]] into an [Either[E, Tuple4]].
|
||||
func SequenceTuple4[E, T1, T2, T3, T4 any](t T.Tuple4[Either[E, T1], Either[E, T2], Either[E, T3], Either[E, T4]]) Either[E, T.Tuple4[T1, T2, T3, T4]] {
|
||||
return A.SequenceTuple4(
|
||||
Map[E, T1, func(T2) func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]],
|
||||
Ap[func(T3) func(T4) T.Tuple4[T1, T2, T3, T4], E, T2],
|
||||
Ap[func(T4) T.Tuple4[T1, T2, T3, T4], E, T3],
|
||||
Ap[T.Tuple4[T1, T2, T3, T4], E, T4],
|
||||
t,
|
||||
)
|
||||
}
|
||||
|
||||
// TraverseTuple4 converts a [Tuple4] of [A] via transformation functions transforming [A] to [Either[E, A]] into a [Either[E, Tuple4]].
|
||||
func TraverseTuple4[F1 ~func(A1) Either[E, T1], F2 ~func(A2) Either[E, T2], F3 ~func(A3) Either[E, T3], F4 ~func(A4) Either[E, T4], E, A1, T1, A2, T2, A3, T3, A4, T4 any](f1 F1, f2 F2, f3 F3, f4 F4) func (T.Tuple4[A1, A2, A3, A4]) Either[E, T.Tuple4[T1, T2, T3, T4]] {
|
||||
return func(t T.Tuple4[A1, A2, A3, A4]) Either[E, T.Tuple4[T1, T2, T3, T4]] {
|
||||
return A.TraverseTuple4(
|
||||
Map[E, T1, func(T2) func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]],
|
||||
Ap[func(T3) func(T4) T.Tuple4[T1, T2, T3, T4], E, T2],
|
||||
Ap[func(T4) T.Tuple4[T1, T2, T3, T4], E, T3],
|
||||
Ap[T.Tuple4[T1, T2, T3, T4], E, T4],
|
||||
f1,
|
||||
f2,
|
||||
f3,
|
||||
f4,
|
||||
t,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Eitherize5 converts a function with 5 parameters returning a tuple into a function with 5 parameters returning an Either
|
||||
// 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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Uneitherize5 converts a function with 5 parameters returning an Either into a function with 5 parameters returning a tuple
|
||||
// The inverse function is [Eitherize5]
|
||||
func Uneitherize5[F ~func(T0, T1, T2, T3, T4) Either[error, R], T0, T1, T2, T3, T4, R any](f F) func(T0, T1, T2, T3, T4) (R, error) {
|
||||
return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4) (R, error) {
|
||||
return UnwrapError(f(t0, t1, t2, t3, t4))
|
||||
}
|
||||
}
|
||||
|
||||
// SequenceT5 converts 5 parameters of [Either[E, T]] into a [Either[E, Tuple5]].
|
||||
func SequenceT5[E, T1, T2, T3, T4, T5 any](t1 Either[E, T1], t2 Either[E, T2], t3 Either[E, T3], t4 Either[E, T4], t5 Either[E, T5]) Either[E, T.Tuple5[T1, T2, T3, T4, T5]] {
|
||||
return A.SequenceT5(
|
||||
Map[E, T1, func(T2) func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]],
|
||||
Ap[func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5], E, T2],
|
||||
Ap[func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5], E, T3],
|
||||
Ap[func(T5) T.Tuple5[T1, T2, T3, T4, T5], E, T4],
|
||||
Ap[T.Tuple5[T1, T2, T3, T4, T5], E, T5],
|
||||
t1,
|
||||
t2,
|
||||
t3,
|
||||
t4,
|
||||
t5,
|
||||
)
|
||||
}
|
||||
|
||||
// SequenceTuple5 converts a [Tuple5] of [Either[E, T]] into an [Either[E, Tuple5]].
|
||||
func SequenceTuple5[E, T1, T2, T3, T4, T5 any](t T.Tuple5[Either[E, T1], Either[E, T2], Either[E, T3], Either[E, T4], Either[E, T5]]) Either[E, T.Tuple5[T1, T2, T3, T4, T5]] {
|
||||
return A.SequenceTuple5(
|
||||
Map[E, T1, func(T2) func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]],
|
||||
Ap[func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5], E, T2],
|
||||
Ap[func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5], E, T3],
|
||||
Ap[func(T5) T.Tuple5[T1, T2, T3, T4, T5], E, T4],
|
||||
Ap[T.Tuple5[T1, T2, T3, T4, T5], E, T5],
|
||||
t,
|
||||
)
|
||||
}
|
||||
|
||||
// TraverseTuple5 converts a [Tuple5] of [A] via transformation functions transforming [A] to [Either[E, A]] into a [Either[E, Tuple5]].
|
||||
func TraverseTuple5[F1 ~func(A1) Either[E, T1], F2 ~func(A2) Either[E, T2], F3 ~func(A3) Either[E, T3], F4 ~func(A4) Either[E, T4], F5 ~func(A5) Either[E, T5], E, A1, T1, A2, T2, A3, T3, A4, T4, A5, T5 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5) func (T.Tuple5[A1, A2, A3, A4, A5]) Either[E, T.Tuple5[T1, T2, T3, T4, T5]] {
|
||||
return func(t T.Tuple5[A1, A2, A3, A4, A5]) Either[E, T.Tuple5[T1, T2, T3, T4, T5]] {
|
||||
return A.TraverseTuple5(
|
||||
Map[E, T1, func(T2) func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]],
|
||||
Ap[func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5], E, T2],
|
||||
Ap[func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5], E, T3],
|
||||
Ap[func(T5) T.Tuple5[T1, T2, T3, T4, T5], E, T4],
|
||||
Ap[T.Tuple5[T1, T2, T3, T4, T5], E, T5],
|
||||
f1,
|
||||
f2,
|
||||
f3,
|
||||
f4,
|
||||
f5,
|
||||
t,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Eitherize6 converts a function with 6 parameters returning a tuple into a function with 6 parameters returning an Either
|
||||
// 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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Uneitherize6 converts a function with 6 parameters returning an Either into a function with 6 parameters returning a tuple
|
||||
// The inverse function is [Eitherize6]
|
||||
func Uneitherize6[F ~func(T0, T1, T2, T3, T4, T5) Either[error, R], T0, T1, T2, T3, T4, T5, R any](f F) func(T0, T1, T2, T3, T4, T5) (R, error) {
|
||||
return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5) (R, error) {
|
||||
return UnwrapError(f(t0, t1, t2, t3, t4, t5))
|
||||
}
|
||||
}
|
||||
|
||||
// SequenceT6 converts 6 parameters of [Either[E, T]] into a [Either[E, Tuple6]].
|
||||
func SequenceT6[E, T1, T2, T3, T4, T5, T6 any](t1 Either[E, T1], t2 Either[E, T2], t3 Either[E, T3], t4 Either[E, T4], t5 Either[E, T5], t6 Either[E, T6]) Either[E, T.Tuple6[T1, T2, T3, T4, T5, T6]] {
|
||||
return A.SequenceT6(
|
||||
Map[E, T1, func(T2) func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]],
|
||||
Ap[func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], E, T2],
|
||||
Ap[func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], E, T3],
|
||||
Ap[func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], E, T4],
|
||||
Ap[func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], E, T5],
|
||||
Ap[T.Tuple6[T1, T2, T3, T4, T5, T6], E, T6],
|
||||
t1,
|
||||
t2,
|
||||
t3,
|
||||
t4,
|
||||
t5,
|
||||
t6,
|
||||
)
|
||||
}
|
||||
|
||||
// SequenceTuple6 converts a [Tuple6] of [Either[E, T]] into an [Either[E, Tuple6]].
|
||||
func SequenceTuple6[E, T1, T2, T3, T4, T5, T6 any](t T.Tuple6[Either[E, T1], Either[E, T2], Either[E, T3], Either[E, T4], Either[E, T5], Either[E, T6]]) Either[E, T.Tuple6[T1, T2, T3, T4, T5, T6]] {
|
||||
return A.SequenceTuple6(
|
||||
Map[E, T1, func(T2) func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]],
|
||||
Ap[func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], E, T2],
|
||||
Ap[func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], E, T3],
|
||||
Ap[func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], E, T4],
|
||||
Ap[func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], E, T5],
|
||||
Ap[T.Tuple6[T1, T2, T3, T4, T5, T6], E, T6],
|
||||
t,
|
||||
)
|
||||
}
|
||||
|
||||
// TraverseTuple6 converts a [Tuple6] of [A] via transformation functions transforming [A] to [Either[E, A]] into a [Either[E, Tuple6]].
|
||||
func TraverseTuple6[F1 ~func(A1) Either[E, T1], F2 ~func(A2) Either[E, T2], F3 ~func(A3) Either[E, T3], F4 ~func(A4) Either[E, T4], F5 ~func(A5) Either[E, T5], F6 ~func(A6) Either[E, T6], E, A1, T1, A2, T2, A3, T3, A4, T4, A5, T5, A6, T6 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6) func (T.Tuple6[A1, A2, A3, A4, A5, A6]) Either[E, T.Tuple6[T1, T2, T3, T4, T5, T6]] {
|
||||
return func(t T.Tuple6[A1, A2, A3, A4, A5, A6]) Either[E, T.Tuple6[T1, T2, T3, T4, T5, T6]] {
|
||||
return A.TraverseTuple6(
|
||||
Map[E, T1, func(T2) func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]],
|
||||
Ap[func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], E, T2],
|
||||
Ap[func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], E, T3],
|
||||
Ap[func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], E, T4],
|
||||
Ap[func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], E, T5],
|
||||
Ap[T.Tuple6[T1, T2, T3, T4, T5, T6], E, T6],
|
||||
f1,
|
||||
f2,
|
||||
f3,
|
||||
f4,
|
||||
f5,
|
||||
f6,
|
||||
t,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Eitherize7 converts a function with 7 parameters returning a tuple into a function with 7 parameters returning an Either
|
||||
// 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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Uneitherize7 converts a function with 7 parameters returning an Either into a function with 7 parameters returning a tuple
|
||||
// The inverse function is [Eitherize7]
|
||||
func Uneitherize7[F ~func(T0, T1, T2, T3, T4, T5, T6) Either[error, R], T0, T1, T2, T3, T4, T5, T6, R any](f F) func(T0, T1, T2, T3, T4, T5, T6) (R, error) {
|
||||
return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6) (R, error) {
|
||||
return UnwrapError(f(t0, t1, t2, t3, t4, t5, t6))
|
||||
}
|
||||
}
|
||||
|
||||
// SequenceT7 converts 7 parameters of [Either[E, T]] into a [Either[E, Tuple7]].
|
||||
func SequenceT7[E, T1, T2, T3, T4, T5, T6, T7 any](t1 Either[E, T1], t2 Either[E, T2], t3 Either[E, T3], t4 Either[E, T4], t5 Either[E, T5], t6 Either[E, T6], t7 Either[E, T7]) Either[E, T.Tuple7[T1, T2, T3, T4, T5, T6, T7]] {
|
||||
return A.SequenceT7(
|
||||
Map[E, T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]],
|
||||
Ap[func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], E, T2],
|
||||
Ap[func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], E, T3],
|
||||
Ap[func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], E, T4],
|
||||
Ap[func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], E, T5],
|
||||
Ap[func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], E, T6],
|
||||
Ap[T.Tuple7[T1, T2, T3, T4, T5, T6, T7], E, T7],
|
||||
t1,
|
||||
t2,
|
||||
t3,
|
||||
t4,
|
||||
t5,
|
||||
t6,
|
||||
t7,
|
||||
)
|
||||
}
|
||||
|
||||
// SequenceTuple7 converts a [Tuple7] of [Either[E, T]] into an [Either[E, Tuple7]].
|
||||
func SequenceTuple7[E, T1, T2, T3, T4, T5, T6, T7 any](t T.Tuple7[Either[E, T1], Either[E, T2], Either[E, T3], Either[E, T4], Either[E, T5], Either[E, T6], Either[E, T7]]) Either[E, T.Tuple7[T1, T2, T3, T4, T5, T6, T7]] {
|
||||
return A.SequenceTuple7(
|
||||
Map[E, T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]],
|
||||
Ap[func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], E, T2],
|
||||
Ap[func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], E, T3],
|
||||
Ap[func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], E, T4],
|
||||
Ap[func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], E, T5],
|
||||
Ap[func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], E, T6],
|
||||
Ap[T.Tuple7[T1, T2, T3, T4, T5, T6, T7], E, T7],
|
||||
t,
|
||||
)
|
||||
}
|
||||
|
||||
// TraverseTuple7 converts a [Tuple7] of [A] via transformation functions transforming [A] to [Either[E, A]] into a [Either[E, Tuple7]].
|
||||
func TraverseTuple7[F1 ~func(A1) Either[E, T1], F2 ~func(A2) Either[E, T2], F3 ~func(A3) Either[E, T3], F4 ~func(A4) Either[E, T4], F5 ~func(A5) Either[E, T5], F6 ~func(A6) Either[E, T6], F7 ~func(A7) Either[E, T7], E, A1, T1, A2, T2, A3, T3, A4, T4, A5, T5, A6, T6, A7, T7 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7) func (T.Tuple7[A1, A2, A3, A4, A5, A6, A7]) Either[E, T.Tuple7[T1, T2, T3, T4, T5, T6, T7]] {
|
||||
return func(t T.Tuple7[A1, A2, A3, A4, A5, A6, A7]) Either[E, T.Tuple7[T1, T2, T3, T4, T5, T6, T7]] {
|
||||
return A.TraverseTuple7(
|
||||
Map[E, T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]],
|
||||
Ap[func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], E, T2],
|
||||
Ap[func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], E, T3],
|
||||
Ap[func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], E, T4],
|
||||
Ap[func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], E, T5],
|
||||
Ap[func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], E, T6],
|
||||
Ap[T.Tuple7[T1, T2, T3, T4, T5, T6, T7], E, T7],
|
||||
f1,
|
||||
f2,
|
||||
f3,
|
||||
f4,
|
||||
f5,
|
||||
f6,
|
||||
f7,
|
||||
t,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Eitherize8 converts a function with 8 parameters returning a tuple into a function with 8 parameters returning an Either
|
||||
// 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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Uneitherize8 converts a function with 8 parameters returning an Either into a function with 8 parameters returning a tuple
|
||||
// The inverse function is [Eitherize8]
|
||||
func Uneitherize8[F ~func(T0, T1, T2, T3, T4, T5, T6, T7) Either[error, R], T0, T1, T2, T3, T4, T5, T6, T7, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7) (R, error) {
|
||||
return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7) (R, error) {
|
||||
return UnwrapError(f(t0, t1, t2, t3, t4, t5, t6, t7))
|
||||
}
|
||||
}
|
||||
|
||||
// SequenceT8 converts 8 parameters of [Either[E, T]] into a [Either[E, Tuple8]].
|
||||
func SequenceT8[E, T1, T2, T3, T4, T5, T6, T7, T8 any](t1 Either[E, T1], t2 Either[E, T2], t3 Either[E, T3], t4 Either[E, T4], t5 Either[E, T5], t6 Either[E, T6], t7 Either[E, T7], t8 Either[E, T8]) Either[E, T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] {
|
||||
return A.SequenceT8(
|
||||
Map[E, T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]],
|
||||
Ap[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], E, T2],
|
||||
Ap[func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], E, T3],
|
||||
Ap[func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], E, T4],
|
||||
Ap[func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], E, T5],
|
||||
Ap[func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], E, T6],
|
||||
Ap[func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], E, T7],
|
||||
Ap[T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], E, T8],
|
||||
t1,
|
||||
t2,
|
||||
t3,
|
||||
t4,
|
||||
t5,
|
||||
t6,
|
||||
t7,
|
||||
t8,
|
||||
)
|
||||
}
|
||||
|
||||
// SequenceTuple8 converts a [Tuple8] of [Either[E, T]] into an [Either[E, Tuple8]].
|
||||
func SequenceTuple8[E, T1, T2, T3, T4, T5, T6, T7, T8 any](t T.Tuple8[Either[E, T1], Either[E, T2], Either[E, T3], Either[E, T4], Either[E, T5], Either[E, T6], Either[E, T7], Either[E, T8]]) Either[E, T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] {
|
||||
return A.SequenceTuple8(
|
||||
Map[E, T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]],
|
||||
Ap[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], E, T2],
|
||||
Ap[func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], E, T3],
|
||||
Ap[func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], E, T4],
|
||||
Ap[func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], E, T5],
|
||||
Ap[func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], E, T6],
|
||||
Ap[func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], E, T7],
|
||||
Ap[T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], E, T8],
|
||||
t,
|
||||
)
|
||||
}
|
||||
|
||||
// TraverseTuple8 converts a [Tuple8] of [A] via transformation functions transforming [A] to [Either[E, A]] into a [Either[E, Tuple8]].
|
||||
func TraverseTuple8[F1 ~func(A1) Either[E, T1], F2 ~func(A2) Either[E, T2], F3 ~func(A3) Either[E, T3], F4 ~func(A4) Either[E, T4], F5 ~func(A5) Either[E, T5], F6 ~func(A6) Either[E, T6], F7 ~func(A7) Either[E, T7], F8 ~func(A8) Either[E, T8], E, A1, T1, A2, T2, A3, T3, A4, T4, A5, T5, A6, T6, A7, T7, A8, T8 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8) func (T.Tuple8[A1, A2, A3, A4, A5, A6, A7, A8]) Either[E, T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] {
|
||||
return func(t T.Tuple8[A1, A2, A3, A4, A5, A6, A7, A8]) Either[E, T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] {
|
||||
return A.TraverseTuple8(
|
||||
Map[E, T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]],
|
||||
Ap[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], E, T2],
|
||||
Ap[func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], E, T3],
|
||||
Ap[func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], E, T4],
|
||||
Ap[func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], E, T5],
|
||||
Ap[func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], E, T6],
|
||||
Ap[func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], E, T7],
|
||||
Ap[T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], E, T8],
|
||||
f1,
|
||||
f2,
|
||||
f3,
|
||||
f4,
|
||||
f5,
|
||||
f6,
|
||||
f7,
|
||||
f8,
|
||||
t,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Eitherize9 converts a function with 9 parameters returning a tuple into a function with 9 parameters returning an Either
|
||||
// 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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Uneitherize9 converts a function with 9 parameters returning an Either into a function with 9 parameters returning a tuple
|
||||
// The inverse function is [Eitherize9]
|
||||
func Uneitherize9[F ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8) Either[error, R], T0, T1, T2, T3, T4, T5, T6, T7, T8, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7, T8) (R, error) {
|
||||
return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8) (R, error) {
|
||||
return UnwrapError(f(t0, t1, t2, t3, t4, t5, t6, t7, t8))
|
||||
}
|
||||
}
|
||||
|
||||
// SequenceT9 converts 9 parameters of [Either[E, T]] into a [Either[E, Tuple9]].
|
||||
func SequenceT9[E, T1, T2, T3, T4, T5, T6, T7, T8, T9 any](t1 Either[E, T1], t2 Either[E, T2], t3 Either[E, T3], t4 Either[E, T4], t5 Either[E, T5], t6 Either[E, T6], t7 Either[E, T7], t8 Either[E, T8], t9 Either[E, T9]) Either[E, T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] {
|
||||
return A.SequenceT9(
|
||||
Map[E, T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]],
|
||||
Ap[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], E, T2],
|
||||
Ap[func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], E, T3],
|
||||
Ap[func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], E, T4],
|
||||
Ap[func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], E, T5],
|
||||
Ap[func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], E, T6],
|
||||
Ap[func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], E, T7],
|
||||
Ap[func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], E, T8],
|
||||
Ap[T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], E, T9],
|
||||
t1,
|
||||
t2,
|
||||
t3,
|
||||
t4,
|
||||
t5,
|
||||
t6,
|
||||
t7,
|
||||
t8,
|
||||
t9,
|
||||
)
|
||||
}
|
||||
|
||||
// SequenceTuple9 converts a [Tuple9] of [Either[E, T]] into an [Either[E, Tuple9]].
|
||||
func SequenceTuple9[E, T1, T2, T3, T4, T5, T6, T7, T8, T9 any](t T.Tuple9[Either[E, T1], Either[E, T2], Either[E, T3], Either[E, T4], Either[E, T5], Either[E, T6], Either[E, T7], Either[E, T8], Either[E, T9]]) Either[E, T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] {
|
||||
return A.SequenceTuple9(
|
||||
Map[E, T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]],
|
||||
Ap[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], E, T2],
|
||||
Ap[func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], E, T3],
|
||||
Ap[func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], E, T4],
|
||||
Ap[func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], E, T5],
|
||||
Ap[func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], E, T6],
|
||||
Ap[func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], E, T7],
|
||||
Ap[func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], E, T8],
|
||||
Ap[T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], E, T9],
|
||||
t,
|
||||
)
|
||||
}
|
||||
|
||||
// TraverseTuple9 converts a [Tuple9] of [A] via transformation functions transforming [A] to [Either[E, A]] into a [Either[E, Tuple9]].
|
||||
func TraverseTuple9[F1 ~func(A1) Either[E, T1], F2 ~func(A2) Either[E, T2], F3 ~func(A3) Either[E, T3], F4 ~func(A4) Either[E, T4], F5 ~func(A5) Either[E, T5], F6 ~func(A6) Either[E, T6], F7 ~func(A7) Either[E, T7], F8 ~func(A8) Either[E, T8], F9 ~func(A9) Either[E, T9], E, A1, T1, A2, T2, A3, T3, A4, T4, A5, T5, A6, T6, A7, T7, A8, T8, A9, T9 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9) func (T.Tuple9[A1, A2, A3, A4, A5, A6, A7, A8, A9]) Either[E, T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] {
|
||||
return func(t T.Tuple9[A1, A2, A3, A4, A5, A6, A7, A8, A9]) Either[E, T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] {
|
||||
return A.TraverseTuple9(
|
||||
Map[E, T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]],
|
||||
Ap[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], E, T2],
|
||||
Ap[func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], E, T3],
|
||||
Ap[func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], E, T4],
|
||||
Ap[func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], E, T5],
|
||||
Ap[func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], E, T6],
|
||||
Ap[func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], E, T7],
|
||||
Ap[func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], E, T8],
|
||||
Ap[T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], E, T9],
|
||||
f1,
|
||||
f2,
|
||||
f3,
|
||||
f4,
|
||||
f5,
|
||||
f6,
|
||||
f7,
|
||||
f8,
|
||||
f9,
|
||||
t,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Eitherize10 converts a function with 10 parameters returning a tuple into a function with 10 parameters returning an Either
|
||||
// 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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Uneitherize10 converts a function with 10 parameters returning an Either into a function with 10 parameters returning a tuple
|
||||
// The inverse function is [Eitherize10]
|
||||
func Uneitherize10[F ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) Either[error, R], 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) (R, error) {
|
||||
return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9) (R, error) {
|
||||
return UnwrapError(f(t0, t1, t2, t3, t4, t5, t6, t7, t8, t9))
|
||||
}
|
||||
}
|
||||
|
||||
// SequenceT10 converts 10 parameters of [Either[E, T]] into a [Either[E, Tuple10]].
|
||||
func SequenceT10[E, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any](t1 Either[E, T1], t2 Either[E, T2], t3 Either[E, T3], t4 Either[E, T4], t5 Either[E, T5], t6 Either[E, T6], t7 Either[E, T7], t8 Either[E, T8], t9 Either[E, T9], t10 Either[E, T10]) Either[E, T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] {
|
||||
return A.SequenceT10(
|
||||
Map[E, T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]],
|
||||
Ap[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], E, T2],
|
||||
Ap[func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], E, T3],
|
||||
Ap[func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], E, T4],
|
||||
Ap[func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], E, T5],
|
||||
Ap[func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], E, T6],
|
||||
Ap[func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], E, T7],
|
||||
Ap[func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], E, T8],
|
||||
Ap[func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], E, T9],
|
||||
Ap[T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], E, T10],
|
||||
t1,
|
||||
t2,
|
||||
t3,
|
||||
t4,
|
||||
t5,
|
||||
t6,
|
||||
t7,
|
||||
t8,
|
||||
t9,
|
||||
t10,
|
||||
)
|
||||
}
|
||||
|
||||
// SequenceTuple10 converts a [Tuple10] of [Either[E, T]] into an [Either[E, Tuple10]].
|
||||
func SequenceTuple10[E, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any](t T.Tuple10[Either[E, T1], Either[E, T2], Either[E, T3], Either[E, T4], Either[E, T5], Either[E, T6], Either[E, T7], Either[E, T8], Either[E, T9], Either[E, T10]]) Either[E, T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] {
|
||||
return A.SequenceTuple10(
|
||||
Map[E, T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]],
|
||||
Ap[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], E, T2],
|
||||
Ap[func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], E, T3],
|
||||
Ap[func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], E, T4],
|
||||
Ap[func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], E, T5],
|
||||
Ap[func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], E, T6],
|
||||
Ap[func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], E, T7],
|
||||
Ap[func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], E, T8],
|
||||
Ap[func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], E, T9],
|
||||
Ap[T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], E, T10],
|
||||
t,
|
||||
)
|
||||
}
|
||||
|
||||
// TraverseTuple10 converts a [Tuple10] of [A] via transformation functions transforming [A] to [Either[E, A]] into a [Either[E, Tuple10]].
|
||||
func TraverseTuple10[F1 ~func(A1) Either[E, T1], F2 ~func(A2) Either[E, T2], F3 ~func(A3) Either[E, T3], F4 ~func(A4) Either[E, T4], F5 ~func(A5) Either[E, T5], F6 ~func(A6) Either[E, T6], F7 ~func(A7) Either[E, T7], F8 ~func(A8) Either[E, T8], F9 ~func(A9) Either[E, T9], F10 ~func(A10) Either[E, T10], E, A1, T1, A2, T2, A3, T3, A4, T4, A5, T5, A6, T6, A7, T7, A8, T8, A9, T9, A10, T10 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10) func (T.Tuple10[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10]) Either[E, T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] {
|
||||
return func(t T.Tuple10[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10]) Either[E, T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] {
|
||||
return A.TraverseTuple10(
|
||||
Map[E, T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]],
|
||||
Ap[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], E, T2],
|
||||
Ap[func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], E, T3],
|
||||
Ap[func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], E, T4],
|
||||
Ap[func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], E, T5],
|
||||
Ap[func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], E, T6],
|
||||
Ap[func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], E, T7],
|
||||
Ap[func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], E, T8],
|
||||
Ap[func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], E, T9],
|
||||
Ap[T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], E, T10],
|
||||
f1,
|
||||
f2,
|
||||
f3,
|
||||
f4,
|
||||
f5,
|
||||
f6,
|
||||
f7,
|
||||
f8,
|
||||
f9,
|
||||
f10,
|
||||
t,
|
||||
)
|
||||
}
|
||||
}
|
36
either/http/request.go
Normal file
36
either/http/request.go
Normal file
@@ -0,0 +1,36 @@
|
||||
package Http
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net/http"
|
||||
|
||||
E "github.com/ibm/fp-go/either"
|
||||
)
|
||||
|
||||
var (
|
||||
PostRequest = bodyRequest("POST")
|
||||
PutRequest = bodyRequest("PUT")
|
||||
|
||||
GetRequest = noBodyRequest("GET")
|
||||
DeleteRequest = noBodyRequest("DELETE")
|
||||
OptionsRequest = noBodyRequest("OPTIONS")
|
||||
HeadRequest = noBodyRequest("HEAD")
|
||||
)
|
||||
|
||||
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))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
})
|
||||
}
|
||||
}
|
33
either/logger.go
Normal file
33
either/logger.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package either
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
F "github.com/ibm/fp-go/function"
|
||||
L "github.com/ibm/fp-go/logging"
|
||||
)
|
||||
|
||||
func _log[E, A any](left func(string, ...any), right func(string, ...any), prefix string) func(Either[E, A]) Either[E, A] {
|
||||
return Fold(
|
||||
func(e E) Either[E, A] {
|
||||
left("%s: %v", prefix, e)
|
||||
return Left[A, E](e)
|
||||
},
|
||||
func(a A) Either[E, A] {
|
||||
right("%s: %v", prefix, a)
|
||||
return Right[E](a)
|
||||
})
|
||||
}
|
||||
|
||||
func Logger[E, A any](loggers ...*log.Logger) func(string) func(Either[E, A]) Either[E, A] {
|
||||
left, right := L.LoggingCallbacks(loggers...)
|
||||
return func(prefix string) func(Either[E, A]) Either[E, A] {
|
||||
delegate := _log[E, A](left, right, prefix)
|
||||
return func(ma Either[E, A]) Either[E, A] {
|
||||
return F.Pipe1(
|
||||
delegate(ma),
|
||||
ChainTo[E, A](ma),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
22
either/logger_test.go
Normal file
22
either/logger_test.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package either
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
F "github.com/ibm/fp-go/function"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestLogger(t *testing.T) {
|
||||
|
||||
l := Logger[error, string]()
|
||||
|
||||
r := Right[error]("test")
|
||||
|
||||
res := F.Pipe1(
|
||||
r,
|
||||
l("out"),
|
||||
)
|
||||
|
||||
assert.Equal(t, r, res)
|
||||
}
|
30
either/record.go
Normal file
30
either/record.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package either
|
||||
|
||||
import (
|
||||
F "github.com/ibm/fp-go/function"
|
||||
RR "github.com/ibm/fp-go/internal/record"
|
||||
)
|
||||
|
||||
// TraverseRecord 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],
|
||||
Map[E, GB, func(B) GB],
|
||||
Ap[GB, E, B],
|
||||
f,
|
||||
)
|
||||
}
|
||||
|
||||
// TraverseRecord transforms a record of eithers into an either of a record
|
||||
func TraverseRecord[K comparable, E, A, B any](f func(A) Either[E, B]) func(map[K]A) Either[E, map[K]B] {
|
||||
return TraverseRecordG[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)
|
||||
}
|
||||
|
||||
// SequenceRecord converts a homogeneous sequence of either into an either of sequence
|
||||
func SequenceRecord[K comparable, E, A any](ma map[K]Either[E, A]) Either[E, map[K]A] {
|
||||
return SequenceRecordG[map[K]A](ma)
|
||||
}
|
29
either/resource.go
Normal file
29
either/resource.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package either
|
||||
|
||||
import (
|
||||
F "github.com/ibm/fp-go/function"
|
||||
)
|
||||
|
||||
// constructs a function that creates a resource, then operates on it and then releases the resource
|
||||
func WithResource[E, R, A any](onCreate func() Either[E, R], onRelease func(R) Either[E, any]) func(func(R) Either[E, A]) Either[E, A] {
|
||||
|
||||
return func(f func(R) Either[E, A]) Either[E, A] {
|
||||
return MonadChain(
|
||||
onCreate(), func(r R) Either[E, A] {
|
||||
// run the code and make sure to release as quickly as possible
|
||||
res := f(r)
|
||||
released := onRelease(r)
|
||||
// handle the errors
|
||||
return MonadFold(
|
||||
res,
|
||||
Left[A, E],
|
||||
func(a A) Either[E, A] {
|
||||
return F.Pipe1(
|
||||
released,
|
||||
MapTo[E, any](a),
|
||||
)
|
||||
})
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
39
either/resource_test.go
Normal file
39
either/resource_test.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package either
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
F "github.com/ibm/fp-go/function"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestWithResource(t *testing.T) {
|
||||
onCreate := func() Either[error, *os.File] {
|
||||
return TryCatchError(func() (*os.File, error) {
|
||||
return os.CreateTemp("", "*")
|
||||
})
|
||||
}
|
||||
onDelete := F.Flow2(
|
||||
func(f *os.File) Either[error, string] {
|
||||
return TryCatchError(func() (string, error) {
|
||||
return f.Name(), f.Close()
|
||||
})
|
||||
},
|
||||
Chain(func(name string) Either[error, any] {
|
||||
return TryCatchError(func() (any, error) {
|
||||
return name, os.Remove(name)
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
onHandler := func(f *os.File) Either[error, string] {
|
||||
return Of[error](f.Name())
|
||||
}
|
||||
|
||||
tempFile := WithResource[error, *os.File, string](onCreate, onDelete)
|
||||
|
||||
resE := tempFile(onHandler)
|
||||
|
||||
assert.True(t, IsRight(resE))
|
||||
}
|
60
either/testing/laws.go
Normal file
60
either/testing/laws.go
Normal file
@@ -0,0 +1,60 @@
|
||||
package testing
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
ET "github.com/ibm/fp-go/either"
|
||||
EQ "github.com/ibm/fp-go/eq"
|
||||
L "github.com/ibm/fp-go/internal/monad/testing"
|
||||
)
|
||||
|
||||
// AssertLaws asserts the apply monad laws for the `Either` monad
|
||||
func AssertLaws[E, A, B, C any](t *testing.T,
|
||||
eqe EQ.Eq[E],
|
||||
eqa EQ.Eq[A],
|
||||
eqb EQ.Eq[B],
|
||||
eqc EQ.Eq[C],
|
||||
|
||||
ab func(A) B,
|
||||
bc func(B) C,
|
||||
) func(a A) bool {
|
||||
|
||||
return L.AssertLaws(t,
|
||||
ET.Eq(eqe, eqa),
|
||||
ET.Eq(eqe, eqb),
|
||||
ET.Eq(eqe, eqc),
|
||||
|
||||
ET.Of[E, A],
|
||||
ET.Of[E, B],
|
||||
ET.Of[E, C],
|
||||
|
||||
ET.Of[E, func(A) A],
|
||||
ET.Of[E, func(A) B],
|
||||
ET.Of[E, func(B) C],
|
||||
ET.Of[E, func(func(A) B) B],
|
||||
|
||||
ET.MonadMap[E, A, A],
|
||||
ET.MonadMap[E, A, B],
|
||||
ET.MonadMap[E, A, C],
|
||||
ET.MonadMap[E, B, C],
|
||||
|
||||
ET.MonadMap[E, func(B) C, func(func(A) B) func(A) C],
|
||||
|
||||
ET.MonadChain[E, A, A],
|
||||
ET.MonadChain[E, A, B],
|
||||
ET.MonadChain[E, A, C],
|
||||
ET.MonadChain[E, B, C],
|
||||
|
||||
ET.MonadAp[A, E, A],
|
||||
ET.MonadAp[B, E, A],
|
||||
ET.MonadAp[C, E, B],
|
||||
ET.MonadAp[C, E, A],
|
||||
|
||||
ET.MonadAp[B, E, func(A) B],
|
||||
ET.MonadAp[func(A) C, E, func(A) B],
|
||||
|
||||
ab,
|
||||
bc,
|
||||
)
|
||||
|
||||
}
|
33
either/testing/laws_test.go
Normal file
33
either/testing/laws_test.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package testing
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
EQ "github.com/ibm/fp-go/eq"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestMonadLaws(t *testing.T) {
|
||||
// some comparison
|
||||
eqe := EQ.FromStrictEquals[string]()
|
||||
eqa := EQ.FromStrictEquals[bool]()
|
||||
eqb := EQ.FromStrictEquals[int]()
|
||||
eqc := EQ.FromStrictEquals[string]()
|
||||
|
||||
ab := func(a bool) int {
|
||||
if a {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
bc := func(b int) string {
|
||||
return fmt.Sprintf("value %d", b)
|
||||
}
|
||||
|
||||
laws := AssertLaws(t, eqe, eqa, eqb, eqc, ab, bc)
|
||||
|
||||
assert.True(t, laws(true))
|
||||
assert.True(t, laws(false))
|
||||
}
|
54
either/traverse.go
Normal file
54
either/traverse.go
Normal file
@@ -0,0 +1,54 @@
|
||||
package either
|
||||
|
||||
import (
|
||||
F "github.com/ibm/fp-go/function"
|
||||
)
|
||||
|
||||
/*
|
||||
*
|
||||
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<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(Either[E, A], func(A) HKTB) HKTRB {
|
||||
|
||||
left := F.Flow2(Left[B, E], _of)
|
||||
right := F.Bind2nd(_map, Right[E, B])
|
||||
|
||||
return func(ta Either[E, A], f func(A) HKTB) HKTRB {
|
||||
return MonadFold(ta,
|
||||
left,
|
||||
F.Flow2(f, right),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
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(func(A) HKTB) func(Either[E, A]) HKTRB {
|
||||
delegate := traverse[E, A, B, HKTA](_of, _map)
|
||||
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>
|
||||
*/
|
||||
func Sequence[E, A, HKTA, HKTRA any](
|
||||
_of func(Either[E, A]) HKTRA,
|
||||
_map func(HKTA, func(A) Either[E, A]) HKTRA,
|
||||
) func(Either[E, HKTA]) HKTRA {
|
||||
return Fold(F.Flow2(Left[A, E], _of), F.Bind2nd(_map, Right[E, A]))
|
||||
}
|
38
either/traverse_test.go
Normal file
38
either/traverse_test.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package either
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
F "github.com/ibm/fp-go/function"
|
||||
O "github.com/ibm/fp-go/option"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestTraverse(t *testing.T) {
|
||||
f := func(n int) O.Option[int] {
|
||||
if n >= 2 {
|
||||
return O.Of(n)
|
||||
}
|
||||
return O.None[int]()
|
||||
}
|
||||
trav := Traverse[string, int, int, O.Option[Either[string, int]]](
|
||||
O.Of[Either[string, int]],
|
||||
O.MonadMap[int, Either[string, int]],
|
||||
)(f)
|
||||
|
||||
assert.Equal(t, O.Of(Left[int]("a")), F.Pipe1(Left[int]("a"), trav))
|
||||
assert.Equal(t, O.None[Either[string, int]](), F.Pipe1(Right[string](1), trav))
|
||||
assert.Equal(t, O.Of(Right[string](3)), F.Pipe1(Right[string](3), trav))
|
||||
}
|
||||
|
||||
func TestSequence(t *testing.T) {
|
||||
|
||||
seq := Sequence(
|
||||
O.Of[Either[string, int]],
|
||||
O.MonadMap[int, Either[string, int]],
|
||||
)
|
||||
|
||||
assert.Equal(t, O.Of(Right[string](1)), seq(Right[string](O.Of(1))))
|
||||
assert.Equal(t, O.Of(Left[int]("a")), seq(Left[O.Option[int]]("a")))
|
||||
assert.Equal(t, O.None[Either[string, int]](), seq(Right[string](O.None[int]())))
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user