1
0
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:
Dr. Carsten Leue
2023-07-14 17:30:58 +02:00
parent e350f70659
commit 5020437b6a
80 changed files with 5436 additions and 2110 deletions

View File

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

View File

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

@@ -0,0 +1,5 @@
package bytes
func ToString(a []byte) string {
return string(a)
}

19
bytes/monoid.go Normal file
View 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
View 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
View 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")
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

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

View File

@@ -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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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]])
)

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

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

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

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

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

View 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
View 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]),
)
}

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

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

View File

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

View File

@@ -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
View 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
View 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,
}
}

View File

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