mirror of
https://github.com/IBM/fp-go.git
synced 2025-11-25 22:21:49 +02:00
fix: add ioeither
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
This commit is contained in:
@@ -3,21 +3,21 @@ package array
|
||||
import "github.com/ibm/fp-go/internal/array"
|
||||
|
||||
func Traverse[A, B, HKTB, HKTAB, HKTRB any](
|
||||
_of func([]B) HKTRB,
|
||||
_map func(HKTRB, func([]B) func(B) []B) HKTAB,
|
||||
_ap func(HKTAB, HKTB) HKTRB,
|
||||
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](_of, _map, _ap, f)
|
||||
return array.Traverse[[]A](fof, fmap, fap, f)
|
||||
}
|
||||
|
||||
func MonadTraverse[A, B, HKTB, HKTAB, HKTRB any](
|
||||
_of func([]B) HKTRB,
|
||||
_map func(HKTRB, func([]B) func(B) []B) HKTAB,
|
||||
_ap func(HKTAB, HKTB) HKTRB,
|
||||
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(_of, _map, _ap, ta, f)
|
||||
return array.MonadTraverse(fof, fmap, fap, ta, f)
|
||||
}
|
||||
|
||||
@@ -13,8 +13,8 @@ func TestTraverse(t *testing.T) {
|
||||
|
||||
traverse := Traverse(
|
||||
O.Of[ArrayType],
|
||||
O.MonadMap[ArrayType, func(int) ArrayType],
|
||||
O.MonadAp[ArrayType, int],
|
||||
O.Map[ArrayType, func(int) ArrayType],
|
||||
O.Ap[ArrayType, int],
|
||||
|
||||
func(n int) O.Option[int] {
|
||||
if n%2 == 0 {
|
||||
|
||||
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")})
|
||||
}
|
||||
127
cli/monad.go
Normal file
127
cli/monad.go
Normal file
@@ -0,0 +1,127 @@
|
||||
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")
|
||||
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,6 @@
|
||||
// 2023-07-14 13:19:40.5850892 +0200 CEST m=+0.008180901
|
||||
package either
|
||||
|
||||
|
||||
// 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] {
|
||||
|
||||
@@ -9,8 +9,8 @@ import (
|
||||
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],
|
||||
MonadMap[E, GB, func(B) GB],
|
||||
MonadAp[GB, E, B],
|
||||
Map[E, GB, func(B) GB],
|
||||
Ap[GB, E, B],
|
||||
f,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// This file was generated by robots at
|
||||
// 2023-07-14 13:19:42.9896471 +0200 CEST m=+0.009694501
|
||||
package function
|
||||
|
||||
// Combinations for a total of 1 arguments
|
||||
|
||||
// Bind1of1 takes a function with 1 parameters and returns a new function with 1 parameters that will bind these parameters to the positions [1] of the original function.
|
||||
@@ -20,6 +21,7 @@ func Ignore1of1[T1 any, F ~func() R, R any](f F) func(T1) R {
|
||||
return f()
|
||||
}
|
||||
}
|
||||
|
||||
// Combinations for a total of 2 arguments
|
||||
|
||||
// Bind1of2 takes a function with 2 parameters and returns a new function with 1 parameters that will bind these parameters to the positions [1] of the original function.
|
||||
@@ -72,6 +74,7 @@ func Ignore12of2[T1, T2 any, F ~func() R, R any](f F) func(T1, T2) R {
|
||||
return f()
|
||||
}
|
||||
}
|
||||
|
||||
// Combinations for a total of 3 arguments
|
||||
|
||||
// Bind1of3 takes a function with 3 parameters and returns a new function with 1 parameters that will bind these parameters to the positions [1] of the original function.
|
||||
@@ -192,6 +195,7 @@ func Ignore123of3[T1, T2, T3 any, F ~func() R, R any](f F) func(T1, T2, T3) R {
|
||||
return f()
|
||||
}
|
||||
}
|
||||
|
||||
// Combinations for a total of 4 arguments
|
||||
|
||||
// Bind1of4 takes a function with 4 parameters and returns a new function with 1 parameters that will bind these parameters to the positions [1] of the original function.
|
||||
|
||||
@@ -15,12 +15,14 @@ func Variadic0[V, R any](f func([]V) R) func(...V) R {
|
||||
return f(v)
|
||||
}
|
||||
}
|
||||
|
||||
// Unvariadic0 converts a function taking 0 parameters and a final variadic argument into a function with 0 parameters but a final slice argument
|
||||
func Unvariadic0[V, R any](f func(...V) R) func([]V) R {
|
||||
return func(v []V) R {
|
||||
return f(v...)
|
||||
}
|
||||
}
|
||||
|
||||
// Pipe1 takes an initial value t0 and successively applies 1 functions where the input of a function is the return value of the previous function
|
||||
// The final return value is the result of the last function application
|
||||
func Pipe1[F1 ~func(T0) T1, T0, T1 any](t0 T0, f1 F1) T1 {
|
||||
@@ -65,12 +67,14 @@ func Variadic1[T1, V, R any](f func(T1, []V) R) func(T1, ...V) R {
|
||||
return f(t1, v)
|
||||
}
|
||||
}
|
||||
|
||||
// Unvariadic1 converts a function taking 1 parameters and a final variadic argument into a function with 1 parameters but a final slice argument
|
||||
func Unvariadic1[T1, V, R any](f func(T1, ...V) R) func(T1, []V) R {
|
||||
return func(t1 T1, v []V) R {
|
||||
return f(t1, v...)
|
||||
}
|
||||
}
|
||||
|
||||
// Pipe2 takes an initial value t0 and successively applies 2 functions where the input of a function is the return value of the previous function
|
||||
// The final return value is the result of the last function application
|
||||
func Pipe2[F1 ~func(T0) T1, F2 ~func(T1) T2, T0, T1, T2 any](t0 T0, f1 F1, f2 F2) T2 {
|
||||
@@ -118,12 +122,14 @@ func Variadic2[T1, T2, V, R any](f func(T1, T2, []V) R) func(T1, T2, ...V) R {
|
||||
return f(t1, t2, v)
|
||||
}
|
||||
}
|
||||
|
||||
// Unvariadic2 converts a function taking 2 parameters and a final variadic argument into a function with 2 parameters but a final slice argument
|
||||
func Unvariadic2[T1, T2, V, R any](f func(T1, T2, ...V) R) func(T1, T2, []V) R {
|
||||
return func(t1 T1, t2 T2, v []V) R {
|
||||
return f(t1, t2, v...)
|
||||
}
|
||||
}
|
||||
|
||||
// Pipe3 takes an initial value t0 and successively applies 3 functions where the input of a function is the return value of the previous function
|
||||
// The final return value is the result of the last function application
|
||||
func Pipe3[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, T0, T1, T2, T3 any](t0 T0, f1 F1, f2 F2, f3 F3) T3 {
|
||||
@@ -174,12 +180,14 @@ func Variadic3[T1, T2, T3, V, R any](f func(T1, T2, T3, []V) R) func(T1, T2, T3,
|
||||
return f(t1, t2, t3, v)
|
||||
}
|
||||
}
|
||||
|
||||
// Unvariadic3 converts a function taking 3 parameters and a final variadic argument into a function with 3 parameters but a final slice argument
|
||||
func Unvariadic3[T1, T2, T3, V, R any](f func(T1, T2, T3, ...V) R) func(T1, T2, T3, []V) R {
|
||||
return func(t1 T1, t2 T2, t3 T3, v []V) R {
|
||||
return f(t1, t2, t3, v...)
|
||||
}
|
||||
}
|
||||
|
||||
// Pipe4 takes an initial value t0 and successively applies 4 functions where the input of a function is the return value of the previous function
|
||||
// The final return value is the result of the last function application
|
||||
func Pipe4[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, T0, T1, T2, T3, T4 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4) T4 {
|
||||
@@ -233,12 +241,14 @@ func Variadic4[T1, T2, T3, T4, V, R any](f func(T1, T2, T3, T4, []V) R) func(T1,
|
||||
return f(t1, t2, t3, t4, v)
|
||||
}
|
||||
}
|
||||
|
||||
// Unvariadic4 converts a function taking 4 parameters and a final variadic argument into a function with 4 parameters but a final slice argument
|
||||
func Unvariadic4[T1, T2, T3, T4, V, R any](f func(T1, T2, T3, T4, ...V) R) func(T1, T2, T3, T4, []V) R {
|
||||
return func(t1 T1, t2 T2, t3 T3, t4 T4, v []V) R {
|
||||
return f(t1, t2, t3, t4, v...)
|
||||
}
|
||||
}
|
||||
|
||||
// Pipe5 takes an initial value t0 and successively applies 5 functions where the input of a function is the return value of the previous function
|
||||
// The final return value is the result of the last function application
|
||||
func Pipe5[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, T0, T1, T2, T3, T4, T5 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5) T5 {
|
||||
@@ -295,12 +305,14 @@ func Variadic5[T1, T2, T3, T4, T5, V, R any](f func(T1, T2, T3, T4, T5, []V) R)
|
||||
return f(t1, t2, t3, t4, t5, v)
|
||||
}
|
||||
}
|
||||
|
||||
// Unvariadic5 converts a function taking 5 parameters and a final variadic argument into a function with 5 parameters but a final slice argument
|
||||
func Unvariadic5[T1, T2, T3, T4, T5, V, R any](f func(T1, T2, T3, T4, T5, ...V) R) func(T1, T2, T3, T4, T5, []V) R {
|
||||
return func(t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, v []V) R {
|
||||
return f(t1, t2, t3, t4, t5, v...)
|
||||
}
|
||||
}
|
||||
|
||||
// Pipe6 takes an initial value t0 and successively applies 6 functions where the input of a function is the return value of the previous function
|
||||
// The final return value is the result of the last function application
|
||||
func Pipe6[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, T0, T1, T2, T3, T4, T5, T6 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6) T6 {
|
||||
@@ -360,12 +372,14 @@ func Variadic6[T1, T2, T3, T4, T5, T6, V, R any](f func(T1, T2, T3, T4, T5, T6,
|
||||
return f(t1, t2, t3, t4, t5, t6, v)
|
||||
}
|
||||
}
|
||||
|
||||
// Unvariadic6 converts a function taking 6 parameters and a final variadic argument into a function with 6 parameters but a final slice argument
|
||||
func Unvariadic6[T1, T2, T3, T4, T5, T6, V, R any](f func(T1, T2, T3, T4, T5, T6, ...V) R) func(T1, T2, T3, T4, T5, T6, []V) R {
|
||||
return func(t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, v []V) R {
|
||||
return f(t1, t2, t3, t4, t5, t6, v...)
|
||||
}
|
||||
}
|
||||
|
||||
// Pipe7 takes an initial value t0 and successively applies 7 functions where the input of a function is the return value of the previous function
|
||||
// The final return value is the result of the last function application
|
||||
func Pipe7[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, T0, T1, T2, T3, T4, T5, T6, T7 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7) T7 {
|
||||
@@ -428,12 +442,14 @@ func Variadic7[T1, T2, T3, T4, T5, T6, T7, V, R any](f func(T1, T2, T3, T4, T5,
|
||||
return f(t1, t2, t3, t4, t5, t6, t7, v)
|
||||
}
|
||||
}
|
||||
|
||||
// Unvariadic7 converts a function taking 7 parameters and a final variadic argument into a function with 7 parameters but a final slice argument
|
||||
func Unvariadic7[T1, T2, T3, T4, T5, T6, T7, V, R any](f func(T1, T2, T3, T4, T5, T6, T7, ...V) R) func(T1, T2, T3, T4, T5, T6, T7, []V) R {
|
||||
return func(t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, v []V) R {
|
||||
return f(t1, t2, t3, t4, t5, t6, t7, v...)
|
||||
}
|
||||
}
|
||||
|
||||
// Pipe8 takes an initial value t0 and successively applies 8 functions where the input of a function is the return value of the previous function
|
||||
// The final return value is the result of the last function application
|
||||
func Pipe8[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, T0, T1, T2, T3, T4, T5, T6, T7, T8 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8) T8 {
|
||||
@@ -499,12 +515,14 @@ func Variadic8[T1, T2, T3, T4, T5, T6, T7, T8, V, R any](f func(T1, T2, T3, T4,
|
||||
return f(t1, t2, t3, t4, t5, t6, t7, t8, v)
|
||||
}
|
||||
}
|
||||
|
||||
// Unvariadic8 converts a function taking 8 parameters and a final variadic argument into a function with 8 parameters but a final slice argument
|
||||
func Unvariadic8[T1, T2, T3, T4, T5, T6, T7, T8, V, R any](f func(T1, T2, T3, T4, T5, T6, T7, T8, ...V) R) func(T1, T2, T3, T4, T5, T6, T7, T8, []V) R {
|
||||
return func(t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, v []V) R {
|
||||
return f(t1, t2, t3, t4, t5, t6, t7, t8, v...)
|
||||
}
|
||||
}
|
||||
|
||||
// Pipe9 takes an initial value t0 and successively applies 9 functions where the input of a function is the return value of the previous function
|
||||
// The final return value is the result of the last function application
|
||||
func Pipe9[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9) T9 {
|
||||
@@ -573,12 +591,14 @@ func Variadic9[T1, T2, T3, T4, T5, T6, T7, T8, T9, V, R any](f func(T1, T2, T3,
|
||||
return f(t1, t2, t3, t4, t5, t6, t7, t8, t9, v)
|
||||
}
|
||||
}
|
||||
|
||||
// Unvariadic9 converts a function taking 9 parameters and a final variadic argument into a function with 9 parameters but a final slice argument
|
||||
func Unvariadic9[T1, T2, T3, T4, T5, T6, T7, T8, T9, V, R any](f func(T1, T2, T3, T4, T5, T6, T7, T8, T9, ...V) R) func(T1, T2, T3, T4, T5, T6, T7, T8, T9, []V) R {
|
||||
return func(t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, v []V) R {
|
||||
return f(t1, t2, t3, t4, t5, t6, t7, t8, t9, v...)
|
||||
}
|
||||
}
|
||||
|
||||
// Pipe10 takes an initial value t0 and successively applies 10 functions where the input of a function is the return value of the previous function
|
||||
// The final return value is the result of the last function application
|
||||
func Pipe10[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10) T10 {
|
||||
@@ -650,12 +670,14 @@ func Variadic10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, V, R any](f func(T1, T2
|
||||
return f(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, v)
|
||||
}
|
||||
}
|
||||
|
||||
// Unvariadic10 converts a function taking 10 parameters and a final variadic argument into a function with 10 parameters but a final slice argument
|
||||
func Unvariadic10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, V, R any](f func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, ...V) R) func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, []V) R {
|
||||
return func(t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, v []V) R {
|
||||
return f(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, v...)
|
||||
}
|
||||
}
|
||||
|
||||
// Pipe11 takes an initial value t0 and successively applies 11 functions where the input of a function is the return value of the previous function
|
||||
// The final return value is the result of the last function application
|
||||
func Pipe11[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, F11 ~func(T10) T11, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11) T11 {
|
||||
@@ -730,12 +752,14 @@ func Variadic11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, V, R any](f func(T
|
||||
return f(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, v)
|
||||
}
|
||||
}
|
||||
|
||||
// Unvariadic11 converts a function taking 11 parameters and a final variadic argument into a function with 11 parameters but a final slice argument
|
||||
func Unvariadic11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, V, R any](f func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, ...V) R) func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, []V) R {
|
||||
return func(t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, v []V) R {
|
||||
return f(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, v...)
|
||||
}
|
||||
}
|
||||
|
||||
// Pipe12 takes an initial value t0 and successively applies 12 functions where the input of a function is the return value of the previous function
|
||||
// The final return value is the result of the last function application
|
||||
func Pipe12[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, F11 ~func(T10) T11, F12 ~func(T11) T12, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12) T12 {
|
||||
@@ -813,12 +837,14 @@ func Variadic12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, V, R any](f f
|
||||
return f(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, v)
|
||||
}
|
||||
}
|
||||
|
||||
// Unvariadic12 converts a function taking 12 parameters and a final variadic argument into a function with 12 parameters but a final slice argument
|
||||
func Unvariadic12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, V, R any](f func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, ...V) R) func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, []V) R {
|
||||
return func(t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12, v []V) R {
|
||||
return f(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, v...)
|
||||
}
|
||||
}
|
||||
|
||||
// Pipe13 takes an initial value t0 and successively applies 13 functions where the input of a function is the return value of the previous function
|
||||
// The final return value is the result of the last function application
|
||||
func Pipe13[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, F11 ~func(T10) T11, F12 ~func(T11) T12, F13 ~func(T12) T13, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12, f13 F13) T13 {
|
||||
@@ -899,12 +925,14 @@ func Variadic13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, V, R any
|
||||
return f(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, v)
|
||||
}
|
||||
}
|
||||
|
||||
// Unvariadic13 converts a function taking 13 parameters and a final variadic argument into a function with 13 parameters but a final slice argument
|
||||
func Unvariadic13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, V, R any](f func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, ...V) R) func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, []V) R {
|
||||
return func(t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12, t13 T13, v []V) R {
|
||||
return f(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, v...)
|
||||
}
|
||||
}
|
||||
|
||||
// Pipe14 takes an initial value t0 and successively applies 14 functions where the input of a function is the return value of the previous function
|
||||
// The final return value is the result of the last function application
|
||||
func Pipe14[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, F11 ~func(T10) T11, F12 ~func(T11) T12, F13 ~func(T12) T13, F14 ~func(T13) T14, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12, f13 F13, f14 F14) T14 {
|
||||
@@ -988,12 +1016,14 @@ func Variadic14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, V,
|
||||
return f(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, v)
|
||||
}
|
||||
}
|
||||
|
||||
// Unvariadic14 converts a function taking 14 parameters and a final variadic argument into a function with 14 parameters but a final slice argument
|
||||
func Unvariadic14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, V, R any](f func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, ...V) R) func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, []V) R {
|
||||
return func(t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12, t13 T13, t14 T14, v []V) R {
|
||||
return f(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, v...)
|
||||
}
|
||||
}
|
||||
|
||||
// Pipe15 takes an initial value t0 and successively applies 15 functions where the input of a function is the return value of the previous function
|
||||
// The final return value is the result of the last function application
|
||||
func Pipe15[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, F11 ~func(T10) T11, F12 ~func(T11) T12, F13 ~func(T12) T13, F14 ~func(T13) T14, F15 ~func(T14) T15, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12, f13 F13, f14 F14, f15 F15) T15 {
|
||||
@@ -1080,12 +1110,14 @@ func Variadic15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15
|
||||
return f(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, v)
|
||||
}
|
||||
}
|
||||
|
||||
// Unvariadic15 converts a function taking 15 parameters and a final variadic argument into a function with 15 parameters but a final slice argument
|
||||
func Unvariadic15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, V, R any](f func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, ...V) R) func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, []V) R {
|
||||
return func(t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12, t13 T13, t14 T14, t15 T15, v []V) R {
|
||||
return f(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, v...)
|
||||
}
|
||||
}
|
||||
|
||||
// Pipe16 takes an initial value t0 and successively applies 16 functions where the input of a function is the return value of the previous function
|
||||
// The final return value is the result of the last function application
|
||||
func Pipe16[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, F11 ~func(T10) T11, F12 ~func(T11) T12, F13 ~func(T12) T13, F14 ~func(T13) T14, F15 ~func(T14) T15, F16 ~func(T15) T16, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12, f13 F13, f14 F14, f15 F15, f16 F16) T16 {
|
||||
@@ -1175,12 +1207,14 @@ func Variadic16[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15
|
||||
return f(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, v)
|
||||
}
|
||||
}
|
||||
|
||||
// Unvariadic16 converts a function taking 16 parameters and a final variadic argument into a function with 16 parameters but a final slice argument
|
||||
func Unvariadic16[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, V, R any](f func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, ...V) R) func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, []V) R {
|
||||
return func(t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12, t13 T13, t14 T14, t15 T15, t16 T16, v []V) R {
|
||||
return f(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, v...)
|
||||
}
|
||||
}
|
||||
|
||||
// Pipe17 takes an initial value t0 and successively applies 17 functions where the input of a function is the return value of the previous function
|
||||
// The final return value is the result of the last function application
|
||||
func Pipe17[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, F11 ~func(T10) T11, F12 ~func(T11) T12, F13 ~func(T12) T13, F14 ~func(T13) T14, F15 ~func(T14) T15, F16 ~func(T15) T16, F17 ~func(T16) T17, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12, f13 F13, f14 F14, f15 F15, f16 F16, f17 F17) T17 {
|
||||
@@ -1273,12 +1307,14 @@ func Variadic17[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15
|
||||
return f(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, v)
|
||||
}
|
||||
}
|
||||
|
||||
// Unvariadic17 converts a function taking 17 parameters and a final variadic argument into a function with 17 parameters but a final slice argument
|
||||
func Unvariadic17[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, V, R any](f func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, ...V) R) func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, []V) R {
|
||||
return func(t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12, t13 T13, t14 T14, t15 T15, t16 T16, t17 T17, v []V) R {
|
||||
return f(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, v...)
|
||||
}
|
||||
}
|
||||
|
||||
// Pipe18 takes an initial value t0 and successively applies 18 functions where the input of a function is the return value of the previous function
|
||||
// The final return value is the result of the last function application
|
||||
func Pipe18[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, F11 ~func(T10) T11, F12 ~func(T11) T12, F13 ~func(T12) T13, F14 ~func(T13) T14, F15 ~func(T14) T15, F16 ~func(T15) T16, F17 ~func(T16) T17, F18 ~func(T17) T18, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12, f13 F13, f14 F14, f15 F15, f16 F16, f17 F17, f18 F18) T18 {
|
||||
@@ -1374,12 +1410,14 @@ func Variadic18[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15
|
||||
return f(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18, v)
|
||||
}
|
||||
}
|
||||
|
||||
// Unvariadic18 converts a function taking 18 parameters and a final variadic argument into a function with 18 parameters but a final slice argument
|
||||
func Unvariadic18[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, V, R any](f func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, ...V) R) func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, []V) R {
|
||||
return func(t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12, t13 T13, t14 T14, t15 T15, t16 T16, t17 T17, t18 T18, v []V) R {
|
||||
return f(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18, v...)
|
||||
}
|
||||
}
|
||||
|
||||
// Pipe19 takes an initial value t0 and successively applies 19 functions where the input of a function is the return value of the previous function
|
||||
// The final return value is the result of the last function application
|
||||
func Pipe19[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, F11 ~func(T10) T11, F12 ~func(T11) T12, F13 ~func(T12) T13, F14 ~func(T13) T14, F15 ~func(T14) T15, F16 ~func(T15) T16, F17 ~func(T16) T17, F18 ~func(T17) T18, F19 ~func(T18) T19, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12, f13 F13, f14 F14, f15 F15, f16 F16, f17 F17, f18 F18, f19 F19) T19 {
|
||||
@@ -1478,12 +1516,14 @@ func Variadic19[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15
|
||||
return f(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18, t19, v)
|
||||
}
|
||||
}
|
||||
|
||||
// Unvariadic19 converts a function taking 19 parameters and a final variadic argument into a function with 19 parameters but a final slice argument
|
||||
func Unvariadic19[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, V, R any](f func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, ...V) R) func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, []V) R {
|
||||
return func(t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12, t13 T13, t14 T14, t15 T15, t16 T16, t17 T17, t18 T18, t19 T19, v []V) R {
|
||||
return f(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18, t19, v...)
|
||||
}
|
||||
}
|
||||
|
||||
// Pipe20 takes an initial value t0 and successively applies 20 functions where the input of a function is the return value of the previous function
|
||||
// The final return value is the result of the last function application
|
||||
func Pipe20[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, F11 ~func(T10) T11, F12 ~func(T11) T12, F13 ~func(T12) T13, F14 ~func(T13) T14, F15 ~func(T14) T15, F16 ~func(T15) T16, F17 ~func(T16) T17, F18 ~func(T17) T18, F19 ~func(T18) T19, F20 ~func(T19) T20, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12, f13 F13, f14 F14, f15 F15, f16 F16, f17 F17, f18 F18, f19 F19, f20 F20) T20 {
|
||||
@@ -1585,6 +1625,7 @@ func Variadic20[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15
|
||||
return f(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18, t19, t20, v)
|
||||
}
|
||||
}
|
||||
|
||||
// Unvariadic20 converts a function taking 20 parameters and a final variadic argument into a function with 20 parameters but a final slice argument
|
||||
func Unvariadic20[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, V, R any](f func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, ...V) R) func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, []V) R {
|
||||
return func(t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12, t13 T13, t14 T14, t15 T15, t16 T16, t17 T17, t18 T18, t19 T19, t20 T20, v []V) R {
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// 2023-07-14 13:19:44.2638147 +0200 CEST m=+0.009515201
|
||||
package apply
|
||||
|
||||
|
||||
import (
|
||||
F "github.com/ibm/fp-go/function"
|
||||
T "github.com/ibm/fp-go/tuple"
|
||||
|
||||
@@ -13,31 +13,31 @@ HKTB = HKT<B>
|
||||
HKTAB = HKT<func(A)B>
|
||||
*/
|
||||
func MonadTraverse[GA ~[]A, GB ~[]B, A, B, HKTB, HKTAB, HKTRB any](
|
||||
_of func(GB) HKTRB,
|
||||
_map func(HKTRB, func(GB) func(B) GB) HKTAB,
|
||||
_ap func(HKTAB, HKTB) HKTRB,
|
||||
fof func(GB) HKTRB,
|
||||
fmap func(func(GB) func(B) GB) func(HKTRB) HKTAB,
|
||||
fap func(HKTB) func(HKTAB) HKTRB,
|
||||
|
||||
ta GA,
|
||||
f func(A) HKTB) HKTRB {
|
||||
return MonadTraverseReduce(_of, _map, _ap, ta, f, Append[GB, B], Empty[GB]())
|
||||
return MonadTraverseReduce(fof, fmap, fap, ta, f, Append[GB, B], Empty[GB]())
|
||||
}
|
||||
|
||||
func Traverse[GA ~[]A, GB ~[]B, A, B, HKTB, HKTAB, HKTRB any](
|
||||
_of func(GB) HKTRB,
|
||||
_map func(HKTRB, func(GB) func(B) GB) HKTAB,
|
||||
_ap func(HKTAB, HKTB) HKTRB,
|
||||
fof func(GB) HKTRB,
|
||||
fmap func(func(GB) func(B) GB) func(HKTRB) HKTAB,
|
||||
fap func(HKTB) func(HKTAB) HKTRB,
|
||||
|
||||
f func(A) HKTB) func(GA) HKTRB {
|
||||
|
||||
return func(ma GA) HKTRB {
|
||||
return MonadTraverse(_of, _map, _ap, ma, f)
|
||||
return MonadTraverse(fof, fmap, fap, ma, f)
|
||||
}
|
||||
}
|
||||
|
||||
func MonadTraverseReduce[GA ~[]A, GB, A, B, HKTB, HKTAB, HKTRB any](
|
||||
_of func(GB) HKTRB,
|
||||
_map func(HKTRB, func(GB) func(B) GB) HKTAB,
|
||||
_ap func(HKTAB, HKTB) HKTRB,
|
||||
fof func(GB) HKTRB,
|
||||
fmap func(func(GB) func(B) GB) func(HKTRB) HKTAB,
|
||||
fap func(HKTB) func(HKTAB) HKTRB,
|
||||
|
||||
ta GA,
|
||||
|
||||
@@ -45,26 +45,27 @@ func MonadTraverseReduce[GA ~[]A, GB, A, B, HKTB, HKTAB, HKTRB any](
|
||||
reduce func(GB, B) GB,
|
||||
initial GB,
|
||||
) HKTRB {
|
||||
mmap := F.Bind2nd(_map, F.Curry2(reduce))
|
||||
mmap := fmap(F.Curry2(reduce))
|
||||
|
||||
return Reduce(ta, func(r HKTRB, a A) HKTRB {
|
||||
return _ap(
|
||||
mmap(r),
|
||||
transform(a),
|
||||
return F.Pipe2(
|
||||
r,
|
||||
mmap,
|
||||
fap(transform(a)),
|
||||
)
|
||||
}, _of(initial))
|
||||
}, fof(initial))
|
||||
}
|
||||
|
||||
func TraverseReduce[GA ~[]A, GB, A, B, HKTB, HKTAB, HKTRB any](
|
||||
_of func(GB) HKTRB,
|
||||
_map func(HKTRB, func(GB) func(B) GB) HKTAB,
|
||||
_ap func(HKTAB, HKTB) HKTRB,
|
||||
fof func(GB) HKTRB,
|
||||
fmap func(func(GB) func(B) GB) func(HKTRB) HKTAB,
|
||||
fap func(HKTB) func(HKTAB) HKTRB,
|
||||
|
||||
transform func(A) HKTB,
|
||||
reduce func(GB, B) GB,
|
||||
initial GB,
|
||||
) func(GA) HKTRB {
|
||||
return func(ta GA) HKTRB {
|
||||
return MonadTraverseReduce(_of, _map, _ap, ta, transform, reduce, initial)
|
||||
return MonadTraverseReduce(fof, fmap, fap, ta, transform, reduce, initial)
|
||||
}
|
||||
}
|
||||
|
||||
108
internal/eithert/either.go
Normal file
108
internal/eithert/either.go
Normal file
@@ -0,0 +1,108 @@
|
||||
package eithert
|
||||
|
||||
import (
|
||||
ET "github.com/ibm/fp-go/either"
|
||||
F "github.com/ibm/fp-go/function"
|
||||
"github.com/ibm/fp-go/internal/apply"
|
||||
FC "github.com/ibm/fp-go/internal/functor"
|
||||
)
|
||||
|
||||
// HKTFA = HKT<F, Either<E, A>>
|
||||
// HKTFB = HKT<F, Either<E, B>>
|
||||
func MonadMap[E, A, B, HKTFA, HKTFB any](fmap func(HKTFA, func(ET.Either[E, A]) ET.Either[E, B]) HKTFB, fa HKTFA, f func(A) B) HKTFB {
|
||||
// HKTGA = Either[E, A]
|
||||
// HKTGB = Either[E, B]
|
||||
return FC.MonadMap(fmap, ET.MonadMap[E, A, B], fa, f)
|
||||
}
|
||||
|
||||
// HKTFA = HKT<F, Either<E, A>>
|
||||
// HKTFB = HKT<F, Either<E, B>>
|
||||
func MonadBiMap[E1, E2, A, B, HKTFA, HKTFB any](fmap func(HKTFA, func(ET.Either[E1, A]) ET.Either[E2, B]) HKTFB, fa HKTFA, f func(E1) E2, g func(A) B) HKTFB {
|
||||
// HKTGA = Either[E, A]
|
||||
// HKTGB = Either[E, B]
|
||||
return fmap(fa, ET.BiMap(f, g))
|
||||
}
|
||||
|
||||
// HKTFA = HKT<F, Either<E, A>>
|
||||
// HKTFB = HKT<F, Either<E, B>>
|
||||
func BiMap[E1, E2, A, B, HKTFA, HKTFB any](fmap func(HKTFA, func(ET.Either[E1, A]) ET.Either[E2, B]) HKTFB, f func(E1) E2, g func(A) B) func(HKTFA) HKTFB {
|
||||
// HKTGA = Either[E, A]
|
||||
// HKTGB = Either[E, B]
|
||||
return F.Bind2nd(fmap, ET.BiMap(f, g))
|
||||
}
|
||||
|
||||
// HKTFA = HKT<F, Either<E, A>>
|
||||
// HKTFB = HKT<F, Either<E, B>>
|
||||
func MonadChain[E, A, B, HKTFA, HKTFB any](
|
||||
fchain func(HKTFA, func(ET.Either[E, A]) HKTFB) HKTFB,
|
||||
fof func(ET.Either[E, B]) HKTFB,
|
||||
ma HKTFA,
|
||||
f func(A) HKTFB) HKTFB {
|
||||
// dispatch to the even more generic implementation
|
||||
return fchain(ma, ET.Fold(F.Flow2(ET.Left[B, E], fof), f))
|
||||
}
|
||||
|
||||
// func(fa func(R) T.Task[ET.Either[E, func(A) B]], f func(ET.Either[E, func(A) B]) func(ET.Either[E, A]) ET.Either[E, B]) GEFAB
|
||||
|
||||
// HKTFA = HKT[Either[E, A]]
|
||||
// HKTFB = HKT[Either[E, B]]
|
||||
// HKTFAB = HKT[Either[E, func(A)B]]
|
||||
func MonadAp[E, A, B, HKTFAB, HKTFGAB, HKTFA, HKTFB any](
|
||||
fap func(HKTFGAB, HKTFA) HKTFB,
|
||||
fmap func(HKTFAB, func(ET.Either[E, func(A) B]) func(ET.Either[E, A]) ET.Either[E, B]) HKTFGAB,
|
||||
fab HKTFAB,
|
||||
fa HKTFA) HKTFB {
|
||||
// HKTGA = ET.Either[E, A]
|
||||
// HKTGB = ET.Either[E, B]
|
||||
// HKTGAB = ET.Either[E, func(a A) B]
|
||||
return apply.MonadAp(fap, fmap, ET.MonadAp[B, E, A], fab, fa)
|
||||
}
|
||||
|
||||
func Right[E, A, HKTA any](fof func(ET.Either[E, A]) HKTA, a A) HKTA {
|
||||
return F.Pipe2(a, ET.Right[E, A], fof)
|
||||
}
|
||||
|
||||
func Left[E, A, HKTA any](fof func(ET.Either[E, A]) HKTA, e E) HKTA {
|
||||
return F.Pipe2(e, ET.Left[A, E], fof)
|
||||
}
|
||||
|
||||
// HKTA = HKT[A]
|
||||
// HKTEA = HKT[Either[E, A]]
|
||||
func RightF[E, A, HKTA, HKTEA any](fmap func(HKTA, func(A) ET.Either[E, A]) HKTEA, fa HKTA) HKTEA {
|
||||
return fmap(fa, ET.Right[E, A])
|
||||
}
|
||||
|
||||
// HKTE = HKT[E]
|
||||
// HKTEA = HKT[Either[E, A]]
|
||||
func LeftF[E, A, HKTE, HKTEA any](fmap func(HKTE, func(E) ET.Either[E, A]) HKTEA, fe HKTE) HKTEA {
|
||||
return fmap(fe, ET.Left[A, E])
|
||||
}
|
||||
|
||||
func FoldE[E, A, HKTEA, HKTB any](mchain func(HKTEA, func(ET.Either[E, A]) HKTB) HKTB, ma HKTEA, onLeft func(E) HKTB, onRight func(A) HKTB) HKTB {
|
||||
return mchain(ma, ET.Fold(onLeft, onRight))
|
||||
}
|
||||
|
||||
func MatchE[E, A, HKTEA, HKTB any](mchain func(HKTEA, func(ET.Either[E, A]) HKTB) HKTB, onLeft func(E) HKTB, onRight func(A) HKTB) func(HKTEA) HKTB {
|
||||
return F.Bind2nd(mchain, ET.Fold(onLeft, onRight))
|
||||
}
|
||||
|
||||
func GetOrElse[E, A, HKTEA, HKTA any](mchain func(HKTEA, func(ET.Either[E, A]) HKTA) HKTA, mof func(A) HKTA, onLeft func(E) HKTA) func(HKTEA) HKTA {
|
||||
return MatchE(mchain, onLeft, mof)
|
||||
}
|
||||
|
||||
func OrElse[E1, E2, A, HKTE1A, HKTE2A any](mchain func(HKTE1A, func(ET.Either[E1, A]) HKTE2A) HKTE2A, mof func(ET.Either[E2, A]) HKTE2A, onLeft func(E1) HKTE2A) func(HKTE1A) HKTE2A {
|
||||
return MatchE(mchain, onLeft, F.Flow2(ET.Right[E2, A], mof))
|
||||
}
|
||||
|
||||
func OrLeft[E1, E2, A, HKTE1A, HKTE2, HKTE2A any](
|
||||
mchain func(HKTE1A, func(ET.Either[E1, A]) HKTE2A) HKTE2A,
|
||||
mmap func(HKTE2, func(E2) ET.Either[E2, A]) HKTE2A,
|
||||
mof func(ET.Either[E2, A]) HKTE2A,
|
||||
onLeft func(E1) HKTE2) func(HKTE1A) HKTE2A {
|
||||
|
||||
return F.Bind2nd(mchain, ET.Fold(F.Flow2(onLeft, F.Bind2nd(mmap, ET.Left[A, E2])), F.Flow2(ET.Right[E2, A], mof)))
|
||||
}
|
||||
|
||||
func MonadMapLeft[E, A, B, HKTFA, HKTFB any](fmap func(HKTFA, func(ET.Either[E, A]) ET.Either[B, A]) HKTFB, fa HKTFA, f func(E) B) HKTFB {
|
||||
return FC.MonadMap(fmap, ET.MonadMapLeft[E, A, B], fa, f)
|
||||
}
|
||||
19
internal/eq/eq.go
Normal file
19
internal/eq/eq.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package eq
|
||||
|
||||
import (
|
||||
EQ "github.com/ibm/fp-go/eq"
|
||||
F "github.com/ibm/fp-go/function"
|
||||
)
|
||||
|
||||
// Eq implements an equals predicate on the basis of `map` and `ap`
|
||||
func Eq[HKTA, HKTABOOL, HKTBOOL, A any](
|
||||
fmap func(HKTA, func(A) func(A) bool) HKTABOOL,
|
||||
fap func(HKTABOOL, HKTA) HKTBOOL,
|
||||
|
||||
e EQ.Eq[A],
|
||||
) func(l, r HKTA) HKTBOOL {
|
||||
c := F.Curry2(e.Equals)
|
||||
return func(fl, fr HKTA) HKTBOOL {
|
||||
return fap(fmap(fl, c), fr)
|
||||
}
|
||||
}
|
||||
37
internal/file/bracket.go
Normal file
37
internal/file/bracket.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package file
|
||||
|
||||
import (
|
||||
F "github.com/ibm/fp-go/function"
|
||||
)
|
||||
|
||||
// Bracket makes sure that a resource is cleaned up in the event of an error. The release action is called regardless of
|
||||
// whether the body action returns and error or not.
|
||||
func Bracket[
|
||||
GA, // IOEither[E, A]
|
||||
GB, // IOEither[E, A]
|
||||
GANY, // IOEither[E, ANY]
|
||||
|
||||
EB, // Either[E, B]
|
||||
|
||||
A, B, ANY any](
|
||||
|
||||
ofeb func(EB) GB,
|
||||
|
||||
chainab func(GA, func(A) GB) GB,
|
||||
chainebb func(GB, func(EB) GB) GB,
|
||||
chainany func(GANY, func(ANY) GB) GB,
|
||||
|
||||
acquire GA,
|
||||
use func(A) GB,
|
||||
release func(A, EB) GANY,
|
||||
) GB {
|
||||
return chainab(acquire,
|
||||
func(a A) GB {
|
||||
return chainebb(use(a), func(eb EB) GB {
|
||||
return chainany(
|
||||
release(a, eb),
|
||||
F.Constant1[ANY](ofeb(eb)),
|
||||
)
|
||||
})
|
||||
})
|
||||
}
|
||||
39
internal/file/file.go
Normal file
39
internal/file/file.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package file
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"io"
|
||||
|
||||
E "github.com/ibm/fp-go/either"
|
||||
)
|
||||
|
||||
type (
|
||||
readerWithContext struct {
|
||||
ctx context.Context
|
||||
delegate io.Reader
|
||||
}
|
||||
)
|
||||
|
||||
func (rdr *readerWithContext) Read(p []byte) (int, error) {
|
||||
// check for cancellarion
|
||||
if err := rdr.ctx.Err(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
// simply dispatch
|
||||
return rdr.delegate.Read(p)
|
||||
}
|
||||
|
||||
// MakeReader creates a context aware reader
|
||||
func MakeReader(ctx context.Context, rdr io.Reader) io.Reader {
|
||||
return &readerWithContext{ctx, rdr}
|
||||
}
|
||||
|
||||
// ReadAll reads the content of a reader and allows it to be canceled
|
||||
func ReadAll(ctx context.Context, rdr io.Reader) E.Either[error, []byte] {
|
||||
return E.TryCatchError(func() ([]byte, error) {
|
||||
var buffer bytes.Buffer
|
||||
_, err := io.Copy(&buffer, MakeReader(ctx, rdr))
|
||||
return buffer.Bytes(), err
|
||||
})
|
||||
}
|
||||
49
internal/file/resource.go
Normal file
49
internal/file/resource.go
Normal file
@@ -0,0 +1,49 @@
|
||||
package file
|
||||
|
||||
import (
|
||||
F "github.com/ibm/fp-go/function"
|
||||
)
|
||||
|
||||
// WithResource constructs a function that creates a resource, then operates on it and then releases the resource
|
||||
func WithResource[
|
||||
GA,
|
||||
GR,
|
||||
GANY,
|
||||
E, R, A, ANY any](
|
||||
mchain func(GR, func(R) GA) GA,
|
||||
mfold1 func(GA, func(E) GA, func(A) GA) GA,
|
||||
mfold2 func(GANY, func(E) GA, func(ANY) GA) GA,
|
||||
mmap func(GANY, func(ANY) A) GA,
|
||||
left func(E) GA,
|
||||
) func(onCreate func() GR, onRelease func(R) GANY) func(func(R) GA) GA {
|
||||
|
||||
return func(onCreate func() GR, onRelease func(R) GANY) func(func(R) GA) GA {
|
||||
|
||||
return func(f func(R) GA) GA {
|
||||
return mchain(
|
||||
onCreate(), func(r R) GA {
|
||||
// handle errors
|
||||
return mfold1(
|
||||
f(r),
|
||||
func(e E) GA {
|
||||
// the original error
|
||||
err := left(e)
|
||||
// if resource processing produced and error, still release the resource but return the first error
|
||||
return mfold2(
|
||||
onRelease(r),
|
||||
F.Constant1[E](err),
|
||||
F.Constant1[ANY](err),
|
||||
)
|
||||
},
|
||||
func(a A) GA {
|
||||
// if resource processing succeeded, release the resource. If this fails return failure, else the original error
|
||||
return F.Pipe1(
|
||||
onRelease(r),
|
||||
F.Bind2nd(mmap, F.Constant1[ANY](a)),
|
||||
)
|
||||
})
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
53
internal/fromeither/either.go
Normal file
53
internal/fromeither/either.go
Normal file
@@ -0,0 +1,53 @@
|
||||
package fromeither
|
||||
|
||||
import (
|
||||
ET "github.com/ibm/fp-go/either"
|
||||
F "github.com/ibm/fp-go/function"
|
||||
O "github.com/ibm/fp-go/option"
|
||||
)
|
||||
|
||||
func FromOption[E, A, HKTEA any](fromEither func(ET.Either[E, A]) HKTEA, onNone func() E) func(ma O.Option[A]) HKTEA {
|
||||
return F.Flow2(ET.FromOption[E, A](onNone), fromEither)
|
||||
}
|
||||
|
||||
func FromPredicate[E, A, HKTEA any](fromEither func(ET.Either[E, A]) HKTEA, pred func(A) bool, onFalse func(A) E) func(A) HKTEA {
|
||||
return F.Flow2(ET.FromPredicate(pred, onFalse), fromEither)
|
||||
}
|
||||
|
||||
func MonadFromOption[E, A, HKTEA any](
|
||||
fromEither func(ET.Either[E, A]) HKTEA,
|
||||
onNone func() E,
|
||||
ma O.Option[A],
|
||||
) HKTEA {
|
||||
return F.Pipe1(
|
||||
O.MonadFold(
|
||||
ma,
|
||||
F.Nullary2(onNone, ET.Left[A, E]),
|
||||
ET.Right[E, A],
|
||||
),
|
||||
fromEither,
|
||||
)
|
||||
}
|
||||
|
||||
func FromOptionK[E, A, B, HKTEB any](
|
||||
fromEither func(ET.Either[E, B]) HKTEB,
|
||||
onNone func() E) func(f func(A) O.Option[B]) func(A) HKTEB {
|
||||
// helper
|
||||
return F.Bind2nd(F.Flow2[func(A) O.Option[B], func(O.Option[B]) HKTEB, A, O.Option[B], HKTEB], FromOption(fromEither, onNone))
|
||||
}
|
||||
|
||||
func MonadChainEitherK[A, E, B, HKTEA, HKTEB any](
|
||||
mchain func(HKTEA, func(A) HKTEB) HKTEB,
|
||||
fromEither func(ET.Either[E, B]) HKTEB,
|
||||
ma HKTEA,
|
||||
f func(A) ET.Either[E, B]) HKTEB {
|
||||
return mchain(ma, F.Flow2(f, fromEither))
|
||||
}
|
||||
|
||||
func ChainOptionK[A, E, B, HKTEA, HKTEB any](
|
||||
mchain func(HKTEA, func(A) HKTEB) HKTEB,
|
||||
fromEither func(ET.Either[E, B]) HKTEB,
|
||||
onNone func() E,
|
||||
) func(f func(A) O.Option[B]) func(ma HKTEA) HKTEB {
|
||||
return F.Flow2(FromOptionK[E, A](fromEither, onNone), F.Bind1st(F.Bind2nd[HKTEA, func(A) HKTEB, HKTEB], mchain))
|
||||
}
|
||||
40
internal/fromio/io.go
Normal file
40
internal/fromio/io.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package fromio
|
||||
|
||||
import (
|
||||
F "github.com/ibm/fp-go/function"
|
||||
C "github.com/ibm/fp-go/internal/chain"
|
||||
)
|
||||
|
||||
func MonadChainFirstIOK[A, B, HKTA, HKTB any, GIOB ~func() B](
|
||||
mchain func(HKTA, func(A) HKTA) HKTA,
|
||||
mmap func(HKTB, func(B) A) HKTA,
|
||||
fromio func(GIOB) HKTB,
|
||||
first HKTA, f func(A) GIOB) HKTA {
|
||||
// chain
|
||||
return C.MonadChainFirst(mchain, mmap, first, F.Flow2(f, fromio))
|
||||
}
|
||||
|
||||
func ChainFirstIOK[A, B, HKTA, HKTB any, GIOB ~func() B](
|
||||
mchain func(HKTA, func(A) HKTA) HKTA,
|
||||
mmap func(HKTB, func(B) A) HKTA,
|
||||
fromio func(GIOB) HKTB,
|
||||
f func(A) GIOB) func(HKTA) HKTA {
|
||||
// chain
|
||||
return C.ChainFirst(mchain, mmap, F.Flow2(f, fromio))
|
||||
}
|
||||
|
||||
func MonadChainIOK[GR ~func() B, A, B, HKTA, HKTB any](
|
||||
mchain func(HKTA, func(A) HKTB) HKTB,
|
||||
fromio func(GR) HKTB,
|
||||
first HKTA, f func(A) GR) HKTB {
|
||||
// chain
|
||||
return C.MonadChain[A, B](mchain, first, F.Flow2(f, fromio))
|
||||
}
|
||||
|
||||
func ChainIOK[GR ~func() B, A, B, HKTA, HKTB any](
|
||||
mchain func(HKTA, func(A) HKTB) HKTB,
|
||||
fromio func(GR) HKTB,
|
||||
f func(A) GR) func(HKTA) HKTB {
|
||||
// chain
|
||||
return C.Chain[A, B](mchain, F.Flow2(f, fromio))
|
||||
}
|
||||
11
internal/functor/functor.go
Normal file
11
internal/functor/functor.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package functor
|
||||
|
||||
import (
|
||||
F "github.com/ibm/fp-go/function"
|
||||
)
|
||||
|
||||
// HKTFGA = HKT[F, HKT[G, A]]
|
||||
// HKTFGB = HKT[F, HKT[G, B]]
|
||||
func MonadMap[A, B, HKTGA, HKTGB, HKTFGA, HKTFGB any](fmap func(HKTFGA, func(HKTGA) HKTGB) HKTFGB, gmap func(HKTGA, func(A) B) HKTGB, fa HKTFGA, f func(A) B) HKTFGB {
|
||||
return fmap(fa, F.Bind2nd(gmap, f))
|
||||
}
|
||||
@@ -27,40 +27,41 @@ HKTB = HKT<B>
|
||||
HKTAB = HKT<func(A)B>
|
||||
*/
|
||||
func traverseWithIndex[MA ~map[K]A, MB ~map[K]B, K comparable, A, B, HKTB, HKTAB, HKTRB any](
|
||||
_of func(MB) HKTRB,
|
||||
_map func(HKTRB, func(MB) func(B) MB) HKTAB,
|
||||
_ap func(HKTAB, HKTB) HKTRB,
|
||||
fof func(MB) HKTRB,
|
||||
fmap func(func(MB) func(B) MB) func(HKTRB) HKTAB,
|
||||
fap func(HKTB) func(HKTAB) HKTRB,
|
||||
|
||||
ta MA, f func(K, A) HKTB) HKTRB {
|
||||
// this function inserts a value into a map with a given key
|
||||
cb := F.Curry3(addKey[MB, K, B])
|
||||
mmap := F.Flow2(F.Curry3(addKey[MB, K, B]), fmap)
|
||||
|
||||
return ReduceWithIndex(ta, func(k K, r HKTRB, a A) HKTRB {
|
||||
return _ap(
|
||||
_map(r, cb(k)),
|
||||
f(k, a),
|
||||
return F.Pipe2(
|
||||
r,
|
||||
mmap(k),
|
||||
fap(f(k, a)),
|
||||
)
|
||||
}, _of(createEmpty[MB]()))
|
||||
}, fof(createEmpty[MB]()))
|
||||
}
|
||||
|
||||
func MonadTraverse[MA ~map[K]A, MB ~map[K]B, K comparable, A, B, HKTB, HKTAB, HKTRB any](
|
||||
_of func(MB) HKTRB,
|
||||
_map func(HKTRB, func(MB) func(B) MB) HKTAB,
|
||||
_ap func(HKTAB, HKTB) HKTRB,
|
||||
fof func(MB) HKTRB,
|
||||
fmap func(func(MB) func(B) MB) func(HKTRB) HKTAB,
|
||||
fap func(HKTB) func(HKTAB) HKTRB,
|
||||
|
||||
r MA, f func(A) HKTB) HKTRB {
|
||||
return traverseWithIndex(_of, _map, _ap, r, F.Ignore1of2[K](f))
|
||||
return traverseWithIndex(fof, fmap, fap, r, F.Ignore1of2[K](f))
|
||||
}
|
||||
|
||||
func TraverseWithIndex[MA ~map[K]A, MB ~map[K]B, K comparable, A, B, HKTB, HKTAB, HKTRB any](
|
||||
_of func(MB) HKTRB,
|
||||
_map func(HKTRB, func(MB) func(B) MB) HKTAB,
|
||||
_ap func(HKTAB, HKTB) HKTRB,
|
||||
fof func(MB) HKTRB,
|
||||
fmap func(func(MB) func(B) MB) func(HKTRB) HKTAB,
|
||||
fap func(HKTB) func(HKTAB) HKTRB,
|
||||
|
||||
f func(K, A) HKTB) func(MA) HKTRB {
|
||||
|
||||
return func(ma MA) HKTRB {
|
||||
return traverseWithIndex(_of, _map, _ap, ma, f)
|
||||
return traverseWithIndex(fof, fmap, fap, ma, f)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,14 +70,14 @@ func TraverseWithIndex[MA ~map[K]A, MB ~map[K]B, K comparable, A, B, HKTB, HKTAB
|
||||
// HKTAB = HKT<func(A)B>
|
||||
// HKTRB = HKT<MB>
|
||||
func Traverse[MA ~map[K]A, MB ~map[K]B, K comparable, A, B, HKTB, HKTAB, HKTRB any](
|
||||
_of func(MB) HKTRB,
|
||||
_map func(HKTRB, func(MB) func(B) MB) HKTAB,
|
||||
_ap func(HKTAB, HKTB) HKTRB,
|
||||
fof func(MB) HKTRB,
|
||||
fmap func(func(MB) func(B) MB) func(HKTRB) HKTAB,
|
||||
fap func(HKTB) func(HKTAB) HKTRB,
|
||||
|
||||
f func(A) HKTB) func(MA) HKTRB {
|
||||
|
||||
return func(ma MA) HKTRB {
|
||||
return MonadTraverse(_of, _map, _ap, ma, f)
|
||||
return MonadTraverse(fof, fmap, fap, ma, f)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,10 +85,10 @@ func Traverse[MA ~map[K]A, MB ~map[K]B, K comparable, A, B, HKTB, HKTAB, HKTRB a
|
||||
// HKTAA = HKT[func(A)MA]
|
||||
// HKTRA = HKT[MA]
|
||||
func Sequence[MA ~map[K]A, MKTA ~map[K]HKTA, K comparable, A, HKTA, HKTAA, HKTRA any](
|
||||
_of func(MA) HKTRA,
|
||||
_map func(HKTRA, func(MA) func(A) MA) HKTAA,
|
||||
_ap func(HKTAA, HKTA) HKTRA,
|
||||
fof func(MA) HKTRA,
|
||||
fmap func(func(MA) func(A) MA) func(HKTRA) HKTAA,
|
||||
fap func(HKTA) func(HKTAA) HKTRA,
|
||||
|
||||
ma MKTA) HKTRA {
|
||||
return MonadTraverse(_of, _map, _ap, ma, F.Identity[HKTA])
|
||||
return MonadTraverse(fof, fmap, fap, ma, F.Identity[HKTA])
|
||||
}
|
||||
|
||||
15
io/apply.go
Normal file
15
io/apply.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package io
|
||||
|
||||
import (
|
||||
G "github.com/ibm/fp-go/io/generic"
|
||||
M "github.com/ibm/fp-go/monoid"
|
||||
S "github.com/ibm/fp-go/semigroup"
|
||||
)
|
||||
|
||||
func ApplySemigroup[A any](s S.Semigroup[A]) S.Semigroup[IO[A]] {
|
||||
return G.ApplySemigroup[IO[A]](s)
|
||||
}
|
||||
|
||||
func ApplicativeMonoid[A any](m M.Monoid[A]) M.Monoid[IO[A]] {
|
||||
return G.ApplicativeMonoid[IO[A]](m)
|
||||
}
|
||||
11
io/eq.go
Normal file
11
io/eq.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package io
|
||||
|
||||
import (
|
||||
EQ "github.com/ibm/fp-go/eq"
|
||||
G "github.com/ibm/fp-go/io/generic"
|
||||
)
|
||||
|
||||
// Eq implements the equals predicate for values contained in the IO monad
|
||||
func Eq[A any](e EQ.Eq[A]) EQ.Eq[IO[A]] {
|
||||
return G.Eq[IO[A]](e)
|
||||
}
|
||||
81
io/generic/ap.go
Normal file
81
io/generic/ap.go
Normal file
@@ -0,0 +1,81 @@
|
||||
package generic
|
||||
|
||||
import (
|
||||
G "github.com/ibm/fp-go/internal/apply"
|
||||
)
|
||||
|
||||
const (
|
||||
// useParallel is the feature flag to control if we use the parallel or the sequential implementation of ap
|
||||
useParallel = true
|
||||
)
|
||||
|
||||
// monadApSeq implements the applicative on a single thread by first executing mab and the ma
|
||||
func monadApSeq[GA ~func() A, GB ~func() B, GAB ~func() func(A) B, A, B any](mab GAB, ma GA) GB {
|
||||
return MakeIO[GB](func() B {
|
||||
return mab()(ma())
|
||||
})
|
||||
}
|
||||
|
||||
// monadApPar implements the applicative on two threads, the main thread executes mab and the actuall
|
||||
// apply operation and the second thred computes ma. Communication between the threads happens via a channel
|
||||
func monadApPar[GA ~func() A, GB ~func() B, GAB ~func() func(A) B, A, B any](mab GAB, ma GA) GB {
|
||||
return MakeIO[GB](func() B {
|
||||
c := make(chan A)
|
||||
go func() {
|
||||
c <- ma()
|
||||
close(c)
|
||||
}()
|
||||
return mab()(<-c)
|
||||
})
|
||||
}
|
||||
|
||||
// MonadAp implements the `ap` operation. Depending on a feature flag this will be sequential or parallel, the preferred implementation
|
||||
// is parallel
|
||||
func MonadAp[GA ~func() A, GB ~func() B, GAB ~func() func(A) B, A, B any](mab GAB, ma GA) GB {
|
||||
if useParallel {
|
||||
return monadApPar[GA, GB](mab, ma)
|
||||
}
|
||||
return monadApSeq[GA, GB](mab, ma)
|
||||
}
|
||||
|
||||
// MonadApFirst combines two effectful actions, keeping only the result of the first.
|
||||
func MonadApFirst[GA ~func() A, GB ~func() B, GBA ~func() func(B) A, A, B any](first GA, second GB) GA {
|
||||
return G.MonadApFirst(
|
||||
MonadAp[GB, GA, GBA, B, A],
|
||||
MonadMap[GA, GBA, A, func(B) A],
|
||||
|
||||
first,
|
||||
second,
|
||||
)
|
||||
}
|
||||
|
||||
// ApFirst combines two effectful actions, keeping only the result of the first.
|
||||
func ApFirst[GA ~func() A, GB ~func() B, GBA ~func() func(B) A, A, B any](second GB) func(GA) GA {
|
||||
return G.ApFirst(
|
||||
MonadAp[GB, GA, GBA, B, A],
|
||||
MonadMap[GA, GBA, A, func(B) A],
|
||||
|
||||
second,
|
||||
)
|
||||
}
|
||||
|
||||
// MonadApSecond combines two effectful actions, keeping only the result of the second.
|
||||
func MonadApSecond[GA ~func() A, GB ~func() B, GBB ~func() func(B) B, A, B any](first GA, second GB) GB {
|
||||
return G.MonadApSecond(
|
||||
MonadAp[GB, GB, GBB, B, B],
|
||||
MonadMap[GA, GBB, A, func(B) B],
|
||||
|
||||
first,
|
||||
second,
|
||||
)
|
||||
}
|
||||
|
||||
// ApSecond combines two effectful actions, keeping only the result of the second.
|
||||
func ApSecond[GA ~func() A, GB ~func() B, GBB ~func() func(B) B, A, B any](second GB) func(GA) GB {
|
||||
return G.ApSecond(
|
||||
MonadAp[GB, GB, GBB, B, B],
|
||||
MonadMap[GA, GBB, A, func(B) B],
|
||||
|
||||
second,
|
||||
)
|
||||
}
|
||||
14
io/generic/apply.go
Normal file
14
io/generic/apply.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package generic
|
||||
|
||||
import (
|
||||
M "github.com/ibm/fp-go/monoid"
|
||||
S "github.com/ibm/fp-go/semigroup"
|
||||
)
|
||||
|
||||
func ApplySemigroup[GA ~func() A, A any](s S.Semigroup[A]) S.Semigroup[GA] {
|
||||
return S.ApplySemigroup(MonadMap[GA, func() func(A) A, A, func(A) A], MonadAp[GA, GA, func() func(A) A, A, A], s)
|
||||
}
|
||||
|
||||
func ApplicativeMonoid[GA ~func() A, A any](m M.Monoid[A]) M.Monoid[GA] {
|
||||
return M.ApplicativeMonoid(Of[GA, A], MonadMap[GA, func() func(A) A, A, func(A) A], MonadAp[GA, GA, func() func(A) A, A, A], m)
|
||||
}
|
||||
20
io/generic/eq.go
Normal file
20
io/generic/eq.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package generic
|
||||
|
||||
import (
|
||||
EQ "github.com/ibm/fp-go/eq"
|
||||
G "github.com/ibm/fp-go/internal/eq"
|
||||
)
|
||||
|
||||
// Eq implements the equals predicate for values contained in the IO monad
|
||||
func Eq[GA ~func() A, A any](e EQ.Eq[A]) EQ.Eq[GA] {
|
||||
// comparator for the monad
|
||||
eq := G.Eq(
|
||||
MonadMap[GA, func() func(A) bool, A, func(A) bool],
|
||||
MonadAp[GA, func() bool, func() func(A) bool, A, bool],
|
||||
e,
|
||||
)
|
||||
// eagerly execute
|
||||
return EQ.FromEquals(func(l, r GA) bool {
|
||||
return eq(l, r)()
|
||||
})
|
||||
}
|
||||
133
io/generic/io.go
Normal file
133
io/generic/io.go
Normal file
@@ -0,0 +1,133 @@
|
||||
package generic
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
F "github.com/ibm/fp-go/function"
|
||||
C "github.com/ibm/fp-go/internal/chain"
|
||||
)
|
||||
|
||||
// type IO[A any] = func() A
|
||||
|
||||
func MakeIO[GA ~func() A, A any](f func() A) GA {
|
||||
return f
|
||||
}
|
||||
|
||||
func Of[GA ~func() A, A any](a A) GA {
|
||||
return MakeIO[GA](F.Constant(a))
|
||||
}
|
||||
|
||||
func FromIO[GA ~func() A, A any](a GA) GA {
|
||||
return a
|
||||
}
|
||||
|
||||
// FromImpure converts a side effect without a return value into a side effect that returns any
|
||||
func FromImpure[GA ~func() any, IMP ~func()](f IMP) GA {
|
||||
return MakeIO[GA](func() any {
|
||||
f()
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func MonadOf[GA ~func() A, A any](a A) GA {
|
||||
return MakeIO[GA](F.Constant(a))
|
||||
}
|
||||
|
||||
func MonadMap[GA ~func() A, GB ~func() B, A, B any](fa GA, f func(A) B) GB {
|
||||
return MakeIO[GB](func() B {
|
||||
return F.Pipe1(fa(), f)
|
||||
})
|
||||
}
|
||||
|
||||
func Map[GA ~func() A, GB ~func() B, A, B any](f func(A) B) func(GA) GB {
|
||||
return F.Bind2nd(MonadMap[GA, GB, A, B], f)
|
||||
}
|
||||
|
||||
func MonadMapTo[GA ~func() A, GB ~func() B, A, B any](fa GA, b B) GB {
|
||||
return MonadMap[GA, GB](fa, F.Constant1[A](b))
|
||||
}
|
||||
|
||||
func MapTo[GA ~func() A, GB ~func() B, A, B any](b B) func(GA) GB {
|
||||
return F.Bind2nd(MonadMapTo[GA, GB, A, B], b)
|
||||
}
|
||||
|
||||
// MonadChain composes computations in sequence, using the return value of one computation to determine the next computation.
|
||||
func MonadChain[GA ~func() A, GB ~func() B, A, B any](fa GA, f func(A) GB) GB {
|
||||
return MakeIO[GB](func() B {
|
||||
return F.Pipe1(fa(), f)()
|
||||
})
|
||||
}
|
||||
|
||||
// Chain composes computations in sequence, using the return value of one computation to determine the next computation.
|
||||
func Chain[GA ~func() A, GB ~func() B, A, B any](f func(A) GB) func(GA) GB {
|
||||
return F.Bind2nd(MonadChain[GA, GB, A, B], f)
|
||||
}
|
||||
|
||||
// MonadChainTo composes computations in sequence, ignoring the return value of the first computation
|
||||
func MonadChainTo[GA ~func() A, GB ~func() B, A, B any](fa GA, fb GB) GB {
|
||||
return MonadChain(fa, F.Constant1[A](fb))
|
||||
}
|
||||
|
||||
// ChainTo composes computations in sequence, ignoring the return value of the first computation
|
||||
func ChainTo[GA ~func() A, GB ~func() B, A, B any](fb GB) func(GA) GB {
|
||||
return F.Bind2nd(MonadChainTo[GA, GB, A, B], fb)
|
||||
}
|
||||
|
||||
// MonadChainFirst composes computations in sequence, using the return value of one computation to determine the next computation and
|
||||
// keeping only the result of the first.
|
||||
func MonadChainFirst[GA ~func() A, GB ~func() B, A, B any](fa GA, f func(A) GB) GA {
|
||||
return C.MonadChainFirst(MonadChain[GA, GA, A, A], MonadMap[GB, GA, B, A], fa, f)
|
||||
}
|
||||
|
||||
// ChainFirst composes computations in sequence, using the return value of one computation to determine the next computation and
|
||||
// keeping only the result of the first.
|
||||
func ChainFirst[GA ~func() A, GB ~func() B, A, B any](f func(A) GB) func(GA) GA {
|
||||
return C.ChainFirst(MonadChain[GA, GA, A, A], MonadMap[GB, GA, B, A], f)
|
||||
}
|
||||
|
||||
func Ap[GA ~func() A, GB ~func() B, GAB ~func() func(A) B, A, B any](ma GA) func(GAB) GB {
|
||||
return F.Bind2nd(MonadAp[GA, GB, GAB, A, B], ma)
|
||||
}
|
||||
|
||||
func Flatten[GA ~func() A, GAA ~func() GA, A any](mma GAA) GA {
|
||||
return mma()
|
||||
}
|
||||
|
||||
// Memoize computes the value of the provided IO monad lazily but exactly once
|
||||
func Memoize[GA ~func() A, A any](ma GA) GA {
|
||||
// synchronization primitives
|
||||
var once sync.Once
|
||||
var result A
|
||||
// callback
|
||||
gen := func() {
|
||||
result = ma()
|
||||
}
|
||||
// returns our memoized wrapper
|
||||
return func() A {
|
||||
once.Do(gen)
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
// Delay creates an operation that passes in the value after some delay
|
||||
func Delay[GA ~func() A, A any](delay time.Duration) func(GA) GA {
|
||||
return func(ga GA) GA {
|
||||
return MakeIO[GA](func() A {
|
||||
time.Sleep(delay)
|
||||
return ga()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Now returns the current timestamp
|
||||
func Now[GA ~func() time.Time]() GA {
|
||||
return MakeIO[GA](time.Now)
|
||||
}
|
||||
|
||||
// Defer creates an IO by creating a brand new IO via a generator function, each time
|
||||
func Defer[GA ~func() A, A any](gen func() GA) GA {
|
||||
return MakeIO[GA](func() A {
|
||||
return gen()()
|
||||
})
|
||||
}
|
||||
29
io/generic/logging.go
Normal file
29
io/generic/logging.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package generic
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
Logging "github.com/ibm/fp-go/logging"
|
||||
)
|
||||
|
||||
func Logger[GA ~func() any, A any](loggers ...*log.Logger) func(string) func(A) GA {
|
||||
_, right := Logging.LoggingCallbacks(loggers...)
|
||||
return func(prefix string) func(A) GA {
|
||||
return func(a A) GA {
|
||||
return FromImpure[GA](func() {
|
||||
right("%s: %v", prefix, a)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Logf[GA ~func() any, A any](loggers ...*log.Logger) func(string) func(A) GA {
|
||||
_, right := Logging.LoggingCallbacks(loggers...)
|
||||
return func(prefix string) func(A) GA {
|
||||
return func(a A) GA {
|
||||
return FromImpure[GA](func() {
|
||||
right(prefix, a)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
34
io/generic/retry.go
Normal file
34
io/generic/retry.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package generic
|
||||
|
||||
import (
|
||||
R "github.com/ibm/fp-go/retry"
|
||||
G "github.com/ibm/fp-go/retry/generic"
|
||||
)
|
||||
|
||||
type retryStatusIO = func() R.RetryStatus
|
||||
|
||||
// Retry combinator for actions that don't raise exceptions, but
|
||||
// signal in their type the outcome has failed. Examples are the
|
||||
// `Option`, `Either` and `EitherT` monads.
|
||||
//
|
||||
// policy - refers to the retry policy
|
||||
// action - converts a status into an operation to be executed
|
||||
// check - checks if the result of the action needs to be retried
|
||||
func Retrying[GA ~func() A, A any](
|
||||
policy R.RetryPolicy,
|
||||
action func(R.RetryStatus) GA,
|
||||
check func(A) bool,
|
||||
) GA {
|
||||
// get an implementation for the types
|
||||
return G.Retrying(
|
||||
Chain[GA, GA, A, A],
|
||||
Chain[retryStatusIO, GA, R.RetryStatus, A],
|
||||
Of[GA, A],
|
||||
Of[retryStatusIO, R.RetryStatus],
|
||||
Delay[retryStatusIO, R.RetryStatus],
|
||||
|
||||
policy,
|
||||
action,
|
||||
check,
|
||||
)
|
||||
}
|
||||
45
io/generic/sequence.go
Normal file
45
io/generic/sequence.go
Normal file
@@ -0,0 +1,45 @@
|
||||
package generic
|
||||
|
||||
import (
|
||||
"github.com/ibm/fp-go/internal/apply"
|
||||
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[GA ~func() A, GTA ~func() T.Tuple1[A], A any](a GA) GTA {
|
||||
return apply.SequenceT1(
|
||||
Map[GA, GTA, A, T.Tuple1[A]],
|
||||
a,
|
||||
)
|
||||
}
|
||||
|
||||
func SequenceT2[GA ~func() A, GB ~func() B, GTAB ~func() T.Tuple2[A, B], A, B any](a GA, b GB) GTAB {
|
||||
return apply.SequenceT2(
|
||||
Map[GA, func() func(B) T.Tuple2[A, B], A, func(B) T.Tuple2[A, B]],
|
||||
Ap[GB, GTAB, func() func(B) T.Tuple2[A, B], B, T.Tuple2[A, B]],
|
||||
|
||||
a, b,
|
||||
)
|
||||
}
|
||||
|
||||
func SequenceT3[GA ~func() A, GB ~func() B, GC ~func() C, GTABC ~func() T.Tuple3[A, B, C], A, B, C any](a GA, b GB, c GC) GTABC {
|
||||
return apply.SequenceT3(
|
||||
Map[GA, func() func(B) func(C) T.Tuple3[A, B, C], A, func(B) func(C) T.Tuple3[A, B, C]],
|
||||
Ap[GB, func() func(C) T.Tuple3[A, B, C], func() func(B) func(C) T.Tuple3[A, B, C], B, func(C) T.Tuple3[A, B, C]],
|
||||
Ap[GC, GTABC, func() func(C) T.Tuple3[A, B, C], C, T.Tuple3[A, B, C]],
|
||||
|
||||
a, b, c,
|
||||
)
|
||||
}
|
||||
|
||||
func SequenceT4[GA ~func() A, GB ~func() B, GC ~func() C, GD ~func() D, GTABCD ~func() T.Tuple4[A, B, C, D], A, B, C, D any](a GA, b GB, c GC, d GD) GTABCD {
|
||||
return apply.SequenceT4(
|
||||
Map[GA, func() func(B) func(C) func(D) T.Tuple4[A, B, C, D], A, func(B) func(C) func(D) T.Tuple4[A, B, C, D]],
|
||||
Ap[GB, func() func(C) func(D) T.Tuple4[A, B, C, D], func() func(B) func(C) func(D) T.Tuple4[A, B, C, D], B, func(C) func(D) T.Tuple4[A, B, C, D]],
|
||||
Ap[GC, func() func(D) T.Tuple4[A, B, C, D], func() func(C) func(D) T.Tuple4[A, B, C, D], C, func(D) T.Tuple4[A, B, C, D]],
|
||||
Ap[GD, GTABCD, func() func(D) T.Tuple4[A, B, C, D], D, T.Tuple4[A, B, C, D]],
|
||||
|
||||
a, b, c, d,
|
||||
)
|
||||
}
|
||||
56
io/generic/traverse.go
Normal file
56
io/generic/traverse.go
Normal file
@@ -0,0 +1,56 @@
|
||||
package generic
|
||||
|
||||
import (
|
||||
F "github.com/ibm/fp-go/function"
|
||||
RA "github.com/ibm/fp-go/internal/array"
|
||||
RR "github.com/ibm/fp-go/internal/record"
|
||||
)
|
||||
|
||||
func MonadTraverseArray[GB ~func() B, GBS ~func() BBS, AAS ~[]A, BBS ~[]B, A, B any](tas AAS, f func(A) GB) GBS {
|
||||
return RA.MonadTraverse(
|
||||
Of[GBS, BBS],
|
||||
Map[GBS, func() func(B) BBS, BBS, func(B) BBS],
|
||||
Ap[GB, GBS, func() func(B) BBS, B, BBS],
|
||||
|
||||
tas,
|
||||
f,
|
||||
)
|
||||
}
|
||||
|
||||
func TraverseArray[GB ~func() B, GBS ~func() BBS, AAS ~[]A, BBS ~[]B, A, B any](f func(A) GB) func(AAS) GBS {
|
||||
return RA.Traverse[AAS](
|
||||
Of[GBS, BBS],
|
||||
Map[GBS, func() func(B) BBS, BBS, func(B) BBS],
|
||||
Ap[GB, GBS, func() func(B) BBS, B, BBS],
|
||||
|
||||
f,
|
||||
)
|
||||
}
|
||||
|
||||
func SequenceArray[GA ~func() A, GAS ~func() AAS, AAS ~[]A, GAAS ~[]GA, A any](tas GAAS) GAS {
|
||||
return MonadTraverseArray[GA, GAS](tas, F.Identity[GA])
|
||||
}
|
||||
|
||||
// MonadTraverseRecord transforms a record using an IO transform an IO of a record
|
||||
func MonadTraverseRecord[GB ~func() B, GBS ~func() MB, MA ~map[K]A, MB ~map[K]B, K comparable, A, B any](ma MA, f func(A) GB) GBS {
|
||||
return RR.MonadTraverse[MA](
|
||||
Of[GBS, MB],
|
||||
Map[GBS, func() func(B) MB, MB, func(B) MB],
|
||||
Ap[GB, GBS, func() func(B) MB, B, MB],
|
||||
ma, f,
|
||||
)
|
||||
}
|
||||
|
||||
// TraverseRecord transforms a record using an IO transform an IO of a record
|
||||
func TraverseRecord[GB ~func() B, GBS ~func() MB, MA ~map[K]A, MB ~map[K]B, K comparable, A, B any](f func(A) GB) func(MA) GBS {
|
||||
return RR.Traverse[MA](
|
||||
Of[GBS, MB],
|
||||
Map[GBS, func() func(B) MB, MB, func(B) MB],
|
||||
Ap[GB, GBS, func() func(B) MB, B, MB],
|
||||
f,
|
||||
)
|
||||
}
|
||||
|
||||
func SequenceRecord[GA ~func() A, GAS ~func() AAS, AAS ~map[K]A, GAAS ~map[K]GA, K comparable, A any](tas GAAS) GAS {
|
||||
return MonadTraverseRecord[GA, GAS](tas, F.Identity[GA])
|
||||
}
|
||||
125
io/io.go
Normal file
125
io/io.go
Normal file
@@ -0,0 +1,125 @@
|
||||
package io
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
G "github.com/ibm/fp-go/io/generic"
|
||||
)
|
||||
|
||||
// IO represents a synchronous computation that cannot fail
|
||||
// refer to [https://andywhite.xyz/posts/2021-01-27-rte-foundations/#ioltagt] for more details
|
||||
type IO[A any] func() A
|
||||
|
||||
func MakeIO[A any](f func() A) IO[A] {
|
||||
return G.MakeIO[IO[A]](f)
|
||||
}
|
||||
|
||||
func Of[A any](a A) IO[A] {
|
||||
return G.Of[IO[A]](a)
|
||||
}
|
||||
|
||||
func FromIO[A any](a IO[A]) IO[A] {
|
||||
return G.FromIO(a)
|
||||
}
|
||||
|
||||
// FromImpure converts a side effect without a return value into a side effect that returns any
|
||||
func FromImpure(f func()) IO[any] {
|
||||
return G.FromImpure[IO[any]](f)
|
||||
}
|
||||
|
||||
func MonadOf[A any](a A) IO[A] {
|
||||
return G.MonadOf[IO[A]](a)
|
||||
}
|
||||
|
||||
func MonadMap[A, B any](fa IO[A], f func(A) B) IO[B] {
|
||||
return G.MonadMap[IO[A], IO[B]](fa, f)
|
||||
}
|
||||
|
||||
func Map[A, B any](f func(A) B) func(fa IO[A]) IO[B] {
|
||||
return G.Map[IO[A], IO[B]](f)
|
||||
}
|
||||
|
||||
func MonadMapTo[A, B any](fa IO[A], b B) IO[B] {
|
||||
return G.MonadMapTo[IO[A], IO[B]](fa, b)
|
||||
}
|
||||
|
||||
func MapTo[A, B any](b B) func(IO[A]) IO[B] {
|
||||
return G.MapTo[IO[A], IO[B]](b)
|
||||
}
|
||||
|
||||
// MonadChain composes computations in sequence, using the return value of one computation to determine the next computation.
|
||||
func MonadChain[A, B any](fa IO[A], f func(A) IO[B]) IO[B] {
|
||||
return G.MonadChain(fa, f)
|
||||
}
|
||||
|
||||
// Chain composes computations in sequence, using the return value of one computation to determine the next computation.
|
||||
func Chain[A, B any](f func(A) IO[B]) func(IO[A]) IO[B] {
|
||||
return G.Chain[IO[A]](f)
|
||||
}
|
||||
|
||||
func MonadAp[B, A any](mab IO[func(A) B], ma IO[A]) IO[B] {
|
||||
return G.MonadAp[IO[A], IO[B]](mab, ma)
|
||||
}
|
||||
|
||||
func Ap[B, A any](ma IO[A]) func(IO[func(A) B]) IO[B] {
|
||||
return G.Ap[IO[A], IO[B], IO[func(A) B]](ma)
|
||||
}
|
||||
|
||||
func Flatten[A any](mma IO[IO[A]]) IO[A] {
|
||||
return G.Flatten(mma)
|
||||
}
|
||||
|
||||
// Memoize computes the value of the provided IO monad lazily but exactly once
|
||||
func Memoize[A any](ma IO[A]) IO[A] {
|
||||
return G.Memoize(ma)
|
||||
}
|
||||
|
||||
// MonadChainFirst composes computations in sequence, using the return value of one computation to determine the next computation and
|
||||
// keeping only the result of the first.
|
||||
func MonadChainFirst[A, B any](fa IO[A], f func(A) IO[B]) IO[A] {
|
||||
return G.MonadChainFirst(fa, f)
|
||||
}
|
||||
|
||||
// ChainFirst composes computations in sequence, using the return value of one computation to determine the next computation and
|
||||
// keeping only the result of the first.
|
||||
func ChainFirst[A, B any](f func(A) IO[B]) func(IO[A]) IO[A] {
|
||||
return G.ChainFirst[IO[A]](f)
|
||||
}
|
||||
|
||||
// MonadApFirst combines two effectful actions, keeping only the result of the first.
|
||||
func MonadApFirst[A, B any](first IO[A], second IO[B]) IO[A] {
|
||||
return G.MonadApFirst[IO[A], IO[B], IO[func(B) A]](first, second)
|
||||
}
|
||||
|
||||
// ApFirst combines two effectful actions, keeping only the result of the first.
|
||||
func ApFirst[A, B any](second IO[B]) func(IO[A]) IO[A] {
|
||||
return G.ApFirst[IO[A], IO[B], IO[func(B) A]](second)
|
||||
}
|
||||
|
||||
// MonadApSecond combines two effectful actions, keeping only the result of the second.
|
||||
func MonadApSecond[A, B any](first IO[A], second IO[B]) IO[B] {
|
||||
return G.MonadApSecond[IO[A], IO[B], IO[func(B) B]](first, second)
|
||||
}
|
||||
|
||||
// ApSecond combines two effectful actions, keeping only the result of the second.
|
||||
func ApSecond[A, B any](second IO[B]) func(IO[A]) IO[B] {
|
||||
return G.ApSecond[IO[A], IO[B], IO[func(B) B]](second)
|
||||
}
|
||||
|
||||
// MonadChainTo composes computations in sequence, ignoring the return value of the first computation
|
||||
func MonadChainTo[A, B any](fa IO[A], fb IO[B]) IO[B] {
|
||||
return G.MonadChainTo(fa, fb)
|
||||
}
|
||||
|
||||
// ChainTo composes computations in sequence, ignoring the return value of the first computation
|
||||
func ChainTo[A, B any](fb IO[B]) func(IO[A]) IO[B] {
|
||||
return G.ChainTo[IO[A]](fb)
|
||||
}
|
||||
|
||||
// Now returns the current timestamp
|
||||
var Now = G.Now[IO[time.Time]]()
|
||||
|
||||
// Defer creates an IO by creating a brand new IO via a generator function, each time
|
||||
func Defer[A any](gen func() IO[A]) IO[A] {
|
||||
return G.Defer[IO[A]](gen)
|
||||
}
|
||||
58
io/io_test.go
Normal file
58
io/io_test.go
Normal file
@@ -0,0 +1,58 @@
|
||||
package io
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"testing"
|
||||
|
||||
F "github.com/ibm/fp-go/function"
|
||||
"github.com/ibm/fp-go/internal/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestMap(t *testing.T) {
|
||||
assert.Equal(t, 2, F.Pipe1(Of(1), Map(utils.Double))())
|
||||
}
|
||||
|
||||
func TestChain(t *testing.T) {
|
||||
f := func(n int) IO[int] {
|
||||
return Of(n * 2)
|
||||
}
|
||||
assert.Equal(t, 2, F.Pipe1(Of(1), Chain(f))())
|
||||
}
|
||||
|
||||
func TestAp(t *testing.T) {
|
||||
assert.Equal(t, 2, F.Pipe1(Of(utils.Double), Ap[int, int](Of(1)))())
|
||||
}
|
||||
|
||||
func TestFlatten(t *testing.T) {
|
||||
assert.Equal(t, 1, F.Pipe1(Of(Of(1)), Flatten[int])())
|
||||
}
|
||||
|
||||
func TestMemoize(t *testing.T) {
|
||||
data := Memoize(MakeIO(rand.Int))
|
||||
|
||||
value1 := data()
|
||||
value2 := data()
|
||||
|
||||
assert.Equal(t, value1, value2)
|
||||
}
|
||||
|
||||
func TestApFirst(t *testing.T) {
|
||||
|
||||
x := F.Pipe1(
|
||||
Of("a"),
|
||||
ApFirst[string](Of("b")),
|
||||
)
|
||||
|
||||
assert.Equal(t, "a", x())
|
||||
}
|
||||
|
||||
func TestApSecond(t *testing.T) {
|
||||
|
||||
x := F.Pipe1(
|
||||
Of("a"),
|
||||
ApSecond[string](Of("b")),
|
||||
)
|
||||
|
||||
assert.Equal(t, "b", x())
|
||||
}
|
||||
18
io/logging.go
Normal file
18
io/logging.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package io
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
G "github.com/ibm/fp-go/io/generic"
|
||||
)
|
||||
|
||||
// Logger constructs a logger function that can be used with ChainXXXIOK
|
||||
func Logger[A any](loggers ...*log.Logger) func(string) func(A) IO[any] {
|
||||
return G.Logger[IO[any], A](loggers...)
|
||||
}
|
||||
|
||||
// Logf constructs a logger function that can be used with ChainXXXIOK
|
||||
// the string prefix contains the format string for the log value
|
||||
func Logf[A any](loggers ...*log.Logger) func(string) func(A) IO[any] {
|
||||
return G.Logf[IO[any], A](loggers...)
|
||||
}
|
||||
25
io/logging_test.go
Normal file
25
io/logging_test.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package io
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestLogger(t *testing.T) {
|
||||
|
||||
l := Logger[int]()
|
||||
|
||||
lio := l("out")
|
||||
|
||||
assert.Equal(t, nil, lio(10)())
|
||||
}
|
||||
|
||||
func TestLogf(t *testing.T) {
|
||||
|
||||
l := Logf[int]()
|
||||
|
||||
lio := l("Value is %d")
|
||||
|
||||
assert.Equal(t, nil, lio(10)())
|
||||
}
|
||||
19
io/retry.go
Normal file
19
io/retry.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package io
|
||||
|
||||
import (
|
||||
G "github.com/ibm/fp-go/io/generic"
|
||||
R "github.com/ibm/fp-go/retry"
|
||||
)
|
||||
|
||||
// Retrying will retry the actions according to the check policy
|
||||
//
|
||||
// policy - refers to the retry policy
|
||||
// action - converts a status into an operation to be executed
|
||||
// check - checks if the result of the action needs to be retried
|
||||
func Retrying[A any](
|
||||
policy R.RetryPolicy,
|
||||
action func(R.RetryStatus) IO[A],
|
||||
check func(A) bool,
|
||||
) IO[A] {
|
||||
return G.Retrying(policy, action, check)
|
||||
}
|
||||
32
io/retry_test.go
Normal file
32
io/retry_test.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package io
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
R "github.com/ibm/fp-go/retry"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var expLogBackoff = R.ExponentialBackoff(10)
|
||||
|
||||
// our retry policy with a 1s cap
|
||||
var testLogPolicy = R.CapDelay(
|
||||
2*time.Second,
|
||||
R.Monoid.Concat(expLogBackoff, R.LimitRetries(20)),
|
||||
)
|
||||
|
||||
func TestRetry(t *testing.T) {
|
||||
action := func(status R.RetryStatus) IO[string] {
|
||||
return Of(fmt.Sprintf("Retrying %d", status.IterNumber))
|
||||
}
|
||||
check := func(value string) bool {
|
||||
return !strings.Contains(value, "5")
|
||||
}
|
||||
|
||||
r := Retrying(testLogPolicy, action, check)
|
||||
|
||||
assert.Equal(t, "Retrying 5", r())
|
||||
}
|
||||
24
io/sequence.go
Normal file
24
io/sequence.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package io
|
||||
|
||||
import (
|
||||
G "github.com/ibm/fp-go/io/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 IO[A]) IO[T.Tuple1[A]] {
|
||||
return G.SequenceT1[IO[A], IO[T.Tuple1[A]]](a)
|
||||
}
|
||||
|
||||
func SequenceT2[A, B any](a IO[A], b IO[B]) IO[T.Tuple2[A, B]] {
|
||||
return G.SequenceT2[IO[A], IO[B], IO[T.Tuple2[A, B]]](a, b)
|
||||
}
|
||||
|
||||
func SequenceT3[A, B, C any](a IO[A], b IO[B], c IO[C]) IO[T.Tuple3[A, B, C]] {
|
||||
return G.SequenceT3[IO[A], IO[B], IO[C], IO[T.Tuple3[A, B, C]]](a, b, c)
|
||||
}
|
||||
|
||||
func SequenceT4[A, B, C, D any](a IO[A], b IO[B], c IO[C], d IO[D]) IO[T.Tuple4[A, B, C, D]] {
|
||||
return G.SequenceT4[IO[A], IO[B], IO[C], IO[D], IO[T.Tuple4[A, B, C, D]]](a, b, c, d)
|
||||
}
|
||||
59
io/testing/laws.go
Normal file
59
io/testing/laws.go
Normal file
@@ -0,0 +1,59 @@
|
||||
package testing
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
EQ "github.com/ibm/fp-go/eq"
|
||||
L "github.com/ibm/fp-go/internal/monad/testing"
|
||||
"github.com/ibm/fp-go/io"
|
||||
)
|
||||
|
||||
// AssertLaws asserts the apply monad laws for the `Either` monad
|
||||
func AssertLaws[A, B, C any](t *testing.T,
|
||||
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,
|
||||
io.Eq(eqa),
|
||||
io.Eq(eqb),
|
||||
io.Eq(eqc),
|
||||
|
||||
io.Of[A],
|
||||
io.Of[B],
|
||||
io.Of[C],
|
||||
|
||||
io.Of[func(A) A],
|
||||
io.Of[func(A) B],
|
||||
io.Of[func(B) C],
|
||||
io.Of[func(func(A) B) B],
|
||||
|
||||
io.MonadMap[A, A],
|
||||
io.MonadMap[A, B],
|
||||
io.MonadMap[A, C],
|
||||
io.MonadMap[B, C],
|
||||
|
||||
io.MonadMap[func(B) C, func(func(A) B) func(A) C],
|
||||
|
||||
io.MonadChain[A, A],
|
||||
io.MonadChain[A, B],
|
||||
io.MonadChain[A, C],
|
||||
io.MonadChain[B, C],
|
||||
|
||||
io.MonadAp[A, A],
|
||||
io.MonadAp[B, A],
|
||||
io.MonadAp[C, B],
|
||||
io.MonadAp[C, A],
|
||||
|
||||
io.MonadAp[B, func(A) B],
|
||||
io.MonadAp[func(A) C, func(A) B],
|
||||
|
||||
ab,
|
||||
bc,
|
||||
)
|
||||
|
||||
}
|
||||
32
io/testing/laws_test.go
Normal file
32
io/testing/laws_test.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package testing
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
EQ "github.com/ibm/fp-go/eq"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestMonadLaws(t *testing.T) {
|
||||
// some comparison
|
||||
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, eqa, eqb, eqc, ab, bc)
|
||||
|
||||
assert.True(t, laws(true))
|
||||
assert.True(t, laws(false))
|
||||
}
|
||||
35
io/traverse.go
Normal file
35
io/traverse.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package io
|
||||
|
||||
import (
|
||||
G "github.com/ibm/fp-go/io/generic"
|
||||
)
|
||||
|
||||
func MonadTraverseArray[A, B any](tas []A, f func(A) IO[B]) IO[[]B] {
|
||||
return G.MonadTraverseArray[IO[B], IO[[]B]](tas, f)
|
||||
}
|
||||
|
||||
// TraverseArray applies a function returning an [IO] to all elements in an array and the
|
||||
// transforms this into an [IO] of that array
|
||||
func TraverseArray[A, B any](f func(A) IO[B]) func([]A) IO[[]B] {
|
||||
return G.TraverseArray[IO[B], IO[[]B], []A](f)
|
||||
}
|
||||
|
||||
// SequenceArray converts an array of [IO] to an [IO] of an array
|
||||
func SequenceArray[A any](tas []IO[A]) IO[[]A] {
|
||||
return G.SequenceArray[IO[A], IO[[]A]](tas)
|
||||
}
|
||||
|
||||
func MonadTraverseRecord[K comparable, A, B any](tas map[K]A, f func(A) IO[B]) IO[map[K]B] {
|
||||
return G.MonadTraverseRecord[IO[B], IO[map[K]B]](tas, f)
|
||||
}
|
||||
|
||||
// TraverseArray applies a function returning an [IO] to all elements in a record and the
|
||||
// transforms this into an [IO] of that record
|
||||
func TraverseRecord[K comparable, A, B any](f func(A) IO[B]) func(map[K]A) IO[map[K]B] {
|
||||
return G.TraverseRecord[IO[B], IO[map[K]B], map[K]A](f)
|
||||
}
|
||||
|
||||
// SequenceRecord converts a record of [IO] to an [IO] of a record
|
||||
func SequenceRecord[K comparable, A any](tas map[K]IO[A]) IO[map[K]A] {
|
||||
return G.SequenceRecord[IO[A], IO[map[K]A]](tas)
|
||||
}
|
||||
25
ioeither/ap.go
Normal file
25
ioeither/ap.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package ioeither
|
||||
|
||||
import (
|
||||
G "github.com/ibm/fp-go/ioeither/generic"
|
||||
)
|
||||
|
||||
// MonadApFirst combines two effectful actions, keeping only the result of the first.
|
||||
func MonadApFirst[E, A, B any](first IOEither[E, A], second IOEither[E, B]) IOEither[E, A] {
|
||||
return G.MonadApFirst[IOEither[E, A], IOEither[E, B], IOEither[E, func(B) A]](first, second)
|
||||
}
|
||||
|
||||
// ApFirst combines two effectful actions, keeping only the result of the first.
|
||||
func ApFirst[E, A, B any](second IOEither[E, B]) func(IOEither[E, A]) IOEither[E, A] {
|
||||
return G.ApFirst[IOEither[E, A], IOEither[E, B], IOEither[E, func(B) A]](second)
|
||||
}
|
||||
|
||||
// MonadApSecond combines two effectful actions, keeping only the result of the second.
|
||||
func MonadApSecond[E, A, B any](first IOEither[E, A], second IOEither[E, B]) IOEither[E, B] {
|
||||
return G.MonadApSecond[IOEither[E, A], IOEither[E, B], IOEither[E, func(B) B]](first, second)
|
||||
}
|
||||
|
||||
// ApSecond combines two effectful actions, keeping only the result of the second.
|
||||
func ApSecond[E, A, B any](second IOEither[E, B]) func(IOEither[E, A]) IOEither[E, B] {
|
||||
return G.ApSecond[IOEither[E, A], IOEither[E, B], IOEither[E, func(B) B]](second)
|
||||
}
|
||||
16
ioeither/bracket.go
Normal file
16
ioeither/bracket.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package ioeither
|
||||
|
||||
import (
|
||||
ET "github.com/ibm/fp-go/either"
|
||||
G "github.com/ibm/fp-go/ioeither/generic"
|
||||
)
|
||||
|
||||
// Bracket makes sure that a resource is cleaned up in the event of an error. The release action is called regardless of
|
||||
// whether the body action returns and error or not.
|
||||
func Bracket[E, A, B, ANY any](
|
||||
acquire IOEither[E, A],
|
||||
use func(A) IOEither[E, B],
|
||||
release func(A, ET.Either[E, B]) IOEither[E, ANY],
|
||||
) IOEither[E, B] {
|
||||
return G.Bracket(acquire, use, release)
|
||||
}
|
||||
17
ioeither/eq.go
Normal file
17
ioeither/eq.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package ioeither
|
||||
|
||||
import (
|
||||
ET "github.com/ibm/fp-go/either"
|
||||
EQ "github.com/ibm/fp-go/eq"
|
||||
G "github.com/ibm/fp-go/ioeither/generic"
|
||||
)
|
||||
|
||||
// Eq implements the equals predicate for values contained in the IOEither monad
|
||||
func Eq[E, A any](eq EQ.Eq[ET.Either[E, A]]) EQ.Eq[IOEither[E, A]] {
|
||||
return G.Eq[IOEither[E, A]](eq)
|
||||
}
|
||||
|
||||
// FromStrictEquals constructs an `Eq` from the canonical comparison function
|
||||
func FromStrictEquals[E, A comparable]() EQ.Eq[IOEither[E, A]] {
|
||||
return G.FromStrictEquals[IOEither[E, A]]()
|
||||
}
|
||||
30
ioeither/eq_test.go
Normal file
30
ioeither/eq_test.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package ioeither
|
||||
|
||||
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))
|
||||
}
|
||||
13
ioeither/exec/exec.go
Normal file
13
ioeither/exec/exec.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package exec
|
||||
|
||||
import (
|
||||
"github.com/ibm/fp-go/exec"
|
||||
F "github.com/ibm/fp-go/function"
|
||||
IOE "github.com/ibm/fp-go/ioeither"
|
||||
G "github.com/ibm/fp-go/ioeither/generic"
|
||||
)
|
||||
|
||||
var (
|
||||
// Command executes a command
|
||||
Command = F.Curry3(G.Command[IOE.IOEither[error, exec.CommandOutput]])
|
||||
)
|
||||
28
ioeither/exec/exec_test.go
Normal file
28
ioeither/exec/exec_test.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package exec
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
RA "github.com/ibm/fp-go/array"
|
||||
B "github.com/ibm/fp-go/bytes"
|
||||
E "github.com/ibm/fp-go/either"
|
||||
"github.com/ibm/fp-go/exec"
|
||||
F "github.com/ibm/fp-go/function"
|
||||
IOE "github.com/ibm/fp-go/ioeither"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestOpenSSL(t *testing.T) {
|
||||
// execute the openSSL binary
|
||||
version := F.Pipe1(
|
||||
Command("openssl")(RA.From("version"))(B.Monoid.Empty()),
|
||||
IOE.Map[error](F.Flow3(
|
||||
exec.StdOut,
|
||||
B.ToString,
|
||||
strings.TrimSpace,
|
||||
)),
|
||||
)
|
||||
|
||||
assert.True(t, E.IsRight(version()))
|
||||
}
|
||||
13
ioeither/file/close.go
Normal file
13
ioeither/file/close.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package file
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
IOE "github.com/ibm/fp-go/ioeither"
|
||||
)
|
||||
|
||||
func onClose[R io.Closer](r R) IOE.IOEither[error, R] {
|
||||
return IOE.TryCatchError(func() (R, error) {
|
||||
return r, r.Close()
|
||||
})
|
||||
}
|
||||
22
ioeither/file/file.go
Normal file
22
ioeither/file/file.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package file
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
IOE "github.com/ibm/fp-go/ioeither"
|
||||
)
|
||||
|
||||
var (
|
||||
// Open opens a file for reading
|
||||
Open = IOE.Eitherize1(os.Open)
|
||||
// ReadFile reads the context of a file
|
||||
ReadFile = IOE.Eitherize1(os.ReadFile)
|
||||
// WriteFile writes a data blob to a file
|
||||
WriteFile = func(dstName string, perm os.FileMode) func([]byte) IOE.IOEither[error, []byte] {
|
||||
return func(data []byte) IOE.IOEither[error, []byte] {
|
||||
return IOE.TryCatchError(func() ([]byte, error) {
|
||||
return data, os.WriteFile(dstName, data, perm)
|
||||
})
|
||||
}
|
||||
}
|
||||
)
|
||||
22
ioeither/file/readall.go
Normal file
22
ioeither/file/readall.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package file
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
IOE "github.com/ibm/fp-go/ioeither"
|
||||
)
|
||||
|
||||
func onReadAll[R io.Reader](r R) IOE.IOEither[error, []byte] {
|
||||
return IOE.TryCatchError(func() ([]byte, error) {
|
||||
return io.ReadAll(r)
|
||||
})
|
||||
}
|
||||
|
||||
// ReadAll uses a generator function to create a stream, reads it and closes it
|
||||
func ReadAll[R io.ReadCloser](acquire IOE.IOEither[error, R]) IOE.IOEither[error, []byte] {
|
||||
return IOE.WithResource[error, R, []byte](
|
||||
acquire,
|
||||
onClose[R])(
|
||||
onReadAll[R],
|
||||
)
|
||||
}
|
||||
35
ioeither/file/write.go
Normal file
35
ioeither/file/write.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package file
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
IOE "github.com/ibm/fp-go/ioeither"
|
||||
)
|
||||
|
||||
func onWriteAll[W io.Writer](data []byte) func(w W) IOE.IOEither[error, []byte] {
|
||||
return func(w W) IOE.IOEither[error, []byte] {
|
||||
return IOE.TryCatchError(func() ([]byte, error) {
|
||||
_, err := w.Write(data)
|
||||
return data, err
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// WriteAll uses a generator function to create a stream, writes data to it and closes it
|
||||
func WriteAll[W io.WriteCloser](data []byte) func(acquire IOE.IOEither[error, W]) IOE.IOEither[error, []byte] {
|
||||
onWrite := onWriteAll[W](data)
|
||||
return func(onCreate IOE.IOEither[error, W]) IOE.IOEither[error, []byte] {
|
||||
return IOE.WithResource[error, W, []byte](
|
||||
onCreate,
|
||||
onClose[W])(
|
||||
onWrite,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Write uses a generator function to create a stream, writes data to it and closes it
|
||||
func Write[W io.WriteCloser, R any](acquire IOE.IOEither[error, W]) func(use func(W) IOE.IOEither[error, R]) IOE.IOEither[error, R] {
|
||||
return IOE.WithResource[error, W, R](
|
||||
acquire,
|
||||
onClose[W])
|
||||
}
|
||||
48
ioeither/generic/ap.go
Normal file
48
ioeither/generic/ap.go
Normal file
@@ -0,0 +1,48 @@
|
||||
package generic
|
||||
|
||||
import (
|
||||
ET "github.com/ibm/fp-go/either"
|
||||
G "github.com/ibm/fp-go/internal/apply"
|
||||
)
|
||||
|
||||
// MonadApFirst combines two effectful actions, keeping only the result of the first.
|
||||
func MonadApFirst[GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B], GBA ~func() ET.Either[E, func(B) A], E, A, B any](first GA, second GB) GA {
|
||||
return G.MonadApFirst(
|
||||
MonadAp[GB, GA, GBA, E, B, A],
|
||||
MonadMap[GA, GBA, E, A, func(B) A],
|
||||
|
||||
first,
|
||||
second,
|
||||
)
|
||||
}
|
||||
|
||||
// ApFirst combines two effectful actions, keeping only the result of the first.
|
||||
func ApFirst[GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B], GBA ~func() ET.Either[E, func(B) A], E, A, B any](second GB) func(GA) GA {
|
||||
return G.ApFirst(
|
||||
MonadAp[GB, GA, GBA, E, B, A],
|
||||
MonadMap[GA, GBA, E, A, func(B) A],
|
||||
|
||||
second,
|
||||
)
|
||||
}
|
||||
|
||||
// MonadApSecond combines two effectful actions, keeping only the result of the second.
|
||||
func MonadApSecond[GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B], GBB ~func() ET.Either[E, func(B) B], E, A, B any](first GA, second GB) GB {
|
||||
return G.MonadApSecond(
|
||||
MonadAp[GB, GB, GBB, E, B, B],
|
||||
MonadMap[GA, GBB, E, A, func(B) B],
|
||||
|
||||
first,
|
||||
second,
|
||||
)
|
||||
}
|
||||
|
||||
// ApSecond combines two effectful actions, keeping only the result of the second.
|
||||
func ApSecond[GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B], GBB ~func() ET.Either[E, func(B) B], E, A, B any](second GB) func(GA) GB {
|
||||
return G.ApSecond(
|
||||
MonadAp[GB, GB, GBB, E, B, B],
|
||||
MonadMap[GA, GBB, E, A, func(B) B],
|
||||
|
||||
second,
|
||||
)
|
||||
}
|
||||
31
ioeither/generic/bracket.go
Normal file
31
ioeither/generic/bracket.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package generic
|
||||
|
||||
import (
|
||||
ET "github.com/ibm/fp-go/either"
|
||||
G "github.com/ibm/fp-go/internal/file"
|
||||
I "github.com/ibm/fp-go/io/generic"
|
||||
)
|
||||
|
||||
// Bracket makes sure that a resource is cleaned up in the event of an error. The release action is called regardless of
|
||||
// whether the body action returns and error or not.
|
||||
func Bracket[
|
||||
GA ~func() ET.Either[E, A],
|
||||
GB ~func() ET.Either[E, B],
|
||||
GANY ~func() ET.Either[E, ANY],
|
||||
E, A, B, ANY any](
|
||||
|
||||
acquire GA,
|
||||
use func(A) GB,
|
||||
release func(A, ET.Either[E, B]) GANY,
|
||||
) GB {
|
||||
return G.Bracket[GA, GB, GANY, ET.Either[E, B], A, B](
|
||||
I.Of[GB, ET.Either[E, B]],
|
||||
MonadChain[GA, GB, E, A, B],
|
||||
I.MonadChain[GB, GB, ET.Either[E, B], ET.Either[E, B]],
|
||||
MonadChain[GANY, GB, E, ANY, B],
|
||||
|
||||
acquire,
|
||||
use,
|
||||
release,
|
||||
)
|
||||
}
|
||||
17
ioeither/generic/eq.go
Normal file
17
ioeither/generic/eq.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package generic
|
||||
|
||||
import (
|
||||
ET "github.com/ibm/fp-go/either"
|
||||
EQ "github.com/ibm/fp-go/eq"
|
||||
G "github.com/ibm/fp-go/io/generic"
|
||||
)
|
||||
|
||||
// Eq implements the equals predicate for values contained in the IOEither monad
|
||||
func Eq[GA ~func() ET.Either[E, A], E, A any](eq EQ.Eq[ET.Either[E, A]]) EQ.Eq[GA] {
|
||||
return G.Eq[GA](eq)
|
||||
}
|
||||
|
||||
// FromStrictEquals constructs an `Eq` from the canonical comparison function
|
||||
func FromStrictEquals[GA ~func() ET.Either[E, A], E, A comparable]() EQ.Eq[GA] {
|
||||
return Eq[GA](ET.FromStrictEquals[E, A]())
|
||||
}
|
||||
16
ioeither/generic/exec.go
Normal file
16
ioeither/generic/exec.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package generic
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
ET "github.com/ibm/fp-go/either"
|
||||
"github.com/ibm/fp-go/exec"
|
||||
GE "github.com/ibm/fp-go/internal/exec"
|
||||
)
|
||||
|
||||
// Command executes a command
|
||||
func Command[GA ~func() ET.Either[error, exec.CommandOutput]](name string, args []string, in []byte) GA {
|
||||
return TryCatchError[GA](func() (exec.CommandOutput, error) {
|
||||
return GE.Exec(context.Background(), name, args, in)
|
||||
})
|
||||
}
|
||||
328
ioeither/generic/ioeither.go
Normal file
328
ioeither/generic/ioeither.go
Normal file
@@ -0,0 +1,328 @@
|
||||
package generic
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
ET "github.com/ibm/fp-go/either"
|
||||
"github.com/ibm/fp-go/errors"
|
||||
F "github.com/ibm/fp-go/function"
|
||||
C "github.com/ibm/fp-go/internal/chain"
|
||||
"github.com/ibm/fp-go/internal/eithert"
|
||||
FE "github.com/ibm/fp-go/internal/fromeither"
|
||||
FI "github.com/ibm/fp-go/internal/fromio"
|
||||
IO "github.com/ibm/fp-go/io/generic"
|
||||
O "github.com/ibm/fp-go/option"
|
||||
)
|
||||
|
||||
// type IOEither[E, A any] = func() Either[E, A]
|
||||
|
||||
func MakeIO[GA ~func() ET.Either[E, A], E, A any](f GA) GA {
|
||||
return f
|
||||
}
|
||||
|
||||
func Left[GA ~func() ET.Either[E, A], E, A any](l E) GA {
|
||||
return MakeIO(eithert.Left(IO.MonadOf[GA, ET.Either[E, A]], l))
|
||||
}
|
||||
|
||||
func Right[GA ~func() ET.Either[E, A], E, A any](r A) GA {
|
||||
return MakeIO(eithert.Right(IO.MonadOf[GA, ET.Either[E, A]], r))
|
||||
}
|
||||
|
||||
func Of[GA ~func() ET.Either[E, A], E, A any](r A) GA {
|
||||
return Right[GA](r)
|
||||
}
|
||||
|
||||
func MonadOf[GA ~func() ET.Either[E, A], E, A any](r A) GA {
|
||||
return Of[GA](r)
|
||||
}
|
||||
|
||||
func LeftIO[GA ~func() ET.Either[E, A], GE ~func() E, E, A any](ml GE) GA {
|
||||
return MakeIO(eithert.LeftF(IO.MonadMap[GE, GA, E, ET.Either[E, A]], ml))
|
||||
}
|
||||
|
||||
func RightIO[GA ~func() ET.Either[E, A], GR ~func() A, E, A any](mr GR) GA {
|
||||
return MakeIO(eithert.RightF(IO.MonadMap[GR, GA, A, ET.Either[E, A]], mr))
|
||||
}
|
||||
|
||||
func FromEither[GA ~func() ET.Either[E, A], E, A any](e ET.Either[E, A]) GA {
|
||||
return IO.Of[GA](e)
|
||||
}
|
||||
|
||||
func FromOption[GA ~func() ET.Either[E, A], E, A any](onNone func() E) func(o O.Option[A]) GA {
|
||||
return FE.FromOption(
|
||||
FromEither[GA, E, A],
|
||||
onNone,
|
||||
)
|
||||
}
|
||||
|
||||
func ChainOptionK[GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B], E, A, B any](onNone func() E) func(func(A) O.Option[B]) func(GA) GB {
|
||||
return FE.ChainOptionK(
|
||||
MonadChain[GA, GB, E, A, B],
|
||||
FromEither[GB, E, B],
|
||||
onNone,
|
||||
)
|
||||
}
|
||||
|
||||
func FromIO[GA ~func() ET.Either[E, A], GR ~func() A, E, A any](mr GR) GA {
|
||||
return RightIO[GA](mr)
|
||||
}
|
||||
|
||||
func MonadMap[GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B], E, A, B any](fa GA, f func(A) B) GB {
|
||||
return eithert.MonadMap(IO.MonadMap[GA, GB, ET.Either[E, A], ET.Either[E, B]], fa, f)
|
||||
}
|
||||
|
||||
func Map[GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B], E, A, B any](f func(A) B) func(GA) GB {
|
||||
return F.Bind2nd(MonadMap[GA, GB, E, A, B], f)
|
||||
}
|
||||
|
||||
func MonadMapTo[GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B], E, A, B any](fa GA, b B) GB {
|
||||
return MonadMap[GA, GB](fa, F.Constant1[A](b))
|
||||
}
|
||||
|
||||
func MapTo[GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B], E, A, B any](b B) func(GA) GB {
|
||||
return F.Bind2nd(MonadMapTo[GA, GB, E, A, B], b)
|
||||
}
|
||||
|
||||
func MonadChain[GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B], E, A, B any](fa GA, f func(A) GB) GB {
|
||||
return eithert.MonadChain(IO.MonadChain[GA, GB, ET.Either[E, A], ET.Either[E, B]], IO.MonadOf[GB, ET.Either[E, B]], fa, f)
|
||||
}
|
||||
|
||||
func Chain[GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B], E, A, B any](f func(A) GB) func(GA) GB {
|
||||
return F.Bind2nd(MonadChain[GA, GB, E, A, B], f)
|
||||
}
|
||||
|
||||
func MonadChainTo[GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B], E, A, B any](fa GA, fb GB) GB {
|
||||
return MonadChain(fa, F.Constant1[A](fb))
|
||||
}
|
||||
|
||||
func ChainTo[GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B], E, A, B any](fb GB) func(GA) GB {
|
||||
return F.Bind2nd(MonadChainTo[GA, GB, E, A, B], fb)
|
||||
}
|
||||
|
||||
func MonadChainEitherK[GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B], E, A, B any](ma GA, f func(A) ET.Either[E, B]) GB {
|
||||
return FE.MonadChainEitherK(
|
||||
MonadChain[GA, GB, E, A, B],
|
||||
FromEither[GB, E, B],
|
||||
ma,
|
||||
f,
|
||||
)
|
||||
}
|
||||
|
||||
func MonadChainIOK[GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B], GR ~func() B, E, A, B any](ma GA, f func(A) GR) GB {
|
||||
return FI.MonadChainIOK(
|
||||
MonadChain[GA, GB, E, A, B],
|
||||
FromIO[GB, GR, E, B],
|
||||
ma,
|
||||
f,
|
||||
)
|
||||
}
|
||||
|
||||
func ChainIOK[GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B], GR ~func() B, E, A, B any](f func(A) GR) func(GA) GB {
|
||||
return FI.ChainIOK(
|
||||
MonadChain[GA, GB, E, A, B],
|
||||
FromIO[GB, GR, E, B],
|
||||
f,
|
||||
)
|
||||
}
|
||||
|
||||
func ChainEitherK[GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B], E, A, B any](f func(A) ET.Either[E, B]) func(GA) GB {
|
||||
return F.Bind2nd(MonadChainEitherK[GA, GB, E, A, B], f)
|
||||
}
|
||||
|
||||
func MonadAp[GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B], GAB ~func() ET.Either[E, func(A) B], E, A, B any](mab GAB, ma GA) GB {
|
||||
return eithert.MonadAp(
|
||||
IO.MonadAp[GA, GB, func() func(ET.Either[E, A]) ET.Either[E, B], ET.Either[E, A], ET.Either[E, B]],
|
||||
IO.MonadMap[GAB, func() func(ET.Either[E, A]) ET.Either[E, B], ET.Either[E, func(A) B], func(ET.Either[E, A]) ET.Either[E, B]],
|
||||
mab, ma)
|
||||
}
|
||||
|
||||
func Ap[GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B], GAB ~func() ET.Either[E, func(A) B], E, A, B any](ma GA) func(GAB) GB {
|
||||
return F.Bind2nd(MonadAp[GA, GB, GAB, E, A, B], ma)
|
||||
}
|
||||
|
||||
func Flatten[GA ~func() ET.Either[E, A], GAA ~func() ET.Either[E, GA], E, A any](mma GAA) GA {
|
||||
return MonadChain(mma, F.Identity[GA])
|
||||
}
|
||||
|
||||
func TryCatch[GA ~func() ET.Either[E, A], E, A any](f func() (A, error), onThrow func(error) E) GA {
|
||||
return MakeIO(func() ET.Either[E, A] {
|
||||
return ET.TryCatch(f, onThrow)
|
||||
})
|
||||
}
|
||||
|
||||
func TryCatchError[GA ~func() ET.Either[error, A], A any](f func() (A, error)) GA {
|
||||
return TryCatch[GA](f, errors.IdentityError)
|
||||
}
|
||||
|
||||
func Eitherize0[GEA ~func() ET.Either[error, A], GA ~func() (A, error), A any](f GA) func() GEA {
|
||||
ef := ET.Eitherize0(f)
|
||||
return func() GEA {
|
||||
return MakeIO(ef)
|
||||
}
|
||||
}
|
||||
|
||||
func Uneitherize0[GEA ~func() ET.Either[error, A], GTA ~func() GEA, A any](f GTA) func() (A, error) {
|
||||
return func() (A, error) {
|
||||
return ET.Unwrap(f()())
|
||||
}
|
||||
}
|
||||
|
||||
func Eitherize1[GEA ~func() ET.Either[error, A], GA ~func(t1 T1) (A, error), T1, A any](f GA) func(T1) GEA {
|
||||
ef := ET.Eitherize1(f)
|
||||
return func(t1 T1) GEA {
|
||||
return MakeIO[GEA](func() ET.Either[error, A] {
|
||||
return ef(t1)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Uneitherize1[GEA ~func() ET.Either[error, A], GTA ~func(t1 T1) GEA, T1, A any](f GTA) func(T1) (A, error) {
|
||||
return func(t1 T1) (A, error) {
|
||||
return ET.Unwrap(f(t1)())
|
||||
}
|
||||
}
|
||||
|
||||
func Eitherize2[GEA ~func() ET.Either[error, A], GA ~func(t1 T1, t2 T2) (A, error), T1, T2, A any](f GA) func(T1, T2) GEA {
|
||||
ef := ET.Eitherize2(f)
|
||||
return func(t1 T1, t2 T2) GEA {
|
||||
return MakeIO[GEA](func() ET.Either[error, A] {
|
||||
return ef(t1, t2)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Uneitherize2[GEA ~func() ET.Either[error, A], GTA ~func(t1 T1, t2 T2) GEA, T1, T2, A any](f GTA) func(T1, T2) (A, error) {
|
||||
return func(t1 T1, t2 T2) (A, error) {
|
||||
return ET.Unwrap(f(t1, t2)())
|
||||
}
|
||||
}
|
||||
|
||||
func Eitherize3[GEA ~func() ET.Either[error, A], GA ~func(t1 T1, t2 T2, t3 T3) (A, error), T1, T2, T3, A any](f GA) func(T1, T2, T3) GEA {
|
||||
ef := ET.Eitherize3(f)
|
||||
return func(t1 T1, t2 T2, t3 T3) GEA {
|
||||
return MakeIO[GEA](func() ET.Either[error, A] {
|
||||
return ef(t1, t2, t3)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Uneitherize3[GEA ~func() ET.Either[error, A], GTA ~func(t1 T1, t2 T2, t3 T3) GEA, T1, T2, T3, A any](f GTA) func(T1, T2, T3) (A, error) {
|
||||
return func(t1 T1, t2 T2, t3 T3) (A, error) {
|
||||
return ET.Unwrap(f(t1, t2, t3)())
|
||||
}
|
||||
}
|
||||
|
||||
func Eitherize4[GEA ~func() ET.Either[error, A], GA ~func(t1 T1, t2 T2, t3 T3, t4 T4) (A, error), T1, T2, T3, T4, A any](f GA) func(T1, T2, T3, T4) GEA {
|
||||
ef := ET.Eitherize4(f)
|
||||
return func(t1 T1, t2 T2, t3 T3, t4 T4) GEA {
|
||||
return MakeIO[GEA](func() ET.Either[error, A] {
|
||||
return ef(t1, t2, t3, t4)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Uneitherize4[GEA ~func() ET.Either[error, A], GTA ~func(t1 T1, t2 T2, t3 T3, t4 T4) GEA, T1, T2, T3, T4, A any](f GTA) func(T1, T2, T3, T4) (A, error) {
|
||||
return func(t1 T1, t2 T2, t3 T3, t4 T4) (A, error) {
|
||||
return ET.Unwrap(f(t1, t2, t3, t4)())
|
||||
}
|
||||
}
|
||||
|
||||
// Memoize computes the value of the provided IO monad lazily but exactly once
|
||||
func Memoize[GA ~func() ET.Either[E, A], E, A any](ma GA) GA {
|
||||
return IO.Memoize(ma)
|
||||
}
|
||||
|
||||
func MonadMapLeft[GA1 ~func() ET.Either[E1, A], GA2 ~func() ET.Either[E2, A], E1, E2, A any](fa GA1, f func(E1) E2) GA2 {
|
||||
return eithert.MonadMapLeft(IO.MonadMap[GA1, GA2, ET.Either[E1, A], ET.Either[E2, A]], fa, f)
|
||||
}
|
||||
|
||||
func MapLeft[GA1 ~func() ET.Either[E1, A], GA2 ~func() ET.Either[E2, A], E1, E2, A any](f func(E1) E2) func(GA1) GA2 {
|
||||
return F.Bind2nd(MonadMapLeft[GA1, GA2, E1, E2, A], f)
|
||||
}
|
||||
|
||||
// Delay creates an operation that passes in the value after some delay
|
||||
func Delay[GA ~func() ET.Either[E, A], E, A any](delay time.Duration) func(GA) GA {
|
||||
return IO.Delay[GA](delay)
|
||||
}
|
||||
|
||||
func MonadBiMap[GA ~func() ET.Either[E1, A], GB ~func() ET.Either[E2, B], E1, E2, A, B any](fa GA, f func(E1) E2, g func(A) B) GB {
|
||||
return eithert.MonadBiMap(IO.MonadMap[GA, GB, ET.Either[E1, A], ET.Either[E2, B]], fa, f, g)
|
||||
}
|
||||
|
||||
// BiMap maps a pair of functions over the two type arguments of the bifunctor.
|
||||
func BiMap[GA ~func() ET.Either[E1, A], GB ~func() ET.Either[E2, B], E1, E2, A, B any](f func(E1) E2, g func(A) B) func(GA) GB {
|
||||
return eithert.BiMap(IO.MonadMap[GA, GB, ET.Either[E1, A], ET.Either[E2, B]], f, g)
|
||||
}
|
||||
|
||||
// Fold convers an IOEither into an IO
|
||||
func Fold[GA ~func() ET.Either[E, A], GB ~func() B, E, A, B any](onLeft func(E) GB, onRight func(A) GB) func(GA) GB {
|
||||
return eithert.MatchE(IO.MonadChain[GA, GB, ET.Either[E, A], B], onLeft, onRight)
|
||||
}
|
||||
|
||||
func MonadFold[GA ~func() ET.Either[E, A], GB ~func() B, E, A, B any](ma GA, onLeft func(E) GB, onRight func(A) GB) GB {
|
||||
return eithert.FoldE(IO.MonadChain[GA, GB, ET.Either[E, A], B], ma, onLeft, onRight)
|
||||
}
|
||||
|
||||
// GetOrElse extracts the value or maps the error
|
||||
func GetOrElse[GA ~func() ET.Either[E, A], GB ~func() A, E, A any](onLeft func(E) GB) func(GA) GB {
|
||||
return eithert.GetOrElse(IO.MonadChain[GA, GB, ET.Either[E, A], A], IO.MonadOf[GB, A], onLeft)
|
||||
}
|
||||
|
||||
// MonadChainFirst runs the monad returned by the function but returns the result of the original monad
|
||||
func MonadChainFirst[GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B], E, A, B any](ma GA, f func(A) GB) GA {
|
||||
return C.MonadChainFirst(
|
||||
MonadChain[GA, GA, E, A, A],
|
||||
MonadMap[GB, GA, E, B, A],
|
||||
ma,
|
||||
f,
|
||||
)
|
||||
}
|
||||
|
||||
// ChainFirst runs the monad returned by the function but returns the result of the original monad
|
||||
func ChainFirst[GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B], E, A, B any](f func(A) GB) func(GA) GA {
|
||||
return C.ChainFirst(
|
||||
MonadChain[GA, GA, E, A, A],
|
||||
MonadMap[GB, GA, E, B, A],
|
||||
f,
|
||||
)
|
||||
}
|
||||
|
||||
// MonadChainFirstIOK runs the monad returned by the function but returns the result of the original monad
|
||||
func MonadChainFirstIOK[GA ~func() ET.Either[E, A], GIOB ~func() B, E, A, B any](first GA, f func(A) GIOB) GA {
|
||||
return FI.MonadChainFirstIOK(
|
||||
MonadChain[GA, GA, E, A, A],
|
||||
MonadMap[func() ET.Either[E, B], GA, E, B, A],
|
||||
FromIO[func() ET.Either[E, B], GIOB, E, B],
|
||||
first,
|
||||
f,
|
||||
)
|
||||
}
|
||||
|
||||
// ChainFirstIOK runs the monad returned by the function but returns the result of the original monad
|
||||
func ChainFirstIOK[GA ~func() ET.Either[E, A], GIOB ~func() B, E, A, B any](f func(A) GIOB) func(GA) GA {
|
||||
return FI.ChainFirstIOK(
|
||||
MonadChain[GA, GA, E, A, A],
|
||||
MonadMap[func() ET.Either[E, B], GA, E, B, A],
|
||||
FromIO[func() ET.Either[E, B], GIOB, E, B],
|
||||
f,
|
||||
)
|
||||
}
|
||||
|
||||
// Swap changes the order of type parameters
|
||||
func Swap[GEA ~func() ET.Either[E, A], GAE ~func() ET.Either[A, E], E, A any](val GEA) GAE {
|
||||
return MonadFold(val, Right[GAE], Left[GAE])
|
||||
}
|
||||
|
||||
// FromImpure converts a side effect without a return value into a side effect that returns any
|
||||
func FromImpure[GA ~func() ET.Either[E, any], IMP ~func(), E any](f IMP) GA {
|
||||
return F.Pipe2(
|
||||
f,
|
||||
IO.FromImpure[func() any, IMP],
|
||||
FromIO[GA, func() any],
|
||||
)
|
||||
}
|
||||
|
||||
// Defer creates an IO by creating a brand new IO via a generator function, each time
|
||||
func Defer[GEA ~func() ET.Either[E, A], E, A any](gen func() GEA) GEA {
|
||||
return IO.Defer[GEA](gen)
|
||||
}
|
||||
22
ioeither/generic/resource.go
Normal file
22
ioeither/generic/resource.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package generic
|
||||
|
||||
import (
|
||||
ET "github.com/ibm/fp-go/either"
|
||||
F "github.com/ibm/fp-go/function"
|
||||
IF "github.com/ibm/fp-go/internal/file"
|
||||
)
|
||||
|
||||
// WithResource constructs a function that creates a resource, then operates on it and then releases the resource
|
||||
func WithResource[
|
||||
GA ~func() ET.Either[E, A],
|
||||
GR ~func() ET.Either[E, R],
|
||||
GANY ~func() ET.Either[E, ANY],
|
||||
E, R, A, ANY any](onCreate GR, onRelease func(R) GANY) func(func(R) GA) GA {
|
||||
return IF.WithResource(
|
||||
MonadChain[GR, GA, E, R, A],
|
||||
MonadFold[GA, GA, E, A, ET.Either[E, A]],
|
||||
MonadFold[GANY, GA, E, ANY, ET.Either[E, A]],
|
||||
MonadMap[GANY, GA, E, ANY, A],
|
||||
Left[GA, E, A],
|
||||
)(F.Constant(onCreate), onRelease)
|
||||
}
|
||||
23
ioeither/generic/retry.go
Normal file
23
ioeither/generic/retry.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package generic
|
||||
|
||||
import (
|
||||
ET "github.com/ibm/fp-go/either"
|
||||
GIO "github.com/ibm/fp-go/io/generic"
|
||||
R "github.com/ibm/fp-go/retry"
|
||||
)
|
||||
|
||||
// Retry combinator for actions that don't raise exceptions, but
|
||||
// signal in their type the outcome has failed. Examples are the
|
||||
// `Option`, `Either` and `EitherT` monads.
|
||||
//
|
||||
// policy - refers to the retry policy
|
||||
// action - converts a status into an operation to be executed
|
||||
// check - checks if the result of the action needs to be retried
|
||||
func Retrying[GA ~func() ET.Either[E, A], E, A any](
|
||||
policy R.RetryPolicy,
|
||||
action func(R.RetryStatus) GA,
|
||||
check func(ET.Either[E, A]) bool,
|
||||
) GA {
|
||||
// get an implementation for the types
|
||||
return GIO.Retrying(policy, action, check)
|
||||
}
|
||||
47
ioeither/generic/sequence.go
Normal file
47
ioeither/generic/sequence.go
Normal file
@@ -0,0 +1,47 @@
|
||||
package generic
|
||||
|
||||
import (
|
||||
ET "github.com/ibm/fp-go/either"
|
||||
"github.com/ibm/fp-go/internal/apply"
|
||||
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[GA ~func() ET.Either[E, A], GTA ~func() ET.Either[E, T.Tuple1[A]], E, A any](a GA) GTA {
|
||||
return apply.SequenceT1(
|
||||
Map[GA, GTA, E, A, T.Tuple1[A]],
|
||||
|
||||
a,
|
||||
)
|
||||
}
|
||||
|
||||
func SequenceT2[GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B], GTAB ~func() ET.Either[E, T.Tuple2[A, B]], E, A, B any](a GA, b GB) GTAB {
|
||||
return apply.SequenceT2(
|
||||
Map[GA, func() ET.Either[E, func(B) T.Tuple2[A, B]], E, A, func(B) T.Tuple2[A, B]],
|
||||
Ap[GB, GTAB, func() ET.Either[E, func(B) T.Tuple2[A, B]], E, B, T.Tuple2[A, B]],
|
||||
|
||||
a, b,
|
||||
)
|
||||
}
|
||||
|
||||
func SequenceT3[GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B], GC ~func() ET.Either[E, C], GTABC ~func() ET.Either[E, T.Tuple3[A, B, C]], E, A, B, C any](a GA, b GB, c GC) GTABC {
|
||||
return apply.SequenceT3(
|
||||
Map[GA, func() ET.Either[E, func(B) func(C) T.Tuple3[A, B, C]], E, A, func(B) func(C) T.Tuple3[A, B, C]],
|
||||
Ap[GB, func() ET.Either[E, func(C) T.Tuple3[A, B, C]], func() ET.Either[E, func(B) func(C) T.Tuple3[A, B, C]], E, B, func(C) T.Tuple3[A, B, C]],
|
||||
Ap[GC, GTABC, func() ET.Either[E, func(C) T.Tuple3[A, B, C]], E, C, T.Tuple3[A, B, C]],
|
||||
|
||||
a, b, c,
|
||||
)
|
||||
}
|
||||
|
||||
func SequenceT4[GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B], GC ~func() ET.Either[E, C], GD ~func() ET.Either[E, D], GTABCD ~func() ET.Either[E, T.Tuple4[A, B, C, D]], E, A, B, C, D any](a GA, b GB, c GC, d GD) GTABCD {
|
||||
return apply.SequenceT4(
|
||||
Map[GA, func() ET.Either[E, func(B) func(C) func(D) T.Tuple4[A, B, C, D]], E, A, func(B) func(C) func(D) T.Tuple4[A, B, C, D]],
|
||||
Ap[GB, func() ET.Either[E, func(C) func(D) T.Tuple4[A, B, C, D]], func() ET.Either[E, func(B) func(C) func(D) T.Tuple4[A, B, C, D]], E, B, func(C) func(D) T.Tuple4[A, B, C, D]],
|
||||
Ap[GC, func() ET.Either[E, func(D) T.Tuple4[A, B, C, D]], func() ET.Either[E, func(C) func(D) T.Tuple4[A, B, C, D]], E, C, func(D) T.Tuple4[A, B, C, D]],
|
||||
Ap[GD, GTABCD, func() ET.Either[E, func(D) T.Tuple4[A, B, C, D]], E, D, T.Tuple4[A, B, C, D]],
|
||||
|
||||
a, b, c, d,
|
||||
)
|
||||
}
|
||||
64
ioeither/generic/traverse.go
Normal file
64
ioeither/generic/traverse.go
Normal file
@@ -0,0 +1,64 @@
|
||||
package generic
|
||||
|
||||
import (
|
||||
ET "github.com/ibm/fp-go/either"
|
||||
F "github.com/ibm/fp-go/function"
|
||||
RA "github.com/ibm/fp-go/internal/array"
|
||||
RR "github.com/ibm/fp-go/internal/record"
|
||||
)
|
||||
|
||||
// MonadTraverseArray transforms an array
|
||||
func MonadTraverseArray[GB ~func() ET.Either[E, B], GBS ~func() ET.Either[E, BBS], AAS ~[]A, BBS ~[]B, E, A, B any](tas AAS, f func(A) GB) GBS {
|
||||
return RA.MonadTraverse[AAS](
|
||||
Of[GBS, E, BBS],
|
||||
Map[GBS, func() ET.Either[E, func(B) BBS], E, BBS, func(B) BBS],
|
||||
Ap[GB, GBS, func() ET.Either[E, func(B) BBS], E, B, BBS],
|
||||
|
||||
tas,
|
||||
f,
|
||||
)
|
||||
}
|
||||
|
||||
// TraverseArray transforms an array
|
||||
func TraverseArray[GB ~func() ET.Either[E, B], GBS ~func() ET.Either[E, BBS], AAS ~[]A, BBS ~[]B, E, A, B any](f func(A) GB) func(AAS) GBS {
|
||||
return RA.Traverse[AAS](
|
||||
Of[GBS, E, BBS],
|
||||
Map[GBS, func() ET.Either[E, func(B) BBS], E, BBS, func(B) BBS],
|
||||
Ap[GB, GBS, func() ET.Either[E, func(B) BBS], E, B, BBS],
|
||||
|
||||
f,
|
||||
)
|
||||
}
|
||||
|
||||
// SequenceArray converts a homogeneous sequence of either into an either of sequence
|
||||
func SequenceArray[GA ~func() ET.Either[E, A], GAS ~func() ET.Either[E, AAS], AAS ~[]A, GAAS ~[]GA, E, A any](tas GAAS) GAS {
|
||||
return MonadTraverseArray[GA, GAS](tas, F.Identity[GA])
|
||||
}
|
||||
|
||||
// MonadTraverseRecord transforms an array
|
||||
func MonadTraverseRecord[GB ~func() ET.Either[E, B], GBS ~func() ET.Either[E, BBS], AAS ~map[K]A, BBS ~map[K]B, K comparable, E, A, B any](tas AAS, f func(A) GB) GBS {
|
||||
return RR.MonadTraverse[AAS](
|
||||
Of[GBS, E, BBS],
|
||||
Map[GBS, func() ET.Either[E, func(B) BBS], E, BBS, func(B) BBS],
|
||||
Ap[GB, GBS, func() ET.Either[E, func(B) BBS], E, B, BBS],
|
||||
|
||||
tas,
|
||||
f,
|
||||
)
|
||||
}
|
||||
|
||||
// TraverseRecord transforms an array
|
||||
func TraverseRecord[GB ~func() ET.Either[E, B], GBS ~func() ET.Either[E, BBS], AAS ~map[K]A, BBS ~map[K]B, K comparable, E, A, B any](f func(A) GB) func(AAS) GBS {
|
||||
return RR.Traverse[AAS](
|
||||
Of[GBS, E, BBS],
|
||||
Map[GBS, func() ET.Either[E, func(B) BBS], E, BBS, func(B) BBS],
|
||||
Ap[GB, GBS, func() ET.Either[E, func(B) BBS], E, B, BBS],
|
||||
|
||||
f,
|
||||
)
|
||||
}
|
||||
|
||||
// SequenceRecord converts a homogeneous sequence of either into an either of sequence
|
||||
func SequenceRecord[GA ~func() ET.Either[E, A], GAS ~func() ET.Either[E, AAS], AAS ~map[K]A, GAAS ~map[K]GA, K comparable, E, A any](tas GAAS) GAS {
|
||||
return MonadTraverseRecord[GA, GAS](tas, F.Identity[GA])
|
||||
}
|
||||
58
ioeither/http/request.go
Normal file
58
ioeither/http/request.go
Normal file
@@ -0,0 +1,58 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
B "github.com/ibm/fp-go/bytes"
|
||||
ER "github.com/ibm/fp-go/errors"
|
||||
F "github.com/ibm/fp-go/function"
|
||||
IOE "github.com/ibm/fp-go/ioeither"
|
||||
IOEF "github.com/ibm/fp-go/ioeither/file"
|
||||
J "github.com/ibm/fp-go/json"
|
||||
)
|
||||
|
||||
type Client interface {
|
||||
Do(req *http.Request) IOE.IOEither[error, *http.Response]
|
||||
}
|
||||
|
||||
type client struct {
|
||||
delegate *http.Client
|
||||
}
|
||||
|
||||
func (client client) Do(req *http.Request) IOE.IOEither[error, *http.Response] {
|
||||
return IOE.TryCatch(func() (*http.Response, error) {
|
||||
return client.delegate.Do(req)
|
||||
}, ER.IdentityError)
|
||||
}
|
||||
|
||||
func MakeClient(httpClient *http.Client) Client {
|
||||
return client{delegate: httpClient}
|
||||
}
|
||||
|
||||
func ReadAll(client Client) func(*http.Request) IOE.IOEither[error, []byte] {
|
||||
return func(req *http.Request) IOE.IOEither[error, []byte] {
|
||||
return IOEF.ReadAll(F.Pipe2(
|
||||
req,
|
||||
client.Do,
|
||||
IOE.Map[error](func(resp *http.Response) io.ReadCloser {
|
||||
return resp.Body
|
||||
}),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func ReadText(client Client) func(*http.Request) IOE.IOEither[error, string] {
|
||||
return F.Flow2(
|
||||
ReadAll(client),
|
||||
IOE.Map[error](B.ToString),
|
||||
)
|
||||
}
|
||||
|
||||
func ReadJson[A any](client Client) func(*http.Request) IOE.IOEither[error, A] {
|
||||
return F.Flow2(
|
||||
ReadAll(client),
|
||||
IOE.ChainEitherK(J.Unmarshal[A]),
|
||||
)
|
||||
}
|
||||
58
ioeither/http/retry_test.go
Normal file
58
ioeither/http/retry_test.go
Normal file
@@ -0,0 +1,58 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
AR "github.com/ibm/fp-go/array"
|
||||
E "github.com/ibm/fp-go/either"
|
||||
HE "github.com/ibm/fp-go/either/http"
|
||||
"github.com/ibm/fp-go/errors"
|
||||
F "github.com/ibm/fp-go/function"
|
||||
IOE "github.com/ibm/fp-go/ioeither"
|
||||
O "github.com/ibm/fp-go/option"
|
||||
R "github.com/ibm/fp-go/retry"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var expLogBackoff = R.ExponentialBackoff(250 * time.Millisecond)
|
||||
|
||||
// our retry policy with a 1s cap
|
||||
var testLogPolicy = R.CapDelay(
|
||||
2*time.Second,
|
||||
R.Monoid.Concat(expLogBackoff, R.LimitRetries(20)),
|
||||
)
|
||||
|
||||
type PostItem struct {
|
||||
UserId uint `json:"userId"`
|
||||
Id uint `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Body string `json:"body"`
|
||||
}
|
||||
|
||||
func TestRetryHttp(t *testing.T) {
|
||||
// URLs to try, the first URLs have an invalid hostname
|
||||
urls := AR.From("https://jsonplaceholder1.typicode.com/posts/1", "https://jsonplaceholder2.typicode.com/posts/1", "https://jsonplaceholder3.typicode.com/posts/1", "https://jsonplaceholder4.typicode.com/posts/1", "https://jsonplaceholder.typicode.com/posts/1")
|
||||
client := MakeClient(&http.Client{})
|
||||
|
||||
action := func(status R.RetryStatus) IOE.IOEither[error, *PostItem] {
|
||||
return F.Pipe2(
|
||||
HE.GetRequest(urls[status.IterNumber]),
|
||||
IOE.FromEither[error, *http.Request],
|
||||
IOE.Chain(ReadJson[*PostItem](client)),
|
||||
)
|
||||
}
|
||||
|
||||
check := E.Fold(
|
||||
F.Flow2(
|
||||
errors.As[*net.DNSError](),
|
||||
O.Fold(F.Constant(false), F.Constant1[*net.DNSError](true)),
|
||||
),
|
||||
F.Constant1[*PostItem](false),
|
||||
)
|
||||
|
||||
item := IOE.Retrying(testLogPolicy, action, check)()
|
||||
assert.True(t, E.IsRight(item))
|
||||
}
|
||||
217
ioeither/ioeither.go
Normal file
217
ioeither/ioeither.go
Normal file
@@ -0,0 +1,217 @@
|
||||
package ioeither
|
||||
|
||||
import (
|
||||
ET "github.com/ibm/fp-go/either"
|
||||
I "github.com/ibm/fp-go/io"
|
||||
G "github.com/ibm/fp-go/ioeither/generic"
|
||||
O "github.com/ibm/fp-go/option"
|
||||
)
|
||||
|
||||
// IO represents a synchronous computation that may fail
|
||||
// refer to [https://andywhite.xyz/posts/2021-01-27-rte-foundations/#ioeitherlte-agt] for more details
|
||||
type IOEither[E, A any] I.IO[ET.Either[E, A]]
|
||||
|
||||
func MakeIO[E, A any](f IOEither[E, A]) IOEither[E, A] {
|
||||
return G.MakeIO(f)
|
||||
}
|
||||
|
||||
func Left[A, E any](l E) IOEither[E, A] {
|
||||
return G.Left[IOEither[E, A]](l)
|
||||
}
|
||||
|
||||
func Right[E, A any](r A) IOEither[E, A] {
|
||||
return G.Right[IOEither[E, A]](r)
|
||||
}
|
||||
|
||||
func Of[E, A any](r A) IOEither[E, A] {
|
||||
return G.Of[IOEither[E, A]](r)
|
||||
}
|
||||
|
||||
func MonadOf[E, A any](r A) IOEither[E, A] {
|
||||
return G.MonadOf[IOEither[E, A]](r)
|
||||
}
|
||||
|
||||
func LeftIO[A, E any](ml I.IO[E]) IOEither[E, A] {
|
||||
return G.LeftIO[IOEither[E, A]](ml)
|
||||
}
|
||||
|
||||
func RightIO[E, A any](mr I.IO[A]) IOEither[E, A] {
|
||||
return G.RightIO[IOEither[E, A]](mr)
|
||||
}
|
||||
|
||||
func FromEither[E, A any](e ET.Either[E, A]) IOEither[E, A] {
|
||||
return G.FromEither[IOEither[E, A]](e)
|
||||
}
|
||||
|
||||
func FromOption[A, E any](onNone func() E) func(o O.Option[A]) IOEither[E, A] {
|
||||
return G.FromOption[IOEither[E, A]](onNone)
|
||||
}
|
||||
|
||||
func ChainOptionK[A, B, E any](onNone func() E) func(func(A) O.Option[B]) func(IOEither[E, A]) IOEither[E, B] {
|
||||
return G.ChainOptionK[IOEither[E, A], IOEither[E, B]](onNone)
|
||||
}
|
||||
|
||||
func MonadChainIOK[E, A, B any](ma IOEither[E, A], f func(A) I.IO[B]) IOEither[E, B] {
|
||||
return G.MonadChainIOK[IOEither[E, A], IOEither[E, B]](ma, f)
|
||||
}
|
||||
|
||||
func ChainIOK[E, A, B any](f func(A) I.IO[B]) func(IOEither[E, A]) IOEither[E, B] {
|
||||
return G.ChainIOK[IOEither[E, A], IOEither[E, B]](f)
|
||||
}
|
||||
|
||||
func FromIO[E, A any](mr I.IO[A]) IOEither[E, A] {
|
||||
return G.FromIO[IOEither[E, A]](mr)
|
||||
}
|
||||
|
||||
func MonadMap[E, A, B any](fa IOEither[E, A], f func(A) B) IOEither[E, B] {
|
||||
return G.MonadMap[IOEither[E, A], IOEither[E, B]](fa, f)
|
||||
}
|
||||
|
||||
func Map[E, A, B any](f func(A) B) func(IOEither[E, A]) IOEither[E, B] {
|
||||
return G.Map[IOEither[E, A], IOEither[E, B]](f)
|
||||
}
|
||||
|
||||
func MonadMapTo[E, A, B any](fa IOEither[E, A], b B) IOEither[E, B] {
|
||||
return G.MonadMapTo[IOEither[E, A], IOEither[E, B]](fa, b)
|
||||
}
|
||||
|
||||
func MapTo[E, A, B any](b B) func(IOEither[E, A]) IOEither[E, B] {
|
||||
return G.MapTo[IOEither[E, A], IOEither[E, B]](b)
|
||||
}
|
||||
|
||||
func MonadChain[E, A, B any](fa IOEither[E, A], f func(A) IOEither[E, B]) IOEither[E, B] {
|
||||
return G.MonadChain(fa, f)
|
||||
}
|
||||
|
||||
func Chain[E, A, B any](f func(A) IOEither[E, B]) func(IOEither[E, A]) IOEither[E, B] {
|
||||
return G.Chain[IOEither[E, A]](f)
|
||||
}
|
||||
|
||||
func MonadChainEitherK[E, A, B any](ma IOEither[E, A], f func(A) ET.Either[E, B]) IOEither[E, B] {
|
||||
return G.MonadChainEitherK[IOEither[E, A], IOEither[E, B]](ma, f)
|
||||
}
|
||||
|
||||
func ChainEitherK[E, A, B any](f func(A) ET.Either[E, B]) func(IOEither[E, A]) IOEither[E, B] {
|
||||
return G.ChainEitherK[IOEither[E, A], IOEither[E, B]](f)
|
||||
}
|
||||
|
||||
func MonadAp[B, E, A any](mab IOEither[E, func(A) B], ma IOEither[E, A]) IOEither[E, B] {
|
||||
return G.MonadAp[IOEither[E, A], IOEither[E, B]](mab, ma)
|
||||
}
|
||||
|
||||
func Ap[B, E, A any](ma IOEither[E, A]) func(IOEither[E, func(A) B]) IOEither[E, B] {
|
||||
return G.Ap[IOEither[E, A], IOEither[E, B], IOEither[E, func(A) B]](ma)
|
||||
}
|
||||
|
||||
func Flatten[E, A any](mma IOEither[E, IOEither[E, A]]) IOEither[E, A] {
|
||||
return G.Flatten(mma)
|
||||
}
|
||||
|
||||
func TryCatch[E, A any](f func() (A, error), onThrow func(error) E) IOEither[E, A] {
|
||||
return G.TryCatch[IOEither[E, A]](f, onThrow)
|
||||
}
|
||||
|
||||
func TryCatchError[A any](f func() (A, error)) IOEither[error, A] {
|
||||
return G.TryCatchError[IOEither[error, A]](f)
|
||||
}
|
||||
|
||||
func Eitherize0[A any](f func() (A, error)) func() IOEither[error, A] {
|
||||
return G.Eitherize0[IOEither[error, A]](f)
|
||||
}
|
||||
|
||||
func Eitherize1[T1, A any](f func(t1 T1) (A, error)) func(T1) IOEither[error, A] {
|
||||
return G.Eitherize1[IOEither[error, A]](f)
|
||||
}
|
||||
|
||||
func Eitherize2[T1, T2, A any](f func(t1 T1, t2 T2) (A, error)) func(T1, T2) IOEither[error, A] {
|
||||
return G.Eitherize2[IOEither[error, A]](f)
|
||||
}
|
||||
|
||||
func Eitherize3[T1, T2, T3, A any](f func(t1 T1, t2 T2, t3 T3) (A, error)) func(T1, T2, T3) IOEither[error, A] {
|
||||
return G.Eitherize3[IOEither[error, A]](f)
|
||||
}
|
||||
|
||||
func Eitherize4[T1, T2, T3, T4, A any](f func(t1 T1, t2 T2, t3 T3, t4 T4) (A, error)) func(T1, T2, T3, T4) IOEither[error, A] {
|
||||
return G.Eitherize4[IOEither[error, A]](f)
|
||||
}
|
||||
|
||||
func Memoize[E, A any](ma IOEither[E, A]) IOEither[E, A] {
|
||||
return G.Memoize(ma)
|
||||
}
|
||||
|
||||
func MonadMapLeft[E1, E2, A any](fa IOEither[E1, A], f func(E1) E2) IOEither[E2, A] {
|
||||
return G.MonadMapLeft[IOEither[E1, A], IOEither[E2, A]](fa, f)
|
||||
}
|
||||
|
||||
func MapLeft[E1, E2, A any](f func(E1) E2) func(IOEither[E1, A]) IOEither[E2, A] {
|
||||
return G.MapLeft[IOEither[E1, A], IOEither[E2, A]](f)
|
||||
}
|
||||
|
||||
func MonadBiMap[E1, E2, A, B any](fa IOEither[E1, A], f func(E1) E2, g func(A) B) IOEither[E2, B] {
|
||||
return G.MonadBiMap[IOEither[E1, A], IOEither[E2, B]](fa, f, g)
|
||||
}
|
||||
|
||||
// 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) B) func(IOEither[E1, A]) IOEither[E2, B] {
|
||||
return G.BiMap[IOEither[E1, A], IOEither[E2, B]](f, g)
|
||||
}
|
||||
|
||||
// Fold converts an IOEither into an IO
|
||||
func Fold[E, A, B any](onLeft func(E) I.IO[B], onRight func(A) I.IO[B]) func(IOEither[E, A]) I.IO[B] {
|
||||
return G.Fold[IOEither[E, A]](onLeft, onRight)
|
||||
}
|
||||
|
||||
// GetOrElse extracts the value or maps the error
|
||||
func GetOrElse[E, A any](onLeft func(E) I.IO[A]) func(IOEither[E, A]) I.IO[A] {
|
||||
return G.GetOrElse[IOEither[E, A]](onLeft)
|
||||
}
|
||||
|
||||
// MonadChainTo composes to the second monad ignoring the return value of the first
|
||||
func MonadChainTo[E, A, B any](fa IOEither[E, A], fb IOEither[E, B]) IOEither[E, B] {
|
||||
return G.MonadChainTo(fa, fb)
|
||||
}
|
||||
|
||||
// ChainTo composes to the second monad ignoring the return value of the first
|
||||
func ChainTo[E, A, B any](fb IOEither[E, B]) func(IOEither[E, A]) IOEither[E, B] {
|
||||
return G.ChainTo[IOEither[E, A]](fb)
|
||||
}
|
||||
|
||||
// MonadChainFirst runs the monad returned by the function but returns the result of the original monad
|
||||
func MonadChainFirst[E, A, B any](ma IOEither[E, A], f func(A) IOEither[E, B]) IOEither[E, A] {
|
||||
return G.MonadChainFirst(ma, f)
|
||||
}
|
||||
|
||||
// ChainFirst runs the monad returned by the function but returns the result of the original monad
|
||||
func ChainFirst[E, A, B any](f func(A) IOEither[E, B]) func(IOEither[E, A]) IOEither[E, A] {
|
||||
return G.ChainFirst[IOEither[E, A]](f)
|
||||
}
|
||||
|
||||
// MonadChainFirstIOK runs the monad returned by the function but returns the result of the original monad
|
||||
func MonadChainFirstIOK[E, A, B any](ma IOEither[E, A], f func(A) I.IO[B]) IOEither[E, A] {
|
||||
return G.MonadChainFirstIOK(ma, f)
|
||||
}
|
||||
|
||||
// ChainFirsIOKt runs the monad returned by the function but returns the result of the original monad
|
||||
func ChainFirstIOK[E, A, B any](f func(A) I.IO[B]) func(IOEither[E, A]) IOEither[E, A] {
|
||||
return G.ChainFirstIOK[IOEither[E, A]](f)
|
||||
}
|
||||
|
||||
// WithResource constructs a function that creates a resource, then operates on it and then releases the resource
|
||||
func WithResource[E, R, A, ANY any](onCreate IOEither[E, R], onRelease func(R) IOEither[E, ANY]) func(func(R) IOEither[E, A]) IOEither[E, A] {
|
||||
return G.WithResource[IOEither[E, A]](onCreate, onRelease)
|
||||
}
|
||||
|
||||
// Swap changes the order of type parameters
|
||||
func Swap[E, A any](val IOEither[E, A]) IOEither[A, E] {
|
||||
return G.Swap[IOEither[E, A], IOEither[A, E]](val)
|
||||
}
|
||||
|
||||
// FromImpure converts a side effect without a return value into a side effect that returns any
|
||||
func FromImpure[E any](f func()) IOEither[E, any] {
|
||||
return G.FromImpure[IOEither[E, any]](f)
|
||||
}
|
||||
|
||||
// Defer creates an IO by creating a brand new IO via a generator function, each time
|
||||
func Defer[E, A any](gen func() IOEither[E, A]) IOEither[E, A] {
|
||||
return G.Defer[IOEither[E, A]](gen)
|
||||
}
|
||||
121
ioeither/ioeither_test.go
Normal file
121
ioeither/ioeither_test.go
Normal file
@@ -0,0 +1,121 @@
|
||||
package ioeither
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
E "github.com/ibm/fp-go/either"
|
||||
F "github.com/ibm/fp-go/function"
|
||||
"github.com/ibm/fp-go/internal/utils"
|
||||
I "github.com/ibm/fp-go/io"
|
||||
IG "github.com/ibm/fp-go/io/generic"
|
||||
O "github.com/ibm/fp-go/option"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestMap(t *testing.T) {
|
||||
assert.Equal(t, E.Of[error](2), F.Pipe1(
|
||||
Of[error](1),
|
||||
Map[error](utils.Double),
|
||||
)())
|
||||
|
||||
}
|
||||
|
||||
func TestChainEitherK(t *testing.T) {
|
||||
f := ChainEitherK(func(n int) E.Either[string, int] {
|
||||
if n > 0 {
|
||||
return E.Of[string](n)
|
||||
}
|
||||
return E.Left[int]("a")
|
||||
|
||||
})
|
||||
assert.Equal(t, E.Right[string](1), f(Right[string](1))())
|
||||
assert.Equal(t, E.Left[int]("a"), f(Right[string](-1))())
|
||||
assert.Equal(t, E.Left[int]("b"), f(Left[int]("b"))())
|
||||
}
|
||||
|
||||
func TestChainOptionK(t *testing.T) {
|
||||
f := ChainOptionK[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, E.Right[string](1), f(Right[string](1))())
|
||||
assert.Equal(t, E.Left[int]("a"), f(Right[string](-1))())
|
||||
assert.Equal(t, E.Left[int]("b"), f(Left[int]("b"))())
|
||||
}
|
||||
|
||||
func TestFromOption(t *testing.T) {
|
||||
f := FromOption[int](F.Constant("a"))
|
||||
assert.Equal(t, E.Right[string](1), f(O.Some(1))())
|
||||
assert.Equal(t, E.Left[int]("a"), f(O.None[int]())())
|
||||
}
|
||||
|
||||
func TestChainIOK(t *testing.T) {
|
||||
f := ChainIOK[string](func(n int) I.IO[string] {
|
||||
return I.MakeIO(func() string {
|
||||
return fmt.Sprintf("%d", n)
|
||||
})
|
||||
})
|
||||
|
||||
assert.Equal(t, E.Right[string]("1"), f(Right[string](1))())
|
||||
assert.Equal(t, E.Left[string, string]("b"), f(Left[int]("b"))())
|
||||
}
|
||||
|
||||
func TestChainWithIO(t *testing.T) {
|
||||
|
||||
r := F.Pipe1(
|
||||
Of[error]("test"),
|
||||
// sad, we need the generics version ...
|
||||
IG.Map[IOEither[error, string], I.IO[bool]](E.IsRight[error, string]),
|
||||
)
|
||||
|
||||
assert.True(t, r())
|
||||
}
|
||||
|
||||
func TestChainFirst(t *testing.T) {
|
||||
f := func(a string) IOEither[string, int] {
|
||||
if len(a) > 2 {
|
||||
return Of[string](len(a))
|
||||
}
|
||||
return Left[int]("foo")
|
||||
}
|
||||
good := Of[string]("foo")
|
||||
bad := Of[string]("a")
|
||||
ch := ChainFirst(f)
|
||||
|
||||
assert.Equal(t, E.Of[string]("foo"), F.Pipe1(good, ch)())
|
||||
assert.Equal(t, E.Left[string, string]("foo"), F.Pipe1(bad, ch)())
|
||||
}
|
||||
|
||||
func TestChainFirstIOK(t *testing.T) {
|
||||
f := func(a string) I.IO[int] {
|
||||
return I.Of(len(a))
|
||||
}
|
||||
good := Of[string]("foo")
|
||||
ch := ChainFirstIOK[string](f)
|
||||
|
||||
assert.Equal(t, E.Of[string]("foo"), F.Pipe1(good, ch)())
|
||||
}
|
||||
|
||||
func TestApFirst(t *testing.T) {
|
||||
|
||||
x := F.Pipe1(
|
||||
Of[error]("a"),
|
||||
ApFirst[error, string](Of[error]("b")),
|
||||
)
|
||||
|
||||
assert.Equal(t, E.Of[error]("a"), x())
|
||||
}
|
||||
|
||||
func TestApSecond(t *testing.T) {
|
||||
|
||||
x := F.Pipe1(
|
||||
Of[error]("a"),
|
||||
ApSecond[error, string](Of[error]("b")),
|
||||
)
|
||||
|
||||
assert.Equal(t, E.Of[error]("b"), x())
|
||||
}
|
||||
20
ioeither/retry.go
Normal file
20
ioeither/retry.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package ioeither
|
||||
|
||||
import (
|
||||
ET "github.com/ibm/fp-go/either"
|
||||
G "github.com/ibm/fp-go/ioeither/generic"
|
||||
R "github.com/ibm/fp-go/retry"
|
||||
)
|
||||
|
||||
// Retrying will retry the actions according to the check policy
|
||||
//
|
||||
// policy - refers to the retry policy
|
||||
// action - converts a status into an operation to be executed
|
||||
// check - checks if the result of the action needs to be retried
|
||||
func Retrying[E, A any](
|
||||
policy R.RetryPolicy,
|
||||
action func(R.RetryStatus) IOEither[E, A],
|
||||
check func(ET.Either[E, A]) bool,
|
||||
) IOEither[E, A] {
|
||||
return G.Retrying(policy, action, check)
|
||||
}
|
||||
33
ioeither/retry_test.go
Normal file
33
ioeither/retry_test.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package ioeither
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
E "github.com/ibm/fp-go/either"
|
||||
R "github.com/ibm/fp-go/retry"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var expLogBackoff = R.ExponentialBackoff(10 * time.Millisecond)
|
||||
|
||||
// our retry policy with a 1s cap
|
||||
var testLogPolicy = R.CapDelay(
|
||||
2*time.Second,
|
||||
R.Monoid.Concat(expLogBackoff, R.LimitRetries(20)),
|
||||
)
|
||||
|
||||
func TestRetry(t *testing.T) {
|
||||
action := func(status R.RetryStatus) IOEither[error, string] {
|
||||
if status.IterNumber < 5 {
|
||||
return Left[string](fmt.Errorf("retrying %d", status.IterNumber))
|
||||
}
|
||||
return Of[error](fmt.Sprintf("Retrying %d", status.IterNumber))
|
||||
}
|
||||
check := E.IsLeft[error, string]
|
||||
|
||||
r := Retrying(testLogPolicy, action, check)
|
||||
|
||||
assert.Equal(t, E.Of[error]("Retrying 5"), r())
|
||||
}
|
||||
54
ioeither/sequence.go
Normal file
54
ioeither/sequence.go
Normal file
@@ -0,0 +1,54 @@
|
||||
package ioeither
|
||||
|
||||
import (
|
||||
G "github.com/ibm/fp-go/ioeither/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[E, A any](a IOEither[E, A]) IOEither[E, T.Tuple1[A]] {
|
||||
return G.SequenceT1[
|
||||
IOEither[E, A],
|
||||
IOEither[E, T.Tuple1[A]],
|
||||
](a)
|
||||
}
|
||||
|
||||
func SequenceT2[E, A, B any](
|
||||
a IOEither[E, A],
|
||||
b IOEither[E, B],
|
||||
) IOEither[E, T.Tuple2[A, B]] {
|
||||
return G.SequenceT2[
|
||||
IOEither[E, A],
|
||||
IOEither[E, B],
|
||||
IOEither[E, T.Tuple2[A, B]],
|
||||
](a, b)
|
||||
}
|
||||
|
||||
func SequenceT3[E, A, B, C any](
|
||||
a IOEither[E, A],
|
||||
b IOEither[E, B],
|
||||
c IOEither[E, C],
|
||||
) IOEither[E, T.Tuple3[A, B, C]] {
|
||||
return G.SequenceT3[
|
||||
IOEither[E, A],
|
||||
IOEither[E, B],
|
||||
IOEither[E, C],
|
||||
IOEither[E, T.Tuple3[A, B, C]],
|
||||
](a, b, c)
|
||||
}
|
||||
|
||||
func SequenceT4[E, A, B, C, D any](
|
||||
a IOEither[E, A],
|
||||
b IOEither[E, B],
|
||||
c IOEither[E, C],
|
||||
d IOEither[E, D],
|
||||
) IOEither[E, T.Tuple4[A, B, C, D]] {
|
||||
return G.SequenceT4[
|
||||
IOEither[E, A],
|
||||
IOEither[E, B],
|
||||
IOEither[E, C],
|
||||
IOEither[E, D],
|
||||
IOEither[E, T.Tuple4[A, B, C, D]],
|
||||
](a, b, c, d)
|
||||
}
|
||||
61
ioeither/testing/laws.go
Normal file
61
ioeither/testing/laws.go
Normal file
@@ -0,0 +1,61 @@
|
||||
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"
|
||||
IOE "github.com/ibm/fp-go/ioeither"
|
||||
)
|
||||
|
||||
// AssertLaws asserts the apply monad laws for the `IOEither` 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,
|
||||
IOE.Eq(ET.Eq(eqe, eqa)),
|
||||
IOE.Eq(ET.Eq(eqe, eqb)),
|
||||
IOE.Eq(ET.Eq(eqe, eqc)),
|
||||
|
||||
IOE.Of[E, A],
|
||||
IOE.Of[E, B],
|
||||
IOE.Of[E, C],
|
||||
|
||||
IOE.Of[E, func(A) A],
|
||||
IOE.Of[E, func(A) B],
|
||||
IOE.Of[E, func(B) C],
|
||||
IOE.Of[E, func(func(A) B) B],
|
||||
|
||||
IOE.MonadMap[E, A, A],
|
||||
IOE.MonadMap[E, A, B],
|
||||
IOE.MonadMap[E, A, C],
|
||||
IOE.MonadMap[E, B, C],
|
||||
|
||||
IOE.MonadMap[E, func(B) C, func(func(A) B) func(A) C],
|
||||
|
||||
IOE.MonadChain[E, A, A],
|
||||
IOE.MonadChain[E, A, B],
|
||||
IOE.MonadChain[E, A, C],
|
||||
IOE.MonadChain[E, B, C],
|
||||
|
||||
IOE.MonadAp[A, E, A],
|
||||
IOE.MonadAp[B, E, A],
|
||||
IOE.MonadAp[C, E, B],
|
||||
IOE.MonadAp[C, E, A],
|
||||
|
||||
IOE.MonadAp[B, E, func(A) B],
|
||||
IOE.MonadAp[func(A) C, E, func(A) B],
|
||||
|
||||
ab,
|
||||
bc,
|
||||
)
|
||||
|
||||
}
|
||||
33
ioeither/testing/laws_test.go
Normal file
33
ioeither/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))
|
||||
}
|
||||
25
ioeither/traverse.go
Normal file
25
ioeither/traverse.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package ioeither
|
||||
|
||||
import (
|
||||
G "github.com/ibm/fp-go/ioeither/generic"
|
||||
)
|
||||
|
||||
// TraverseArray transforms an array
|
||||
func TraverseArray[E, A, B any](f func(A) IOEither[E, B]) func([]A) IOEither[E, []B] {
|
||||
return G.TraverseArray[IOEither[E, B], IOEither[E, []B], []A](f)
|
||||
}
|
||||
|
||||
// SequenceArray converts a homogeneous sequence of either into an either of sequence
|
||||
func SequenceArray[E, A any](ma []IOEither[E, A]) IOEither[E, []A] {
|
||||
return G.SequenceArray[IOEither[E, A], IOEither[E, []A]](ma)
|
||||
}
|
||||
|
||||
// TraverseRecord transforms a record
|
||||
func TraverseRecord[K comparable, E, A, B any](f func(A) IOEither[E, B]) func(map[K]A) IOEither[E, map[K]B] {
|
||||
return G.TraverseRecord[IOEither[E, B], IOEither[E, map[K]B], map[K]A](f)
|
||||
}
|
||||
|
||||
// SequenceRecord converts a homogeneous sequence of either into an either of sequence
|
||||
func SequenceRecord[K comparable, E, A any](ma map[K]IOEither[E, A]) IOEither[E, map[K]A] {
|
||||
return G.SequenceRecord[IOEither[E, A], IOEither[E, map[K]A]](ma)
|
||||
}
|
||||
23
json/json.go
Normal file
23
json/json.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package json
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
E "github.com/ibm/fp-go/either"
|
||||
)
|
||||
|
||||
// Unmarshal parses a JSON data structure from bytes
|
||||
func Unmarshal[A any](data []byte) E.Either[error, A] {
|
||||
return E.TryCatchError(func() (A, error) {
|
||||
var result A
|
||||
err := json.Unmarshal(data, &result)
|
||||
return result, err
|
||||
})
|
||||
}
|
||||
|
||||
// Marshal converts a data structure to json
|
||||
func Marshal[A any](a A) E.Either[error, []byte] {
|
||||
return E.TryCatchError(func() ([]byte, error) {
|
||||
return json.Marshal(a)
|
||||
})
|
||||
}
|
||||
27
json/json_test.go
Normal file
27
json/json_test.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package json
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
E "github.com/ibm/fp-go/either"
|
||||
F "github.com/ibm/fp-go/function"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
type Json map[string]any
|
||||
|
||||
func TestJsonMarshal(t *testing.T) {
|
||||
|
||||
resRight := Unmarshal[Json]([]byte("{\"a\": \"b\"}"))
|
||||
assert.True(t, E.IsRight(resRight))
|
||||
|
||||
resLeft := Unmarshal[Json]([]byte("{\"a\""))
|
||||
assert.True(t, E.IsLeft(resLeft))
|
||||
|
||||
res1 := F.Pipe1(
|
||||
resRight,
|
||||
E.Chain(Marshal[Json]),
|
||||
)
|
||||
fmt.Println(res1)
|
||||
}
|
||||
22
json/type.go
Normal file
22
json/type.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package json
|
||||
|
||||
import (
|
||||
E "github.com/ibm/fp-go/either"
|
||||
F "github.com/ibm/fp-go/function"
|
||||
O "github.com/ibm/fp-go/option"
|
||||
)
|
||||
|
||||
func ToTypeE[A any](src any) E.Either[error, A] {
|
||||
return F.Pipe2(
|
||||
src,
|
||||
Marshal[any],
|
||||
E.Chain(Unmarshal[A]),
|
||||
)
|
||||
}
|
||||
|
||||
func ToTypeO[A any](src any) O.Option[A] {
|
||||
return F.Pipe1(
|
||||
ToTypeE[A](src),
|
||||
E.ToOption[error, A](),
|
||||
)
|
||||
}
|
||||
25
json/type_test.go
Normal file
25
json/type_test.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package json
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
E "github.com/ibm/fp-go/either"
|
||||
F "github.com/ibm/fp-go/function"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
type TestType struct {
|
||||
A string `json:"a"`
|
||||
B int `json:"b"`
|
||||
}
|
||||
|
||||
func TestToType(t *testing.T) {
|
||||
|
||||
generic := map[string]any{"a": "value", "b": 1}
|
||||
|
||||
assert.True(t, E.IsRight(ToTypeE[TestType](generic)))
|
||||
assert.True(t, E.IsRight(ToTypeE[TestType](&generic)))
|
||||
|
||||
assert.Equal(t, E.Right[error](&TestType{A: "value", B: 1}), ToTypeE[*TestType](&generic))
|
||||
assert.Equal(t, E.Right[error](TestType{A: "value", B: 1}), F.Pipe1(ToTypeE[*TestType](&generic), E.Map[error](F.Deref[TestType])))
|
||||
}
|
||||
@@ -9,8 +9,8 @@ import (
|
||||
func TraverseArrayG[GA ~[]A, GB ~[]B, A, B any](f func(A) Option[B]) func(GA) Option[GB] {
|
||||
return RA.Traverse[GA](
|
||||
Of[GB],
|
||||
MonadMap[GB, func(B) GB],
|
||||
MonadAp[GB, B],
|
||||
Map[GB, func(B) GB],
|
||||
Ap[GB, B],
|
||||
|
||||
f,
|
||||
)
|
||||
|
||||
@@ -9,8 +9,8 @@ import (
|
||||
func TraverseRecordG[GA ~map[K]A, GB ~map[K]B, K comparable, A, B any](f func(A) Option[B]) func(GA) Option[GB] {
|
||||
return RR.Traverse[GA](
|
||||
Of[GB],
|
||||
MonadMap[GB, func(B) GB],
|
||||
MonadAp[GB, B],
|
||||
Map[GB, func(B) GB],
|
||||
Ap[GB, B],
|
||||
|
||||
f,
|
||||
)
|
||||
|
||||
87
retry/generic/retry.go
Normal file
87
retry/generic/retry.go
Normal file
@@ -0,0 +1,87 @@
|
||||
package generic
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
F "github.com/ibm/fp-go/function"
|
||||
O "github.com/ibm/fp-go/option"
|
||||
R "github.com/ibm/fp-go/retry"
|
||||
)
|
||||
|
||||
// Apply policy and delay by its amount if it results in a R.
|
||||
// Returns updated status.
|
||||
// HKTSTATUS = HKT<R.RetryStatus>
|
||||
func applyAndDelay[HKTSTATUS any](
|
||||
monadOf func(R.RetryStatus) HKTSTATUS,
|
||||
monadDelay func(time.Duration) func(HKTSTATUS) HKTSTATUS,
|
||||
) func(policy R.RetryPolicy, status R.RetryStatus) HKTSTATUS {
|
||||
return func(policy R.RetryPolicy, status R.RetryStatus) HKTSTATUS {
|
||||
newStatus := R.ApplyPolicy(policy, status)
|
||||
return F.Pipe1(
|
||||
newStatus.PreviousDelay,
|
||||
O.Fold(
|
||||
F.Nullary2(F.Constant(newStatus), monadOf),
|
||||
func(delay time.Duration) HKTSTATUS {
|
||||
return monadDelay(delay)(monadOf(newStatus))
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Retry combinator for actions that don't raise exceptions, but
|
||||
// signal in their type the outcome has failed. Examples are the
|
||||
// `Option`, `Either` and `EitherT` monads.
|
||||
//
|
||||
// policy - refers to the retry policy
|
||||
// action - converts a status into an operation to be executed
|
||||
// check - checks if the result of the action needs to be retried
|
||||
func Retrying[HKTA, HKTSTATUS, A any](
|
||||
monadChain func(func(A) HKTA) func(HKTA) HKTA,
|
||||
monadChainStatus func(func(R.RetryStatus) HKTA) func(HKTSTATUS) HKTA,
|
||||
monadOf func(A) HKTA,
|
||||
monadOfStatus func(R.RetryStatus) HKTSTATUS,
|
||||
monadDelay func(time.Duration) func(HKTSTATUS) HKTSTATUS,
|
||||
|
||||
policy R.RetryPolicy,
|
||||
action func(R.RetryStatus) HKTA,
|
||||
check func(A) bool,
|
||||
) HKTA {
|
||||
// delay callback
|
||||
applyDelay := applyAndDelay(monadOfStatus, monadDelay)
|
||||
|
||||
// function to check if we need to retry or not
|
||||
checkForRetry := O.FromPredicate(check)
|
||||
|
||||
var f func(status R.RetryStatus) HKTA
|
||||
|
||||
// need some lazy init because we reference it in the chain
|
||||
f = func(status R.RetryStatus) HKTA {
|
||||
return F.Pipe2(
|
||||
status,
|
||||
action,
|
||||
monadChain(func(a A) HKTA {
|
||||
return F.Pipe3(
|
||||
a,
|
||||
checkForRetry,
|
||||
O.Map(func(a A) HKTA {
|
||||
return F.Pipe1(
|
||||
applyDelay(policy, status),
|
||||
monadChainStatus(func(status R.RetryStatus) HKTA {
|
||||
return F.Pipe1(
|
||||
status.PreviousDelay,
|
||||
O.Fold(F.Constant(monadOf(a)), func(_ time.Duration) HKTA {
|
||||
return f(status)
|
||||
}),
|
||||
)
|
||||
}),
|
||||
)
|
||||
}),
|
||||
O.GetOrElse(F.Constant(monadOf(a))),
|
||||
)
|
||||
}),
|
||||
)
|
||||
}
|
||||
// seed
|
||||
return f(R.DefaultRetryStatus)
|
||||
}
|
||||
108
retry/retry.go
Normal file
108
retry/retry.go
Normal file
@@ -0,0 +1,108 @@
|
||||
package Retry
|
||||
|
||||
import (
|
||||
"math"
|
||||
"time"
|
||||
|
||||
F "github.com/ibm/fp-go/function"
|
||||
M "github.com/ibm/fp-go/monoid"
|
||||
O "github.com/ibm/fp-go/option"
|
||||
"github.com/ibm/fp-go/ord"
|
||||
)
|
||||
|
||||
type RetryStatus struct {
|
||||
// Iteration number, where `0` is the first try
|
||||
IterNumber uint
|
||||
// Delay incurred so far from retries
|
||||
CumulativeDelay time.Duration
|
||||
// Latest attempt's delay. Will always be `none` on first run.
|
||||
PreviousDelay O.Option[time.Duration]
|
||||
}
|
||||
|
||||
// RetryPolicy is a function that takes an `RetryStatus` and
|
||||
// possibly returns a delay in milliseconds. Iteration numbers start
|
||||
// at zero and increase by one on each retry. A //None// return value from
|
||||
// the function implies we have reached the retry limit.
|
||||
type RetryPolicy = func(RetryStatus) O.Option[time.Duration]
|
||||
|
||||
const emptyDuration = time.Duration(0)
|
||||
|
||||
var ordDuration = ord.FromStrictCompare[time.Duration]()
|
||||
|
||||
// 'RetryPolicy' is a 'Monoid'. You can collapse multiple strategies into one using 'concat'.
|
||||
// The semantics of this combination are as follows:
|
||||
//
|
||||
// 1. If either policy returns 'None', the combined policy returns
|
||||
// 'None'. This can be used to inhibit after a number of retries,
|
||||
// for example.
|
||||
//
|
||||
// 2. If both policies return a delay, the larger delay will be used.
|
||||
// This is quite natural when combining multiple policies to achieve a
|
||||
// certain effect.
|
||||
var Monoid = M.FunctionMonoid[RetryStatus](O.ApplicativeMonoid(M.MakeMonoid(
|
||||
ord.MaxSemigroup(ordDuration).Concat, emptyDuration)))
|
||||
|
||||
// LimitRetries retries immediately, but only up to `i` times.
|
||||
func LimitRetries(i uint) RetryPolicy {
|
||||
pred := func(value uint) bool {
|
||||
return value < i
|
||||
}
|
||||
empty := F.Constant1[uint](emptyDuration)
|
||||
return func(status RetryStatus) O.Option[time.Duration] {
|
||||
return F.Pipe2(
|
||||
status.IterNumber,
|
||||
O.FromPredicate(pred),
|
||||
O.Map(empty),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// ConstantDelay delays with unlimited retries
|
||||
func ConstantDelay(delay time.Duration) RetryPolicy {
|
||||
return F.Constant1[RetryStatus](O.Of(delay))
|
||||
}
|
||||
|
||||
// CapDelay sets a time-upperbound for any delays that may be directed by the
|
||||
// given policy. This function does not terminate the retrying. The policy
|
||||
// capDelay(maxDelay, exponentialBackoff(n))` will never stop retrying. It
|
||||
// will reach a state where it retries forever with a delay of `maxDelay`
|
||||
// between each one. To get termination you need to use one of the
|
||||
// 'limitRetries' function variants.
|
||||
func CapDelay(maxDelay time.Duration, policy RetryPolicy) RetryPolicy {
|
||||
return F.Flow2(
|
||||
policy,
|
||||
O.Map(F.Bind1st(ord.Min(ordDuration), maxDelay)),
|
||||
)
|
||||
}
|
||||
|
||||
// ExponentialBackoff grows delay exponentially each iteration.
|
||||
// Each delay will increase by a factor of two.
|
||||
func ExponentialBackoff(delay time.Duration) RetryPolicy {
|
||||
return func(status RetryStatus) O.Option[time.Duration] {
|
||||
return O.Some(delay * time.Duration(math.Pow(2, float64(status.IterNumber))))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initial, default retry status. Exported mostly to allow user code
|
||||
* to test their handlers and retry policies.
|
||||
*/
|
||||
var DefaultRetryStatus = RetryStatus{
|
||||
IterNumber: 0,
|
||||
CumulativeDelay: 0,
|
||||
PreviousDelay: O.None[time.Duration](),
|
||||
}
|
||||
|
||||
var getOrElseDelay = O.GetOrElse(F.Constant(emptyDuration))
|
||||
|
||||
/**
|
||||
* Apply policy on status to see what the decision would be.
|
||||
*/
|
||||
func ApplyPolicy(policy RetryPolicy, status RetryStatus) RetryStatus {
|
||||
previousDelay := policy(status)
|
||||
return RetryStatus{
|
||||
IterNumber: status.IterNumber + 1,
|
||||
CumulativeDelay: status.CumulativeDelay + getOrElseDelay(previousDelay),
|
||||
PreviousDelay: previousDelay,
|
||||
}
|
||||
}
|
||||
221
tuple/gen.go
221
tuple/gen.go
@@ -3,7 +3,6 @@
|
||||
// 2023-07-14 13:19:47.4331101 +0200 CEST m=+0.011802301
|
||||
package tuple
|
||||
|
||||
|
||||
import (
|
||||
M "github.com/ibm/fp-go/monoid"
|
||||
O "github.com/ibm/fp-go/ord"
|
||||
@@ -135,7 +134,9 @@ func Monoid1[T1 any](m1 M.Monoid[T1]) M.Monoid[Tuple1[T1]] {
|
||||
// Ord1 creates n [Ord] for a [Tuple1] based on 1 [Ord]s for the contained types
|
||||
func Ord1[T1 any](o1 O.Ord[T1]) O.Ord[Tuple1[T1]] {
|
||||
return O.MakeOrd(func(l, r Tuple1[T1]) int {
|
||||
if c:= o1.Compare(l.F1, r.F1); c != 0 {return c}
|
||||
if c := o1.Compare(l.F1, r.F1); c != 0 {
|
||||
return c
|
||||
}
|
||||
return 0
|
||||
}, func(l, r Tuple1[T1]) bool {
|
||||
return o1.Equals(l.F1, r.F1)
|
||||
@@ -173,8 +174,12 @@ func Monoid2[T1, T2 any](m1 M.Monoid[T1], m2 M.Monoid[T2]) M.Monoid[Tuple2[T1, T
|
||||
// Ord2 creates n [Ord] for a [Tuple2] based on 2 [Ord]s for the contained types
|
||||
func Ord2[T1, T2 any](o1 O.Ord[T1], o2 O.Ord[T2]) O.Ord[Tuple2[T1, T2]] {
|
||||
return O.MakeOrd(func(l, r Tuple2[T1, T2]) int {
|
||||
if c:= o1.Compare(l.F1, r.F1); c != 0 {return c}
|
||||
if c:= o2.Compare(l.F2, r.F2); c != 0 {return c}
|
||||
if c := o1.Compare(l.F1, r.F1); c != 0 {
|
||||
return c
|
||||
}
|
||||
if c := o2.Compare(l.F2, r.F2); c != 0 {
|
||||
return c
|
||||
}
|
||||
return 0
|
||||
}, func(l, r Tuple2[T1, T2]) bool {
|
||||
return o1.Equals(l.F1, r.F1) && o2.Equals(l.F2, r.F2)
|
||||
@@ -212,9 +217,15 @@ func Monoid3[T1, T2, T3 any](m1 M.Monoid[T1], m2 M.Monoid[T2], m3 M.Monoid[T3])
|
||||
// Ord3 creates n [Ord] for a [Tuple3] based on 3 [Ord]s for the contained types
|
||||
func Ord3[T1, T2, T3 any](o1 O.Ord[T1], o2 O.Ord[T2], o3 O.Ord[T3]) O.Ord[Tuple3[T1, T2, T3]] {
|
||||
return O.MakeOrd(func(l, r Tuple3[T1, T2, T3]) int {
|
||||
if c:= o1.Compare(l.F1, r.F1); c != 0 {return c}
|
||||
if c:= o2.Compare(l.F2, r.F2); c != 0 {return c}
|
||||
if c:= o3.Compare(l.F3, r.F3); c != 0 {return c}
|
||||
if c := o1.Compare(l.F1, r.F1); c != 0 {
|
||||
return c
|
||||
}
|
||||
if c := o2.Compare(l.F2, r.F2); c != 0 {
|
||||
return c
|
||||
}
|
||||
if c := o3.Compare(l.F3, r.F3); c != 0 {
|
||||
return c
|
||||
}
|
||||
return 0
|
||||
}, func(l, r Tuple3[T1, T2, T3]) bool {
|
||||
return o1.Equals(l.F1, r.F1) && o2.Equals(l.F2, r.F2) && o3.Equals(l.F3, r.F3)
|
||||
@@ -252,10 +263,18 @@ func Monoid4[T1, T2, T3, T4 any](m1 M.Monoid[T1], m2 M.Monoid[T2], m3 M.Monoid[T
|
||||
// Ord4 creates n [Ord] for a [Tuple4] based on 4 [Ord]s for the contained types
|
||||
func Ord4[T1, T2, T3, T4 any](o1 O.Ord[T1], o2 O.Ord[T2], o3 O.Ord[T3], o4 O.Ord[T4]) O.Ord[Tuple4[T1, T2, T3, T4]] {
|
||||
return O.MakeOrd(func(l, r Tuple4[T1, T2, T3, T4]) int {
|
||||
if c:= o1.Compare(l.F1, r.F1); c != 0 {return c}
|
||||
if c:= o2.Compare(l.F2, r.F2); c != 0 {return c}
|
||||
if c:= o3.Compare(l.F3, r.F3); c != 0 {return c}
|
||||
if c:= o4.Compare(l.F4, r.F4); c != 0 {return c}
|
||||
if c := o1.Compare(l.F1, r.F1); c != 0 {
|
||||
return c
|
||||
}
|
||||
if c := o2.Compare(l.F2, r.F2); c != 0 {
|
||||
return c
|
||||
}
|
||||
if c := o3.Compare(l.F3, r.F3); c != 0 {
|
||||
return c
|
||||
}
|
||||
if c := o4.Compare(l.F4, r.F4); c != 0 {
|
||||
return c
|
||||
}
|
||||
return 0
|
||||
}, func(l, r Tuple4[T1, T2, T3, T4]) bool {
|
||||
return o1.Equals(l.F1, r.F1) && o2.Equals(l.F2, r.F2) && o3.Equals(l.F3, r.F3) && o4.Equals(l.F4, r.F4)
|
||||
@@ -293,11 +312,21 @@ func Monoid5[T1, T2, T3, T4, T5 any](m1 M.Monoid[T1], m2 M.Monoid[T2], m3 M.Mono
|
||||
// Ord5 creates n [Ord] for a [Tuple5] based on 5 [Ord]s for the contained types
|
||||
func Ord5[T1, T2, T3, T4, T5 any](o1 O.Ord[T1], o2 O.Ord[T2], o3 O.Ord[T3], o4 O.Ord[T4], o5 O.Ord[T5]) O.Ord[Tuple5[T1, T2, T3, T4, T5]] {
|
||||
return O.MakeOrd(func(l, r Tuple5[T1, T2, T3, T4, T5]) int {
|
||||
if c:= o1.Compare(l.F1, r.F1); c != 0 {return c}
|
||||
if c:= o2.Compare(l.F2, r.F2); c != 0 {return c}
|
||||
if c:= o3.Compare(l.F3, r.F3); c != 0 {return c}
|
||||
if c:= o4.Compare(l.F4, r.F4); c != 0 {return c}
|
||||
if c:= o5.Compare(l.F5, r.F5); c != 0 {return c}
|
||||
if c := o1.Compare(l.F1, r.F1); c != 0 {
|
||||
return c
|
||||
}
|
||||
if c := o2.Compare(l.F2, r.F2); c != 0 {
|
||||
return c
|
||||
}
|
||||
if c := o3.Compare(l.F3, r.F3); c != 0 {
|
||||
return c
|
||||
}
|
||||
if c := o4.Compare(l.F4, r.F4); c != 0 {
|
||||
return c
|
||||
}
|
||||
if c := o5.Compare(l.F5, r.F5); c != 0 {
|
||||
return c
|
||||
}
|
||||
return 0
|
||||
}, func(l, r Tuple5[T1, T2, T3, T4, T5]) bool {
|
||||
return o1.Equals(l.F1, r.F1) && o2.Equals(l.F2, r.F2) && o3.Equals(l.F3, r.F3) && o4.Equals(l.F4, r.F4) && o5.Equals(l.F5, r.F5)
|
||||
@@ -335,12 +364,24 @@ func Monoid6[T1, T2, T3, T4, T5, T6 any](m1 M.Monoid[T1], m2 M.Monoid[T2], m3 M.
|
||||
// Ord6 creates n [Ord] for a [Tuple6] based on 6 [Ord]s for the contained types
|
||||
func Ord6[T1, T2, T3, T4, T5, T6 any](o1 O.Ord[T1], o2 O.Ord[T2], o3 O.Ord[T3], o4 O.Ord[T4], o5 O.Ord[T5], o6 O.Ord[T6]) O.Ord[Tuple6[T1, T2, T3, T4, T5, T6]] {
|
||||
return O.MakeOrd(func(l, r Tuple6[T1, T2, T3, T4, T5, T6]) int {
|
||||
if c:= o1.Compare(l.F1, r.F1); c != 0 {return c}
|
||||
if c:= o2.Compare(l.F2, r.F2); c != 0 {return c}
|
||||
if c:= o3.Compare(l.F3, r.F3); c != 0 {return c}
|
||||
if c:= o4.Compare(l.F4, r.F4); c != 0 {return c}
|
||||
if c:= o5.Compare(l.F5, r.F5); c != 0 {return c}
|
||||
if c:= o6.Compare(l.F6, r.F6); c != 0 {return c}
|
||||
if c := o1.Compare(l.F1, r.F1); c != 0 {
|
||||
return c
|
||||
}
|
||||
if c := o2.Compare(l.F2, r.F2); c != 0 {
|
||||
return c
|
||||
}
|
||||
if c := o3.Compare(l.F3, r.F3); c != 0 {
|
||||
return c
|
||||
}
|
||||
if c := o4.Compare(l.F4, r.F4); c != 0 {
|
||||
return c
|
||||
}
|
||||
if c := o5.Compare(l.F5, r.F5); c != 0 {
|
||||
return c
|
||||
}
|
||||
if c := o6.Compare(l.F6, r.F6); c != 0 {
|
||||
return c
|
||||
}
|
||||
return 0
|
||||
}, func(l, r Tuple6[T1, T2, T3, T4, T5, T6]) bool {
|
||||
return o1.Equals(l.F1, r.F1) && o2.Equals(l.F2, r.F2) && o3.Equals(l.F3, r.F3) && o4.Equals(l.F4, r.F4) && o5.Equals(l.F5, r.F5) && o6.Equals(l.F6, r.F6)
|
||||
@@ -378,13 +419,27 @@ func Monoid7[T1, T2, T3, T4, T5, T6, T7 any](m1 M.Monoid[T1], m2 M.Monoid[T2], m
|
||||
// Ord7 creates n [Ord] for a [Tuple7] based on 7 [Ord]s for the contained types
|
||||
func Ord7[T1, T2, T3, T4, T5, T6, T7 any](o1 O.Ord[T1], o2 O.Ord[T2], o3 O.Ord[T3], o4 O.Ord[T4], o5 O.Ord[T5], o6 O.Ord[T6], o7 O.Ord[T7]) O.Ord[Tuple7[T1, T2, T3, T4, T5, T6, T7]] {
|
||||
return O.MakeOrd(func(l, r Tuple7[T1, T2, T3, T4, T5, T6, T7]) int {
|
||||
if c:= o1.Compare(l.F1, r.F1); c != 0 {return c}
|
||||
if c:= o2.Compare(l.F2, r.F2); c != 0 {return c}
|
||||
if c:= o3.Compare(l.F3, r.F3); c != 0 {return c}
|
||||
if c:= o4.Compare(l.F4, r.F4); c != 0 {return c}
|
||||
if c:= o5.Compare(l.F5, r.F5); c != 0 {return c}
|
||||
if c:= o6.Compare(l.F6, r.F6); c != 0 {return c}
|
||||
if c:= o7.Compare(l.F7, r.F7); c != 0 {return c}
|
||||
if c := o1.Compare(l.F1, r.F1); c != 0 {
|
||||
return c
|
||||
}
|
||||
if c := o2.Compare(l.F2, r.F2); c != 0 {
|
||||
return c
|
||||
}
|
||||
if c := o3.Compare(l.F3, r.F3); c != 0 {
|
||||
return c
|
||||
}
|
||||
if c := o4.Compare(l.F4, r.F4); c != 0 {
|
||||
return c
|
||||
}
|
||||
if c := o5.Compare(l.F5, r.F5); c != 0 {
|
||||
return c
|
||||
}
|
||||
if c := o6.Compare(l.F6, r.F6); c != 0 {
|
||||
return c
|
||||
}
|
||||
if c := o7.Compare(l.F7, r.F7); c != 0 {
|
||||
return c
|
||||
}
|
||||
return 0
|
||||
}, func(l, r Tuple7[T1, T2, T3, T4, T5, T6, T7]) bool {
|
||||
return o1.Equals(l.F1, r.F1) && o2.Equals(l.F2, r.F2) && o3.Equals(l.F3, r.F3) && o4.Equals(l.F4, r.F4) && o5.Equals(l.F5, r.F5) && o6.Equals(l.F6, r.F6) && o7.Equals(l.F7, r.F7)
|
||||
@@ -422,14 +477,30 @@ func Monoid8[T1, T2, T3, T4, T5, T6, T7, T8 any](m1 M.Monoid[T1], m2 M.Monoid[T2
|
||||
// Ord8 creates n [Ord] for a [Tuple8] based on 8 [Ord]s for the contained types
|
||||
func Ord8[T1, T2, T3, T4, T5, T6, T7, T8 any](o1 O.Ord[T1], o2 O.Ord[T2], o3 O.Ord[T3], o4 O.Ord[T4], o5 O.Ord[T5], o6 O.Ord[T6], o7 O.Ord[T7], o8 O.Ord[T8]) O.Ord[Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] {
|
||||
return O.MakeOrd(func(l, r Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]) int {
|
||||
if c:= o1.Compare(l.F1, r.F1); c != 0 {return c}
|
||||
if c:= o2.Compare(l.F2, r.F2); c != 0 {return c}
|
||||
if c:= o3.Compare(l.F3, r.F3); c != 0 {return c}
|
||||
if c:= o4.Compare(l.F4, r.F4); c != 0 {return c}
|
||||
if c:= o5.Compare(l.F5, r.F5); c != 0 {return c}
|
||||
if c:= o6.Compare(l.F6, r.F6); c != 0 {return c}
|
||||
if c:= o7.Compare(l.F7, r.F7); c != 0 {return c}
|
||||
if c:= o8.Compare(l.F8, r.F8); c != 0 {return c}
|
||||
if c := o1.Compare(l.F1, r.F1); c != 0 {
|
||||
return c
|
||||
}
|
||||
if c := o2.Compare(l.F2, r.F2); c != 0 {
|
||||
return c
|
||||
}
|
||||
if c := o3.Compare(l.F3, r.F3); c != 0 {
|
||||
return c
|
||||
}
|
||||
if c := o4.Compare(l.F4, r.F4); c != 0 {
|
||||
return c
|
||||
}
|
||||
if c := o5.Compare(l.F5, r.F5); c != 0 {
|
||||
return c
|
||||
}
|
||||
if c := o6.Compare(l.F6, r.F6); c != 0 {
|
||||
return c
|
||||
}
|
||||
if c := o7.Compare(l.F7, r.F7); c != 0 {
|
||||
return c
|
||||
}
|
||||
if c := o8.Compare(l.F8, r.F8); c != 0 {
|
||||
return c
|
||||
}
|
||||
return 0
|
||||
}, func(l, r Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]) bool {
|
||||
return o1.Equals(l.F1, r.F1) && o2.Equals(l.F2, r.F2) && o3.Equals(l.F3, r.F3) && o4.Equals(l.F4, r.F4) && o5.Equals(l.F5, r.F5) && o6.Equals(l.F6, r.F6) && o7.Equals(l.F7, r.F7) && o8.Equals(l.F8, r.F8)
|
||||
@@ -467,15 +538,33 @@ func Monoid9[T1, T2, T3, T4, T5, T6, T7, T8, T9 any](m1 M.Monoid[T1], m2 M.Monoi
|
||||
// Ord9 creates n [Ord] for a [Tuple9] based on 9 [Ord]s for the contained types
|
||||
func Ord9[T1, T2, T3, T4, T5, T6, T7, T8, T9 any](o1 O.Ord[T1], o2 O.Ord[T2], o3 O.Ord[T3], o4 O.Ord[T4], o5 O.Ord[T5], o6 O.Ord[T6], o7 O.Ord[T7], o8 O.Ord[T8], o9 O.Ord[T9]) O.Ord[Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] {
|
||||
return O.MakeOrd(func(l, r Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]) int {
|
||||
if c:= o1.Compare(l.F1, r.F1); c != 0 {return c}
|
||||
if c:= o2.Compare(l.F2, r.F2); c != 0 {return c}
|
||||
if c:= o3.Compare(l.F3, r.F3); c != 0 {return c}
|
||||
if c:= o4.Compare(l.F4, r.F4); c != 0 {return c}
|
||||
if c:= o5.Compare(l.F5, r.F5); c != 0 {return c}
|
||||
if c:= o6.Compare(l.F6, r.F6); c != 0 {return c}
|
||||
if c:= o7.Compare(l.F7, r.F7); c != 0 {return c}
|
||||
if c:= o8.Compare(l.F8, r.F8); c != 0 {return c}
|
||||
if c:= o9.Compare(l.F9, r.F9); c != 0 {return c}
|
||||
if c := o1.Compare(l.F1, r.F1); c != 0 {
|
||||
return c
|
||||
}
|
||||
if c := o2.Compare(l.F2, r.F2); c != 0 {
|
||||
return c
|
||||
}
|
||||
if c := o3.Compare(l.F3, r.F3); c != 0 {
|
||||
return c
|
||||
}
|
||||
if c := o4.Compare(l.F4, r.F4); c != 0 {
|
||||
return c
|
||||
}
|
||||
if c := o5.Compare(l.F5, r.F5); c != 0 {
|
||||
return c
|
||||
}
|
||||
if c := o6.Compare(l.F6, r.F6); c != 0 {
|
||||
return c
|
||||
}
|
||||
if c := o7.Compare(l.F7, r.F7); c != 0 {
|
||||
return c
|
||||
}
|
||||
if c := o8.Compare(l.F8, r.F8); c != 0 {
|
||||
return c
|
||||
}
|
||||
if c := o9.Compare(l.F9, r.F9); c != 0 {
|
||||
return c
|
||||
}
|
||||
return 0
|
||||
}, func(l, r Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]) bool {
|
||||
return o1.Equals(l.F1, r.F1) && o2.Equals(l.F2, r.F2) && o3.Equals(l.F3, r.F3) && o4.Equals(l.F4, r.F4) && o5.Equals(l.F5, r.F5) && o6.Equals(l.F6, r.F6) && o7.Equals(l.F7, r.F7) && o8.Equals(l.F8, r.F8) && o9.Equals(l.F9, r.F9)
|
||||
@@ -513,16 +602,36 @@ func Monoid10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any](m1 M.Monoid[T1], m2 M
|
||||
// Ord10 creates n [Ord] for a [Tuple10] based on 10 [Ord]s for the contained types
|
||||
func Ord10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any](o1 O.Ord[T1], o2 O.Ord[T2], o3 O.Ord[T3], o4 O.Ord[T4], o5 O.Ord[T5], o6 O.Ord[T6], o7 O.Ord[T7], o8 O.Ord[T8], o9 O.Ord[T9], o10 O.Ord[T10]) O.Ord[Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] {
|
||||
return O.MakeOrd(func(l, r Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]) int {
|
||||
if c:= o1.Compare(l.F1, r.F1); c != 0 {return c}
|
||||
if c:= o2.Compare(l.F2, r.F2); c != 0 {return c}
|
||||
if c:= o3.Compare(l.F3, r.F3); c != 0 {return c}
|
||||
if c:= o4.Compare(l.F4, r.F4); c != 0 {return c}
|
||||
if c:= o5.Compare(l.F5, r.F5); c != 0 {return c}
|
||||
if c:= o6.Compare(l.F6, r.F6); c != 0 {return c}
|
||||
if c:= o7.Compare(l.F7, r.F7); c != 0 {return c}
|
||||
if c:= o8.Compare(l.F8, r.F8); c != 0 {return c}
|
||||
if c:= o9.Compare(l.F9, r.F9); c != 0 {return c}
|
||||
if c:= o10.Compare(l.F10, r.F10); c != 0 {return c}
|
||||
if c := o1.Compare(l.F1, r.F1); c != 0 {
|
||||
return c
|
||||
}
|
||||
if c := o2.Compare(l.F2, r.F2); c != 0 {
|
||||
return c
|
||||
}
|
||||
if c := o3.Compare(l.F3, r.F3); c != 0 {
|
||||
return c
|
||||
}
|
||||
if c := o4.Compare(l.F4, r.F4); c != 0 {
|
||||
return c
|
||||
}
|
||||
if c := o5.Compare(l.F5, r.F5); c != 0 {
|
||||
return c
|
||||
}
|
||||
if c := o6.Compare(l.F6, r.F6); c != 0 {
|
||||
return c
|
||||
}
|
||||
if c := o7.Compare(l.F7, r.F7); c != 0 {
|
||||
return c
|
||||
}
|
||||
if c := o8.Compare(l.F8, r.F8); c != 0 {
|
||||
return c
|
||||
}
|
||||
if c := o9.Compare(l.F9, r.F9); c != 0 {
|
||||
return c
|
||||
}
|
||||
if c := o10.Compare(l.F10, r.F10); c != 0 {
|
||||
return c
|
||||
}
|
||||
return 0
|
||||
}, func(l, r Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]) bool {
|
||||
return o1.Equals(l.F1, r.F1) && o2.Equals(l.F2, r.F2) && o3.Equals(l.F3, r.F3) && o4.Equals(l.F4, r.F4) && o5.Equals(l.F5, r.F5) && o6.Equals(l.F6, r.F6) && o7.Equals(l.F7, r.F7) && o8.Equals(l.F8, r.F8) && o9.Equals(l.F9, r.F9) && o10.Equals(l.F10, r.F10)
|
||||
|
||||
Reference in New Issue
Block a user