From c07df5c771abd8cc5786acdcfcb853e46073baff Mon Sep 17 00:00:00 2001 From: "Dr. Carsten Leue" Date: Fri, 7 Jul 2023 22:31:06 +0200 Subject: [PATCH 01/16] initial checkin Signed-off-by: Dr. Carsten Leue --- README.md | 126 +++++++++- apply/sequence.go | 72 ++++++ array/array.go | 279 ++++++++++++++++++++++ array/array_test.go | 129 +++++++++++ array/eq.go | 25 ++ array/generic/array.go | 109 +++++++++ array/generic/sort.go | 26 +++ array/magma.go | 10 + array/magma_test.go | 21 ++ array/monoid.go | 43 ++++ array/monoid_test.go | 11 + array/sequence.go | 43 ++++ array/sequence_test.go | 16 ++ array/sort.go | 11 + array/sort_test.go | 21 ++ array/traverse.go | 23 ++ array/traverse_test.go | 28 +++ constraints/constraints.go | 21 ++ either/apply.go | 14 ++ either/apply_test.go | 48 ++++ either/curry.go | 61 +++++ either/either.go | 344 ++++++++++++++++++++++++++++ either/either_test.go | 96 ++++++++ either/eq.go | 25 ++ either/eq_test.go | 30 +++ either/exec/exec.go | 23 ++ either/http/request.go | 36 +++ either/legacy.go | 55 +++++ either/logger.go | 33 +++ either/logger_test.go | 22 ++ either/modern._go | 69 ++++++ either/record.go | 30 +++ either/resource.go | 29 +++ either/resource_test.go | 39 ++++ either/sequence.go | 45 ++++ either/testing/laws.go | 60 +++++ either/testing/laws_test.go | 33 +++ either/traverse.go | 54 +++++ either/traverse_test.go | 38 +++ either/variadic.go | 81 +++++++ eq/contramap.go | 11 + eq/eq.go | 43 ++++ eq/monoid.go | 18 ++ eq/testing/eq.go | 13 ++ errors/conv.go | 16 ++ errors/conv_test.go | 39 ++++ errors/identity.go | 7 + errors/message_test.go | 18 ++ errors/messages.go | 23 ++ exec/exec.go | 15 ++ fp-go.exe | Bin 0 -> 2005504 bytes function/bind.go | 52 +++++ function/constants.go | 9 + function/curry.go | 77 +++++++ function/function.go | 218 ++++++++++++++++++ function/ref.go | 9 + function/ternary.go | 10 + function/type.go | 5 + function/variadic.go | 61 +++++ go.mod | 12 + go.sum | 17 ++ internal/applicative/testing/law.go | 126 ++++++++++ internal/apply/ap.go | 94 ++++++++ internal/apply/testing/laws.go | 82 +++++++ internal/array/array.go | 61 +++++ internal/array/traverse.go | 70 ++++++ internal/chain/chain.go | 46 ++++ internal/chain/testing/laws.go | 84 +++++++ internal/exec/exec.go | 31 +++ internal/functor/testing/laws.go | 57 +++++ internal/monad/testing/laws.go | 107 +++++++++ internal/record/record.go | 33 +++ internal/record/traverse.go | 93 ++++++++ internal/utils/utils.go | 28 +++ logging/logger.go | 18 ++ magma/array.go | 29 +++ magma/magma.go | 84 +++++++ magma/magma_test.go | 19 ++ main.go | 25 ++ monoid/apply.go | 19 ++ monoid/array.go | 19 ++ monoid/function.go | 14 ++ monoid/monoid.go | 33 +++ monoid/testing/rules.go | 28 +++ number/magma.go | 11 + number/magma_test.go | 18 ++ number/monoid.go | 13 ++ number/monoid_test.go | 11 + number/semigroup.go | 11 + option/apply.go | 14 ++ option/array.go | 31 +++ option/array_test.go | 21 ++ option/eq.go | 22 ++ option/eq_test.go | 26 +++ option/legacy.go | 58 +++++ option/logger.go | 33 +++ option/modern._go | 61 +++++ option/monoid.go | 38 +++ option/number/number.go | 20 ++ option/option.go | 205 +++++++++++++++++ option/option_test.go | 116 ++++++++++ option/record.go | 31 +++ option/record_test.go | 17 ++ option/sequence.go | 58 +++++ option/sequence_test.go | 29 +++ option/testing/laws.go | 59 +++++ option/testing/laws_test.go | 32 +++ option/type.go | 14 ++ option/type_test.go | 22 ++ ord/monoid.go | 37 +++ ord/ord.go | 166 ++++++++++++++ predicate/bool.go | 25 ++ predicate/contramap.go | 15 ++ predicate/monoid.go | 43 ++++ resources/images/logo.png | Bin 0 -> 476264 bytes semigroup/apply.go | 23 ++ semigroup/array.go | 21 ++ semigroup/ord/semigroup.go | 16 ++ semigroup/semigroup.go | 46 ++++ semigroup/semigroup_test.go | 21 ++ string/monoid.go | 8 + string/monoid_test.go | 11 + string/semigroup.go | 15 ++ string/string.go | 49 ++++ string/string_test.go | 12 + tuple/monoid.go | 33 +++ tuple/ord.go | 68 ++++++ tuple/tuple.go | 97 ++++++++ 128 files changed, 5827 insertions(+), 2 deletions(-) create mode 100644 apply/sequence.go create mode 100644 array/array.go create mode 100644 array/array_test.go create mode 100644 array/eq.go create mode 100644 array/generic/array.go create mode 100644 array/generic/sort.go create mode 100644 array/magma.go create mode 100644 array/magma_test.go create mode 100644 array/monoid.go create mode 100644 array/monoid_test.go create mode 100644 array/sequence.go create mode 100644 array/sequence_test.go create mode 100644 array/sort.go create mode 100644 array/sort_test.go create mode 100644 array/traverse.go create mode 100644 array/traverse_test.go create mode 100644 constraints/constraints.go create mode 100644 either/apply.go create mode 100644 either/apply_test.go create mode 100644 either/curry.go create mode 100644 either/either.go create mode 100644 either/either_test.go create mode 100644 either/eq.go create mode 100644 either/eq_test.go create mode 100644 either/exec/exec.go create mode 100644 either/http/request.go create mode 100644 either/legacy.go create mode 100644 either/logger.go create mode 100644 either/logger_test.go create mode 100644 either/modern._go create mode 100644 either/record.go create mode 100644 either/resource.go create mode 100644 either/resource_test.go create mode 100644 either/sequence.go create mode 100644 either/testing/laws.go create mode 100644 either/testing/laws_test.go create mode 100644 either/traverse.go create mode 100644 either/traverse_test.go create mode 100644 either/variadic.go create mode 100644 eq/contramap.go create mode 100644 eq/eq.go create mode 100644 eq/monoid.go create mode 100644 eq/testing/eq.go create mode 100644 errors/conv.go create mode 100644 errors/conv_test.go create mode 100644 errors/identity.go create mode 100644 errors/message_test.go create mode 100644 errors/messages.go create mode 100644 exec/exec.go create mode 100644 fp-go.exe create mode 100644 function/bind.go create mode 100644 function/constants.go create mode 100644 function/curry.go create mode 100644 function/function.go create mode 100644 function/ref.go create mode 100644 function/ternary.go create mode 100644 function/type.go create mode 100644 function/variadic.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 internal/applicative/testing/law.go create mode 100644 internal/apply/ap.go create mode 100644 internal/apply/testing/laws.go create mode 100644 internal/array/array.go create mode 100644 internal/array/traverse.go create mode 100644 internal/chain/chain.go create mode 100644 internal/chain/testing/laws.go create mode 100644 internal/exec/exec.go create mode 100644 internal/functor/testing/laws.go create mode 100644 internal/monad/testing/laws.go create mode 100644 internal/record/record.go create mode 100644 internal/record/traverse.go create mode 100644 internal/utils/utils.go create mode 100644 logging/logger.go create mode 100644 magma/array.go create mode 100644 magma/magma.go create mode 100644 magma/magma_test.go create mode 100644 main.go create mode 100644 monoid/apply.go create mode 100644 monoid/array.go create mode 100644 monoid/function.go create mode 100644 monoid/monoid.go create mode 100644 monoid/testing/rules.go create mode 100644 number/magma.go create mode 100644 number/magma_test.go create mode 100644 number/monoid.go create mode 100644 number/monoid_test.go create mode 100644 number/semigroup.go create mode 100644 option/apply.go create mode 100644 option/array.go create mode 100644 option/array_test.go create mode 100644 option/eq.go create mode 100644 option/eq_test.go create mode 100644 option/legacy.go create mode 100644 option/logger.go create mode 100644 option/modern._go create mode 100644 option/monoid.go create mode 100644 option/number/number.go create mode 100644 option/option.go create mode 100644 option/option_test.go create mode 100644 option/record.go create mode 100644 option/record_test.go create mode 100644 option/sequence.go create mode 100644 option/sequence_test.go create mode 100644 option/testing/laws.go create mode 100644 option/testing/laws_test.go create mode 100644 option/type.go create mode 100644 option/type_test.go create mode 100644 ord/monoid.go create mode 100644 ord/ord.go create mode 100644 predicate/bool.go create mode 100644 predicate/contramap.go create mode 100644 predicate/monoid.go create mode 100644 resources/images/logo.png create mode 100644 semigroup/apply.go create mode 100644 semigroup/array.go create mode 100644 semigroup/ord/semigroup.go create mode 100644 semigroup/semigroup.go create mode 100644 semigroup/semigroup_test.go create mode 100644 string/monoid.go create mode 100644 string/monoid_test.go create mode 100644 string/semigroup.go create mode 100644 string/string.go create mode 100644 string/string_test.go create mode 100644 tuple/monoid.go create mode 100644 tuple/ord.go create mode 100644 tuple/tuple.go diff --git a/README.md b/README.md index 32ac5c3..26699c4 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,124 @@ -# fp-go -functional programming library for golang +# Functional programming library for golang + +![logo](resources/images/logo.png) + +## Design Goal + +This library aims to provide a set of data types and functions that make it easy and fun to write maintainable and testable code in golang. It encourages the following patterns: + +- write many small, testable and pure functions, i.e. functions that produce output only depending on their input and that do not execute side effects +- offer helpers to isolate side effects into lazily executed functions (IO) +- expose a consistent set of composition to create new functions from existing ones + - for each data type there exists a small set of composition functions + - these functions are called the same across all data types, so you only have to learn a small number of function names + - the semantic of functions of the same name is consistent across all data types + +### How does this play with the [🧘🏽 Zen Of Go](https://the-zen-of-go.netlify.app/)? + +#### 🧘🏽 Each package fulfils a single purpose + +βœ”οΈ Each of the top level packages (e.g. Option, Either, Task, ...) fulfils the purpose of defining the respective data type and implementing the set of common operations for this data type. + +#### 🧘🏽 Handle errors explicitly + +βœ”οΈ The library makes a clear distinction between that operations that cannot fail by design and operations that can fail. Failure is represented via the `Either` type and errors are handled explicitly by using `Either`'s monadic set of operations. + +#### 🧘🏽 Return early rather than nesting deeply + +βœ”οΈ We recommend to implement simple, small functions that implement one feature and that would typically not invoke other functions. Interaction with other functions is done by function composition and the composition makes sure to run one function after the other. In the error case the `Either` monad makes sure to skip the error path. + +#### 🧘🏽 Leave concurrency to the caller + +βœ”οΈ All operations are synchronous by default, including `Task`. Concurrency must be coded by the consumer of these functions explicitly, but the implementation is ready to deal with concurrent usage. + +#### 🧘🏽 Before you launch a goroutine, know when it will stop + +🀷🏽 This is left to the user of the library since the library itself will not start goroutines on its own. The Task monad offers support for cancellation via the golang context, though. + +#### 🧘🏽 Avoid package level state + +βœ”οΈ No package level state anywhere, this would be a significant anti-pattern + +#### 🧘🏽 Simplicity matters + +βœ”οΈ The library is simple in the sense that it offers a small, consistent interface to a variety of data types. Users can concentrate on implementing business logic rather than dealing with low level data structures. + +#### 🧘🏽 Write tests to lock in the behaviour of your package’s API + +🟑 The programming pattern suggested by this library encourages writing test cases. The library itself also has a growing number of tests, but not enough, yet. TBD + +#### 🧘🏽 If you think it’s slow, first prove it with a benchmark + +βœ”οΈ Absolutely. If you think the function composition offered by this library is too slow, please provide a benchmark. + +#### 🧘🏽 Moderation is a virtue + +βœ”οΈ The library does not implement its own goroutines and also does not require any expensive synchronization primitives. Coordination of Tasks is implemented via atomic counters without additional primitives. Channels are only used in the `Wait` function of a Task that should be invoked at most once in a complete application. + +#### 🧘🏽 Maintainability counts + +βœ”οΈ Code that consumes this library is easy to maintain because of the small and concise set of operations exposed. Also the suggested programming paradigm to decompose an application into small functions increases maintainability, because these functions are easy to understand and if they are pure, it's often sufficient to look at the type signature to understand the purpose. + +The library itself also comprises many small functions, but it's admittedly harder to maintain than code that uses it. However this asymmetry is intended because it offloads complexity from users into a central component. + +## Implementation Notes + +### Generics + +All monadic operations are implemented via generics, i.e. they offer a type safe way to compose operations. This allows for convenient IDE support and also gives confidence about the correctness of the composition at compile time. + +Downside is that this will result in different versions of each operation per type, these versions are generated by the golang compiler at build time (unlike type erasure in languages such as Java of TypeScript). This might lead to large binaries for codebases with many different types. If this is a concern, you can always implement type erasure on top, i.e. use the monadic operations with the `any` type as if generics were not supported. You loose type safety, but this might result in smaller binaries. + +### Use of the [~ Operator](https://go.googlesource.com/proposal/+/master/design/47781-parameterized-go-ast.md) + +The FP library attempts to be easy to consume and one aspect of this is the definition of higher level type definitions instead of having to use their low level equivalent. It is e.g. more convenient and readable to use + +```go +TaskEither[E, A] +``` + +than + +```go +func(func(Either.Either[E, A])) +``` + +although both are logically equivalent. At the time of this writing the go type system does not support generic type aliases, only generic type definition, i.e. it is not possible to write: + +```go +type TaskEither[E, A any] = T.Task[ET.Either[E, A]] +``` + +only + +```go +type TaskEither[E, A any] T.Task[ET.Either[E, A]] +``` + +This makes a big difference, because in the second case the type `TaskEither[E, A any]` is considered a completely new type, not compatible to its right hand side, so it's not just a shortcut but a fully new type. + +From the implementation perspective however there is no reason to restrict the implementation to the new type, it can be generic for all compatible types. The way to express this in go is the [~](https://go.googlesource.com/proposal/+/master/design/47781-parameterized-go-ast.md) operator. This comes with some quite complicated type declarations in some cases, which undermines the goal of the library to be easy to use. + +For that reason there exist sub-packages called `Generic` for all higher level types. These packages contain the fully generic implementation of the operations, preferring abstraction over usability. These packages are not meant to be used by end-users but are meant to be used by library extensions. The implementation for the convenient higher level types specializes the generic implementation for the particular higher level type, i.e. this layer does not contain any business logic but only *type magic*. + +### Higher Kinded Types + +Go does not support higher kinded types (HKT). Such types occur if a generic type itself is parametrized by another generic type. Example: + +The `Map` operation for `Task` is defined as: + +```go +func Map[A, B any](f func(A) B) func(Task[A]) Task[B] +``` + +and in fact the equivalent operations for all other mondas follow the same pattern, we could try to introduce a new type for `Task` (without a parameter) as a HKT, e.g. like so (made-up syntax, does not work in go): + +```go +func Map[HKT, A, B any](f func(A) B) func(HKT[A]) HKT[B] +``` + +this would be the completely generic method signature for all possible monads. In particular in many cases it is possible to compose functions independent of the concrete knowledge of the actual `HKT`. From the perspective of a library this is the ideal situation because then a particular algorithm only has to be implemented and tested once. + +This FP library addresses this by introducing the HKTs as individual types, e.g. `HKT[A]` would be represented as a new generic type `HKTA`. This loses the correlation to the type `A` but allows to implement generic algorithms, at the price of readability. + +For that reason these implementations are kept in the `internal` package. These are meant to be used by the library itself or by extensions, not by end users. diff --git a/apply/sequence.go b/apply/sequence.go new file mode 100644 index 0000000..5b23ca9 --- /dev/null +++ b/apply/sequence.go @@ -0,0 +1,72 @@ +package Apply + +import ( + F "github.com/ibm/fp-go/function" + T "github.com/ibm/fp-go/tuple" +) + +func tupleConstructor1[A any]() func(A) T.Tuple1[A] { + return F.Curry1(T.MakeTuple1[A]) +} + +func tupleConstructor2[A, B any]() func(A) func(B) T.Tuple2[A, B] { + return F.Curry2(T.MakeTuple2[A, B]) +} + +func tupleConstructor3[A, B, C any]() func(A) func(B) func(C) T.Tuple3[A, B, C] { + return F.Curry3(T.MakeTuple3[A, B, C]) +} + +func tupleConstructor4[A, B, C, D any]() func(A) func(B) func(C) func(D) T.Tuple4[A, B, C, D] { + return F.Curry4(T.MakeTuple4[A, B, C, D]) +} + +func SequenceT1[A, HKTA, HKT1A any]( + fmap func(HKTA, func(A) T.Tuple1[A]) HKT1A, + a HKTA) HKT1A { + return fmap(a, tupleConstructor1[A]()) +} + +// HKTA = HKT[A] +// HKTB = HKT[B] +// HKT2AB = HKT[Tuple[A, B]] +// HKTFB2AB = HKT[func(B)Tuple[A, B]] +func SequenceT2[A, B, HKTA, HKTB, HKTFB2AB, HKT2AB any]( + fmap func(HKTA, func(A) func(B) T.Tuple2[A, B]) HKTFB2AB, + fap1 func(HKTFB2AB, HKTB) HKT2AB, + a HKTA, b HKTB, +) HKT2AB { + return fap1(fmap(a, tupleConstructor2[A, B]()), b) +} + +// HKTA = HKT[A] +// HKTB = HKT[B] +// HKTC = HKT[C] +// HKT3ABC = HKT[Tuple[A, B, C]] +// HKTFB3ABC = HKT[func(B)func(C)Tuple[A, B, C]] +// HKTFC3ABC = HKT[func(C)Tuple[A, B, C]] +func SequenceT3[A, B, C, HKTA, HKTB, HKTC, HKTFB3ABC, HKTFC3ABC, HKT3ABC any]( + fmap func(HKTA, func(A) func(B) func(C) T.Tuple3[A, B, C]) HKTFB3ABC, + fap1 func(HKTFB3ABC, HKTB) HKTFC3ABC, + fap2 func(HKTFC3ABC, HKTC) HKT3ABC, + + a HKTA, b HKTB, c HKTC) HKT3ABC { + return fap2(fap1(fmap(a, tupleConstructor3[A, B, C]()), b), c) +} + +// HKTA = HKT[A] +// HKTB = HKT[B] +// HKTC = HKT[C] +// HKT3ABCD = HKT[Tuple[A, B, C, D]] +// HKTFB3ABCD = HKT[func(B)func(C)func(D)Tuple[A, B, C, D]] +// HKTFC3ABCD = HKT[func(C)func(D)Tuple[A, B, C, D]] +// HKTFD3ABCD = HKT[func(D)Tuple[A, B, C, D]] +func SequenceT4[A, B, C, D, HKTA, HKTB, HKTC, HKTD, HKTFB4ABCD, HKTFC4ABCD, HKTFD4ABCD, HKT4ABCD any]( + fmap func(HKTA, func(A) func(B) func(C) func(D) T.Tuple4[A, B, C, D]) HKTFB4ABCD, + fap1 func(HKTFB4ABCD, HKTB) HKTFC4ABCD, + fap2 func(HKTFC4ABCD, HKTC) HKTFD4ABCD, + fap3 func(HKTFD4ABCD, HKTD) HKT4ABCD, + + a HKTA, b HKTB, c HKTC, d HKTD) HKT4ABCD { + return fap3(fap2(fap1(fmap(a, tupleConstructor4[A, B, C, D]()), b), c), d) +} diff --git a/array/array.go b/array/array.go new file mode 100644 index 0000000..5351a04 --- /dev/null +++ b/array/array.go @@ -0,0 +1,279 @@ +package array + +import ( + G "github.com/ibm/fp-go/array/generic" + F "github.com/ibm/fp-go/function" + "github.com/ibm/fp-go/internal/array" + M "github.com/ibm/fp-go/monoid" + O "github.com/ibm/fp-go/option" + "github.com/ibm/fp-go/tuple" +) + +// From constructs an array from a set of variadic arguments +func From[A any](data ...A) []A { + return G.From[[]A](data...) +} + +// MakeBy returns a `Array` of length `n` with element `i` initialized with `f(i)`. +func MakeBy[A any](n int, f func(int) A) []A { + // sanity check + if n <= 0 { + return Empty[A]() + } + // run the generator function across the input + as := make([]A, n) + for i := n - 1; i >= 0; i-- { + as[i] = f(i) + } + return as +} + +// Replicate creates a `Array` containing a value repeated the specified number of times. +func Replicate[A any](n int, a A) []A { + return MakeBy(n, F.Constant1[int](a)) +} + +func MonadMap[A, B any](as []A, f func(a A) B) []B { + return G.MonadMap[[]A, []B](as, f) +} + +func MonadMapRef[A, B any](as []A, f func(a *A) B) []B { + count := len(as) + bs := make([]B, count) + for i := count - 1; i >= 0; i-- { + bs[i] = f(&as[i]) + } + return bs +} + +func Map[A, B any](f func(a A) B) func([]A) []B { + return F.Bind2nd(MonadMap[A, B], f) +} + +func MapRef[A, B any](f func(a *A) B) func([]A) []B { + return F.Bind2nd(MonadMapRef[A, B], f) +} + +func filter[A any](fa []A, pred func(A) bool) []A { + var result []A + count := len(fa) + for i := 0; i < count; i++ { + a := fa[i] + if pred(a) { + result = append(result, a) + } + } + return result +} + +func filterRef[A any](fa []A, pred func(a *A) bool) []A { + var result []A + count := len(fa) + for i := 0; i < count; i++ { + a := fa[i] + if pred(&a) { + result = append(result, a) + } + } + return result +} + +func filterMapRef[A, B any](fa []A, pred func(a *A) bool, f func(a *A) B) []B { + var result []B + count := len(fa) + for i := 0; i < count; i++ { + a := fa[i] + if pred(&a) { + result = append(result, f(&a)) + } + } + return result +} + +func Filter[A any](pred func(A) bool) func([]A) []A { + return F.Bind2nd(filter[A], pred) +} + +func FilterRef[A any](pred func(*A) bool) func([]A) []A { + return F.Bind2nd(filterRef[A], pred) +} + +func MonadFilterMap[A, B any](fa []A, f func(a A) O.Option[B]) []B { + return G.MonadFilterMap[[]A, []B](fa, f) +} + +func FilterMap[A, B any](f func(a A) O.Option[B]) func([]A) []B { + return G.FilterMap[[]A, []B](f) +} + +func FilterMapRef[A, B any](pred func(a *A) bool, f func(a *A) B) func([]A) []B { + return func(fa []A) []B { + return filterMapRef(fa, pred, f) + } +} + +func reduceRef[A, B any](fa []A, f func(B, *A) B, initial B) B { + current := initial + count := len(fa) + for i := 0; i < count; i++ { + current = f(current, &fa[i]) + } + return current +} + +func Reduce[A, B any](f func(B, A) B, initial B) func([]A) B { + return func(as []A) B { + return array.Reduce(as, f, initial) + } +} + +func ReduceRef[A, B any](f func(B, *A) B, initial B) func([]A) B { + return func(as []A) B { + return reduceRef(as, f, initial) + } +} + +func Append[A any](as []A, a A) []A { + return G.Append(as, a) +} + +func IsEmpty[A any](as []A) bool { + return array.IsEmpty(as) +} + +func IsNonEmpty[A any](as []A) bool { + return len(as) > 0 +} + +func Empty[A any]() []A { + return G.Empty[[]A]() +} + +func Zero[A any]() []A { + return Empty[A]() +} + +// Of constructs a single element array +func Of[A any](a A) []A { + return G.Of[[]A](a) +} + +func MonadChain[A, B any](fa []A, f func(a A) []B) []B { + return array.Reduce(fa, func(bs []B, a A) []B { + return append(bs, f(a)...) + }, Zero[B]()) +} + +func Chain[A, B any](f func(a A) []B) func([]A) []B { + return F.Bind2nd(MonadChain[A, B], f) +} + +func MonadAp[A, B any](fab []func(A) B, fa []A) []B { + return MonadChain(fab, F.Bind1st(MonadMap[A, B], fa)) +} + +func Ap[A, B any](fa []A) func([]func(A) B) []B { + return F.Bind2nd(MonadAp[A, B], fa) +} + +func Match[A, B any](onEmpty func() B, onNonEmpty func([]A) B) func([]A) B { + return func(as []A) B { + if IsEmpty(as) { + return onEmpty() + } + return onNonEmpty(as) + } +} + +func Tail[A any](as []A) O.Option[[]A] { + return G.Tail(as) +} + +func Head[A any](as []A) O.Option[A] { + return G.Head(as) +} + +func First[A any](as []A) O.Option[A] { + return G.First(as) +} + +func Last[A any](as []A) O.Option[A] { + return G.Last(as) +} + +func PrependAll[A any](middle A) func([]A) []A { + return func(as []A) []A { + count := len(as) + dst := count * 2 + result := make([]A, dst) + for i := count - 1; i >= 0; i-- { + dst-- + result[dst] = as[i] + dst-- + result[dst] = middle + } + return result + } +} + +func Intersperse[A any](middle A) func([]A) []A { + prepend := PrependAll(middle) + return func(as []A) []A { + if IsEmpty(as) { + return as + } + return prepend(as)[1:] + } +} + +func Intercalate[A any](m M.Monoid[A]) func(A) func([]A) A { + concatAll := ConcatAll[A](m)(m.Empty()) + return func(middle A) func([]A) A { + return Match(m.Empty, F.Flow2(Intersperse(middle), concatAll)) + } +} + +func Flatten[A any](mma [][]A) []A { + return MonadChain(mma, F.Identity[[]A]) +} + +func Slice[A any](low, high int) func(as []A) []A { + return array.Slice[[]A](low, high) +} + +func Lookup[A any](idx int) func([]A) O.Option[A] { + return G.Lookup[[]A](idx) +} + +func UpsertAt[A any](a A) func([]A) []A { + return G.UpsertAt[[]A](a) +} + +func Size[A any](as []A) int { + return G.Size(as) +} + +func MonadPartition[A any](as []A, pred func(A) bool) tuple.Tuple2[[]A, []A] { + return G.MonadPartition(as, pred) +} + +// Partition creates two new arrays out of one, the left result contains the elements +// for which the predicate returns false, the right one those for which the predicate returns true +func Partition[A any](pred func(A) bool) func([]A) tuple.Tuple2[[]A, []A] { + return G.Partition[[]A](pred) +} + +// IsNil checks if the array is set to nil +func IsNil[A any](as []A) bool { + return array.IsNil(as) +} + +// IsNonNil checks if the array is set to nil +func IsNonNil[A any](as []A) bool { + return array.IsNonNil(as) +} + +// ConstNil returns a nil array +func ConstNil[A any]() []A { + return array.ConstNil[[]A]() +} diff --git a/array/array_test.go b/array/array_test.go new file mode 100644 index 0000000..88fbe36 --- /dev/null +++ b/array/array_test.go @@ -0,0 +1,129 @@ +package array + +import ( + "strings" + "testing" + + F "github.com/ibm/fp-go/function" + "github.com/ibm/fp-go/internal/utils" + O "github.com/ibm/fp-go/option" + S "github.com/ibm/fp-go/string" + T "github.com/ibm/fp-go/tuple" + "github.com/stretchr/testify/assert" +) + +func TestMap1(t *testing.T) { + + src := []string{"a", "b", "c"} + + up := Map(strings.ToUpper)(src) + + var up1 = []string{} + for _, s := range src { + up1 = append(up1, strings.ToUpper(s)) + } + + var up2 = []string{} + for i := range src { + up2 = append(up2, strings.ToUpper(src[i])) + } + + assert.Equal(t, up, up1) + assert.Equal(t, up, up2) +} + +func TestMap(t *testing.T) { + + mapper := Map(utils.Upper) + + src := []string{"a", "b", "c"} + + dst := mapper(src) + + assert.Equal(t, dst, []string{"A", "B", "C"}) +} + +func TestReduce(t *testing.T) { + + values := MakeBy(101, F.Identity[int]) + + sum := func(val int, current int) int { + return val + current + } + reducer := Reduce(sum, 0) + + result := reducer(values) + assert.Equal(t, result, 5050) + +} + +func TestEmpty(t *testing.T) { + assert.True(t, IsNonEmpty(MakeBy(101, F.Identity[int]))) + assert.True(t, IsEmpty([]int{})) +} + +func TestAp(t *testing.T) { + assert.Equal(t, + []int{2, 4, 6, 3, 6, 9}, + F.Pipe1( + []func(int) int{ + utils.Double, + utils.Triple, + }, + Ap[int, int]([]int{1, 2, 3}), + ), + ) +} + +func TestIntercalate(t *testing.T) { + is := Intercalate(S.Monoid)("-") + + assert.Equal(t, "", is(Empty[string]())) + assert.Equal(t, "a", is([]string{"a"})) + assert.Equal(t, "a-b-c", is([]string{"a", "b", "c"})) + assert.Equal(t, "a--c", is([]string{"a", "", "c"})) + assert.Equal(t, "a-b", is([]string{"a", "b"})) + assert.Equal(t, "a-b-c-d", is([]string{"a", "b", "c", "d"})) +} + +func TestPrependAll(t *testing.T) { + empty := Empty[int]() + prep := PrependAll(0) + assert.Equal(t, empty, prep(empty)) + assert.Equal(t, []int{0, 1, 0, 2, 0, 3}, prep([]int{1, 2, 3})) + assert.Equal(t, []int{0, 1}, prep([]int{1})) + assert.Equal(t, []int{0, 1, 0, 2, 0, 3, 0, 4}, prep([]int{1, 2, 3, 4})) +} + +func TestFlatten(t *testing.T) { + assert.Equal(t, []int{1, 2, 3}, Flatten([][]int{{1}, {2}, {3}})) +} + +func TestLookup(t *testing.T) { + data := []int{0, 1, 2} + none := O.None[int]() + + assert.Equal(t, none, Lookup[int](-1)(data)) + assert.Equal(t, none, Lookup[int](10)(data)) + assert.Equal(t, O.Some(1), Lookup[int](1)(data)) +} + +func TestSlice(t *testing.T) { + data := []int{0, 1, 2, 3} + + assert.Equal(t, []int{1, 2}, Slice[int](1, 3)(data)) +} + +func TestFrom(t *testing.T) { + assert.Equal(t, []int{1, 2, 3}, From(1, 2, 3)) +} + +func TestPartition(t *testing.T) { + + pred := func(n int) bool { + return n > 2 + } + + assert.Equal(t, T.MakeTuple2(Empty[int](), Empty[int]()), Partition(pred)(Empty[int]())) + assert.Equal(t, T.MakeTuple2(From(1), From(3)), Partition(pred)(From(1, 3))) +} diff --git a/array/eq.go b/array/eq.go new file mode 100644 index 0000000..49cecbc --- /dev/null +++ b/array/eq.go @@ -0,0 +1,25 @@ +package array + +import ( + E "github.com/ibm/fp-go/eq" +) + +func equals[T any](left []T, right []T, eq func(T, T) bool) bool { + if len(left) != len(right) { + return false + } + for i, v1 := range left { + v2 := right[i] + if !eq(v1, v2) { + return false + } + } + return true +} + +func Eq[T any](e E.Eq[T]) E.Eq[[]T] { + eq := e.Equals + return E.FromEquals(func(left, right []T) bool { + return equals(left, right, eq) + }) +} diff --git a/array/generic/array.go b/array/generic/array.go new file mode 100644 index 0000000..f652fa7 --- /dev/null +++ b/array/generic/array.go @@ -0,0 +1,109 @@ +package generic + +import ( + F "github.com/ibm/fp-go/function" + "github.com/ibm/fp-go/internal/array" + O "github.com/ibm/fp-go/option" + "github.com/ibm/fp-go/tuple" +) + +// Of constructs a single element array +func Of[GA ~[]A, A any](value A) GA { + return GA{value} +} + +// From constructs an array from a set of variadic arguments +func From[GA ~[]A, A any](data ...A) GA { + return data +} + +func Lookup[GA ~[]A, A any](idx int) func(GA) O.Option[A] { + none := O.None[A]() + if idx < 0 { + return F.Constant1[GA](none) + } + return func(as GA) O.Option[A] { + if idx < len(as) { + return O.Some(as[idx]) + } + return none + } +} + +func Tail[GA ~[]A, A any](as GA) O.Option[GA] { + if array.IsEmpty(as) { + return O.None[GA]() + } + return O.Some(as[1:]) +} + +func Head[GA ~[]A, A any](as GA) O.Option[A] { + if array.IsEmpty(as) { + return O.None[A]() + } + return O.Some(as[0]) +} + +func First[GA ~[]A, A any](as GA) O.Option[A] { + return Head(as) +} + +func Last[GA ~[]A, A any](as GA) O.Option[A] { + if array.IsEmpty(as) { + return O.None[A]() + } + return O.Some(as[len(as)-1]) +} + +func Append[GA ~[]A, A any](as GA, a A) GA { + return array.Append(as, a) +} + +func Empty[GA ~[]A, A any]() GA { + return array.Empty[GA]() +} + +func UpsertAt[GA ~[]A, A any](a A) func(GA) GA { + return array.UpsertAt[GA](a) +} + +func MonadMap[GA ~[]A, GB ~[]B, A, B any](as GA, f func(a A) B) GB { + return array.MonadMap[GA, GB](as, f) +} + +func Size[GA ~[]A, A any](as GA) int { + return len(as) +} + +func filterMap[GA ~[]A, GB ~[]B, A, B any](fa GA, f func(a A) O.Option[B]) GB { + return array.Reduce(fa, func(bs GB, a A) GB { + return O.MonadFold(f(a), F.Constant(bs), F.Bind1st(Append[GB, B], bs)) + }, Empty[GB]()) +} + +func MonadFilterMap[GA ~[]A, GB ~[]B, A, B any](fa GA, f func(a A) O.Option[B]) GB { + return filterMap[GA, GB](fa, f) +} + +func FilterMap[GA ~[]A, GB ~[]B, A, B any](f func(a A) O.Option[B]) func(GA) GB { + return F.Bind2nd(MonadFilterMap[GA, GB, A, B], f) +} + +func MonadPartition[GA ~[]A, A any](as GA, pred func(A) bool) tuple.Tuple2[GA, GA] { + left := Empty[GA]() + right := Empty[GA]() + array.Reduce(as, func(c bool, a A) bool { + if pred(a) { + right = append(right, a) + } else { + left = append(left, a) + } + return c + }, true) + // returns the partition + return tuple.MakeTuple2(left, right) +} + +func Partition[GA ~[]A, A any](pred func(A) bool) func(GA) tuple.Tuple2[GA, GA] { + return F.Bind2nd(MonadPartition[GA, A], pred) +} diff --git a/array/generic/sort.go b/array/generic/sort.go new file mode 100644 index 0000000..4e63cb3 --- /dev/null +++ b/array/generic/sort.go @@ -0,0 +1,26 @@ +package generic + +import ( + "sort" + + O "github.com/ibm/fp-go/ord" +) + +// Sort implements a stable sort on the array given the provided ordering +func Sort[GA ~[]T, T any](ord O.Ord[T]) func(ma GA) GA { + + return func(ma GA) GA { + // nothing to sort + l := len(ma) + if l < 2 { + return ma + } + // copy + cpy := make(GA, l) + copy(cpy, ma) + sort.Slice(cpy, func(i, j int) bool { + return ord.Compare(cpy[i], cpy[j]) < 0 + }) + return cpy + } +} diff --git a/array/magma.go b/array/magma.go new file mode 100644 index 0000000..ae82396 --- /dev/null +++ b/array/magma.go @@ -0,0 +1,10 @@ +package array + +import ( + F "github.com/ibm/fp-go/function" + M "github.com/ibm/fp-go/magma" +) + +func ConcatAll[A any](m M.Magma[A]) func(A) func([]A) A { + return F.Bind1st(Reduce[A, A], m.Concat) +} diff --git a/array/magma_test.go b/array/magma_test.go new file mode 100644 index 0000000..077a186 --- /dev/null +++ b/array/magma_test.go @@ -0,0 +1,21 @@ +package array + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + M "github.com/ibm/fp-go/magma" +) + +var subInt = M.MakeMagma(func(first int, second int) int { + return first - second +}) + +func TestConcatAll(t *testing.T) { + + var subAll = ConcatAll(subInt)(0) + + assert.Equal(t, subAll([]int{1, 2, 3}), -6) + +} diff --git a/array/monoid.go b/array/monoid.go new file mode 100644 index 0000000..c869e60 --- /dev/null +++ b/array/monoid.go @@ -0,0 +1,43 @@ +package array + +import ( + "github.com/ibm/fp-go/internal/array" + M "github.com/ibm/fp-go/monoid" +) + +func concat[T any](left, right []T) []T { + // some performance checks + ll := len(left) + lr := len(right) + if ll == 0 { + return right + } + if lr == 0 { + return left + } + // need to copy + buf := make([]T, ll+lr) + copy(buf[copy(buf, left):], right) + return buf +} + +func Monoid[T any]() M.Monoid[[]T] { + return M.MakeMonoid(concat[T], Empty[T]()) +} + +func addLen[A any](count int, data []A) int { + return count + len(data) +} + +// ConcatAll efficiently concatenates the input arrays into a final array +func ArrayConcatAll[A any](data ...[]A) []A { + // get the full size + count := array.Reduce(data, addLen[A], 0) + buf := make([]A, count) + // copy + array.Reduce(data, func(idx int, seg []A) int { + return idx + copy(buf[idx:], seg) + }, 0) + // returns the final array + return buf +} diff --git a/array/monoid_test.go b/array/monoid_test.go new file mode 100644 index 0000000..5c7cc05 --- /dev/null +++ b/array/monoid_test.go @@ -0,0 +1,11 @@ +package array + +import ( + "testing" + + M "github.com/ibm/fp-go/monoid/testing" +) + +func TestMonoid(t *testing.T) { + M.AssertLaws(t, Monoid[int]())([][]int{{}, {1}, {1, 2}}) +} diff --git a/array/sequence.go b/array/sequence.go new file mode 100644 index 0000000..513ca69 --- /dev/null +++ b/array/sequence.go @@ -0,0 +1,43 @@ +package array + +import ( + F "github.com/ibm/fp-go/function" + O "github.com/ibm/fp-go/option" +) + +// We need to pass the members of the applicative explicitly, because golang does neither support higher kinded types nor template methods on structs or interfaces + +// HKTA = HKT +// HKTRA = HKT<[]A> +// HKTFRA = HKT + +// Sequence takes an `Array` where elements are `HKT` (higher kinded type) and, +// using an applicative of that `HKT`, returns an `HKT` of `[]A`. +// e.g. it can turn an `[]Either[error, string]` into an `Either[error, []string]`. +// +// Sequence requires an `Applicative` of the `HKT` you are targeting, e.g. to turn an +// `[]Either[E, A]` into an `Either[E, []A]`, it needs an +// Applicative` for `Either`, to to turn an `[]Option[A]` into an `Option[ []A]`, +// it needs an `Applicative` for `Option`. +func Sequence[A, HKTA, HKTRA, HKTFRA any]( + _of func([]A) HKTRA, + _map func(HKTRA, func([]A) func(A) []A) HKTFRA, + _ap func(HKTFRA, HKTA) HKTRA, +) func([]HKTA) HKTRA { + ca := F.Curry2(Append[A]) + return Reduce(func(fas HKTRA, fa HKTA) HKTRA { + return _ap( + _map(fas, ca), + fa, + ) + }, _of(Empty[A]())) +} + +// ArrayOption returns a function to convert sequence of options into an option of a sequence +func ArrayOption[A any]() func([]O.Option[A]) O.Option[[]A] { + return Sequence( + O.Of[[]A], + O.MonadMap[[]A, func(A) []A], + O.MonadAp[A, []A], + ) +} diff --git a/array/sequence_test.go b/array/sequence_test.go new file mode 100644 index 0000000..e05a1b6 --- /dev/null +++ b/array/sequence_test.go @@ -0,0 +1,16 @@ +package array + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + O "github.com/ibm/fp-go/option" +) + +func TestSequenceOption(t *testing.T) { + seq := ArrayOption[int]() + + assert.Equal(t, O.Of([]int{1, 3}), seq([]O.Option[int]{O.Of(1), O.Of(3)})) + assert.Equal(t, O.None[[]int](), seq([]O.Option[int]{O.Of(1), O.None[int]()})) +} diff --git a/array/sort.go b/array/sort.go new file mode 100644 index 0000000..4b94ffd --- /dev/null +++ b/array/sort.go @@ -0,0 +1,11 @@ +package array + +import ( + G "github.com/ibm/fp-go/array/generic" + O "github.com/ibm/fp-go/ord" +) + +// Sort implements a stable sort on the array given the provided ordering +func Sort[T any](ord O.Ord[T]) func(ma []T) []T { + return G.Sort[[]T](ord) +} diff --git a/array/sort_test.go b/array/sort_test.go new file mode 100644 index 0000000..124132a --- /dev/null +++ b/array/sort_test.go @@ -0,0 +1,21 @@ +package array + +import ( + "testing" + + O "github.com/ibm/fp-go/ord" + "github.com/stretchr/testify/assert" +) + +func TestSort(t *testing.T) { + + ordInt := O.FromStrictCompare[int]() + + input := []int{2, 1, 3} + + res := Sort(ordInt)(input) + + assert.Equal(t, []int{1, 2, 3}, res) + assert.Equal(t, []int{2, 1, 3}, input) + +} diff --git a/array/traverse.go b/array/traverse.go new file mode 100644 index 0000000..1fa62ce --- /dev/null +++ b/array/traverse.go @@ -0,0 +1,23 @@ +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, + + f func(A) HKTB) func([]A) HKTRB { + return array.Traverse[[]A](_of, _map, _ap, 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, + + ta []A, + f func(A) HKTB) HKTRB { + + return array.MonadTraverse(_of, _map, _ap, ta, f) +} diff --git a/array/traverse_test.go b/array/traverse_test.go new file mode 100644 index 0000000..f50449e --- /dev/null +++ b/array/traverse_test.go @@ -0,0 +1,28 @@ +package array + +import ( + "testing" + + O "github.com/ibm/fp-go/option" + "github.com/stretchr/testify/assert" +) + +type ArrayType = []int + +func TestTraverse(t *testing.T) { + + traverse := Traverse( + O.Of[ArrayType], + O.MonadMap[ArrayType, func(int) ArrayType], + O.MonadAp[int, ArrayType], + + func(n int) O.Option[int] { + if n%2 == 0 { + return O.None[int]() + } + return O.Of(n) + }) + + assert.Equal(t, O.None[[]int](), traverse(ArrayType{1, 2})) + assert.Equal(t, O.Of(ArrayType{1, 3}), traverse(ArrayType{1, 3})) +} diff --git a/constraints/constraints.go b/constraints/constraints.go new file mode 100644 index 0000000..ebab42d --- /dev/null +++ b/constraints/constraints.go @@ -0,0 +1,21 @@ +package constraints + +type Ordered interface { + Integer | Float | ~string +} + +type Signed interface { + ~int | ~int8 | ~int16 | ~int32 | ~int64 +} + +type Unsigned interface { + ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr +} + +type Integer interface { + Signed | Unsigned +} + +type Float interface { + ~float32 | ~float64 +} diff --git a/either/apply.go b/either/apply.go new file mode 100644 index 0000000..808ea3e --- /dev/null +++ b/either/apply.go @@ -0,0 +1,14 @@ +package either + +import ( + M "github.com/ibm/fp-go/monoid" + S "github.com/ibm/fp-go/semigroup" +) + +func ApplySemigroup[E, A any](s S.Semigroup[A]) S.Semigroup[Either[E, A]] { + return S.ApplySemigroup(MonadMap[E, A, func(A) A], MonadAp[E, A, A], s) +} + +func ApplicativeMonoid[E, A any](m M.Monoid[A]) M.Monoid[Either[E, A]] { + return M.ApplicativeMonoid(Of[E, A], MonadMap[E, A, func(A) A], MonadAp[E, A, A], m) +} diff --git a/either/apply_test.go b/either/apply_test.go new file mode 100644 index 0000000..d7c66e7 --- /dev/null +++ b/either/apply_test.go @@ -0,0 +1,48 @@ +package either + +import ( + "testing" + + M "github.com/ibm/fp-go/monoid/testing" + N "github.com/ibm/fp-go/number" + "github.com/stretchr/testify/assert" +) + +func TestApplySemigroup(t *testing.T) { + + sg := ApplySemigroup[string](N.SemigroupSum[int]()) + + la := Left[string, int]("a") + lb := Left[string, int]("b") + r1 := Right[string](1) + r2 := Right[string](2) + r3 := Right[string](3) + + assert.Equal(t, la, sg.Concat(la, lb)) + assert.Equal(t, lb, sg.Concat(r1, lb)) + assert.Equal(t, la, sg.Concat(la, r2)) + assert.Equal(t, lb, sg.Concat(r1, lb)) + assert.Equal(t, r3, sg.Concat(r1, r2)) +} + +func TestApplicativeMonoid(t *testing.T) { + + m := ApplicativeMonoid[string](N.MonoidSum[int]()) + + la := Left[string, int]("a") + lb := Left[string, int]("b") + r1 := Right[string](1) + r2 := Right[string](2) + r3 := Right[string](3) + + assert.Equal(t, la, m.Concat(la, m.Empty())) + assert.Equal(t, lb, m.Concat(m.Empty(), lb)) + assert.Equal(t, r1, m.Concat(r1, m.Empty())) + assert.Equal(t, r2, m.Concat(m.Empty(), r2)) + assert.Equal(t, r3, m.Concat(r1, r2)) +} + +func TestApplicativeMonoidLaws(t *testing.T) { + m := ApplicativeMonoid[string](N.MonoidSum[int]()) + M.AssertLaws(t, m)([]Either[string, int]{Left[string, int]("a"), Right[string](1)}) +} diff --git a/either/curry.go b/either/curry.go new file mode 100644 index 0000000..a08c0a2 --- /dev/null +++ b/either/curry.go @@ -0,0 +1,61 @@ +package either + +import ( + F "github.com/ibm/fp-go/function" +) + +// these function curry a golang function that returns an error into its curried version that returns an either + +func Curry0[R any](f func() (R, error)) func() Either[error, R] { + return Eitherize0(f) +} + +func Curry1[T1, R any](f func(T1) (R, error)) func(T1) Either[error, R] { + return Eitherize1(f) +} + +func Curry2[T1, T2, R any](f func(T1, T2) (R, error)) func(T1) func(T2) Either[error, R] { + return F.Curry2(Eitherize2(f)) +} + +func Curry3[T1, T2, T3, R any](f func(T1, T2, T3) (R, error)) func(T1) func(T2) func(T3) Either[error, R] { + return F.Curry3(Eitherize3(f)) +} + +func Curry4[T1, T2, T3, T4, R any](f func(T1, T2, T3, T4) (R, error)) func(T1) func(T2) func(T3) func(T4) Either[error, R] { + return F.Curry4(Eitherize4(f)) +} + +func Uncurry0[R any](f func() Either[error, R]) func() (R, error) { + return func() (R, error) { + return UnwrapError(f()) + } +} + +func Uncurry1[T1, R any](f func(T1) Either[error, R]) func(T1) (R, error) { + uc := F.Uncurry1(f) + return func(t1 T1) (R, error) { + return UnwrapError(uc(t1)) + } +} + +func Uncurry2[T1, T2, R any](f func(T1) func(T2) Either[error, R]) func(T1, T2) (R, error) { + uc := F.Uncurry2(f) + return func(t1 T1, t2 T2) (R, error) { + return UnwrapError(uc(t1, t2)) + } +} + +func Uncurry3[T1, T2, T3, R any](f func(T1) func(T2) func(T3) Either[error, R]) func(T1, T2, T3) (R, error) { + uc := F.Uncurry3(f) + return func(t1 T1, t2 T2, t3 T3) (R, error) { + return UnwrapError(uc(t1, t2, t3)) + } +} + +func Uncurry4[T1, T2, T3, T4, R any](f func(T1) func(T2) func(T3) func(T4) Either[error, R]) func(T1, T2, T3, T4) (R, error) { + uc := F.Uncurry4(f) + return func(t1 T1, t2 T2, t3 T3, t4 T4) (R, error) { + return UnwrapError(uc(t1, t2, t3, t4)) + } +} diff --git a/either/either.go b/either/either.go new file mode 100644 index 0000000..ad1b61b --- /dev/null +++ b/either/either.go @@ -0,0 +1,344 @@ +// package either implements the Either monad +// +// A data type that can be of either of two types but not both. This is +// typically used to carry an error or a return value +package either + +import ( + E "github.com/ibm/fp-go/errors" + F "github.com/ibm/fp-go/function" + O "github.com/ibm/fp-go/option" +) + +func Of[E, A any](value A) Either[E, A] { + return F.Pipe1(value, Right[E, A]) +} + +func FromIO[E, A any](f func() A) Either[E, A] { + return F.Pipe1(f(), Right[E, A]) +} + +func MonadAp[E, A, B any](fab Either[E, func(a A) B], fa Either[E, A]) Either[E, B] { + return fold(fab, Left[E, B], func(ab func(A) B) Either[E, B] { + return fold(fa, Left[E, B], F.Flow2(ab, Right[E, B])) + }) +} + +func Ap[E, A, B any](fa Either[E, A]) func(fab Either[E, func(a A) B]) Either[E, B] { + return F.Bind2nd(MonadAp[E, A, B], fa) +} + +func MonadMap[E, A, B any](fa Either[E, A], f func(a A) B) Either[E, B] { + return MonadChain(fa, F.Flow2(f, Right[E, B])) +} + +func MonadBiMap[E1, E2, A, B any](fa Either[E1, A], f func(E1) E2, g func(a A) B) Either[E2, B] { + return fold(fa, F.Flow2(f, Left[E2, B]), F.Flow2(g, Right[E2, B])) +} + +// BiMap maps a pair of functions over the two type arguments of the bifunctor. +func BiMap[E1, E2, A, B any](f func(E1) E2, g func(a A) B) func(Either[E1, A]) Either[E2, B] { + return Fold(F.Flow2(f, Left[E2, B]), F.Flow2(g, Right[E2, B])) +} + +func MonadMapTo[E, A, B any](fa Either[E, A], b B) Either[E, B] { + return MonadMap(fa, F.Constant1[A](b)) +} + +func MapTo[E, A, B any](b B) func(Either[E, A]) Either[E, B] { + return F.Bind2nd(MonadMapTo[E, A, B], b) +} + +func MonadMapLeft[E, A, B any](fa Either[E, A], f func(E) B) Either[B, A] { + return fold(fa, F.Flow2(f, Left[B, A]), Right[B, A]) +} + +func Map[E, A, B any](f func(a A) B) func(fa Either[E, A]) Either[E, B] { + return Chain(F.Flow2(f, Right[E, B])) +} + +func MapLeft[E, A, B any](f func(E) B) func(fa Either[E, A]) Either[B, A] { + return F.Bind2nd(MonadMapLeft[E, A, B], f) +} + +func MonadChain[E, A, B any](fa Either[E, A], f func(a A) Either[E, B]) Either[E, B] { + return fold(fa, Left[E, B], f) +} + +func MonadChainFirst[E, A, B any](ma Either[E, A], f func(a A) Either[E, B]) Either[E, A] { + return MonadChain(ma, func(a A) Either[E, A] { + return MonadMap(f(a), F.Constant1[B](a)) + }) +} + +func MonadChainTo[E, A, B any](ma Either[E, A], mb Either[E, B]) Either[E, B] { + return mb +} + +func MonadChainOptionK[E, A, B any](onNone func() E, ma Either[E, A], f func(A) O.Option[B]) Either[E, B] { + return MonadChain(ma, F.Flow2(f, FromOption[E, B](onNone))) +} + +func ChainOptionK[E, A, B any](onNone func() E) func(func(A) O.Option[B]) func(Either[E, A]) Either[E, B] { + from := FromOption[E, B](onNone) + return func(f func(A) O.Option[B]) func(Either[E, A]) Either[E, B] { + return Chain(F.Flow2(f, from)) + } +} + +func ChainTo[E, A, B any](mb Either[E, B]) func(Either[E, A]) Either[E, B] { + return F.Bind2nd(MonadChainTo[E, A, B], mb) +} + +func Chain[E, A, B any](f func(a A) Either[E, B]) func(Either[E, A]) Either[E, B] { + return F.Bind2nd(MonadChain[E, A, B], f) +} + +func ChainFirst[E, A, B any](f func(a A) Either[E, B]) func(Either[E, A]) Either[E, A] { + return F.Bind2nd(MonadChainFirst[E, A, B], f) +} + +func Flatten[E, A any](mma Either[E, Either[E, A]]) Either[E, A] { + return MonadChain(mma, F.Identity[Either[E, A]]) +} + +func TryCatch[E, A any](f func() (A, error), onThrow func(error) E) Either[E, A] { + val, err := f() + if err != nil { + return F.Pipe2(err, onThrow, Left[E, A]) + } + return F.Pipe1(val, Right[E, A]) +} + +func TryCatchErrorG[GA ~func() (A, error), A any](f GA) Either[error, A] { + return TryCatch(f, E.IdentityError) +} + +func TryCatchError[A any](f func() (A, error)) Either[error, A] { + return TryCatchErrorG(f) +} + +func Sequence2[E, T1, T2, R any](f func(T1, T2) Either[E, R]) func(Either[E, T1], Either[E, T2]) Either[E, R] { + return func(e1 Either[E, T1], e2 Either[E, T2]) Either[E, R] { + return MonadSequence2(e1, e2, f) + } +} + +func Sequence3[E, T1, T2, T3, R any](f func(T1, T2, T3) Either[E, R]) func(Either[E, T1], Either[E, T2], Either[E, T3]) Either[E, R] { + return func(e1 Either[E, T1], e2 Either[E, T2], e3 Either[E, T3]) Either[E, R] { + return MonadSequence3(e1, e2, e3, f) + } +} + +func FromOption[E, A any](onNone func() E) func(O.Option[A]) Either[E, A] { + return O.Fold(F.Nullary2(onNone, Left[E, A]), Right[E, A]) +} + +func ToOption[E, A any]() func(Either[E, A]) O.Option[A] { + return Fold(F.Ignore1[E](O.None[A]), O.Some[A]) +} + +func FromError[A any](f func(a A) error) func(A) Either[error, A] { + return func(a A) Either[error, A] { + return TryCatchError(func() (A, error) { + return a, f(a) + }) + } +} + +func ToError[A any](e Either[error, A]) error { + return fold(e, E.IdentityError, F.Constant1[A, error](nil)) +} + +func Eitherize0G[GA ~func() (R, error), GB ~func() Either[error, R], R any](f GA) GB { + return F.Bind1(TryCatchErrorG[GA, R], f) +} + +func Eitherize0[R any](f func() (R, error)) func() Either[error, R] { + return Eitherize0G[func() (R, error), func() Either[error, R]](f) +} + +func Uneitherize0G[GA ~func() Either[error, R], GB ~func() (R, error), R any](f GA) GB { + return func() (R, error) { + return UnwrapError(f()) + } +} + +func Uneitherize0[R any](f func() Either[error, R]) func() (R, error) { + return Uneitherize0G[func() Either[error, R], func() (R, error)](f) +} + +func Eitherize1G[GA ~func(T1) (R, error), GB ~func(T1) Either[error, R], T1, R any](f GA) GB { + return func(t1 T1) Either[error, R] { + return TryCatchError(func() (R, error) { + return f(t1) + }) + } +} + +func Eitherize1[T1, R any](f func(T1) (R, error)) func(T1) Either[error, R] { + return Eitherize1G[func(T1) (R, error), func(T1) Either[error, R]](f) +} + +func Uneitherize1G[GA ~func(T1) Either[error, R], GB ~func(T1) (R, error), T1, R any](f GA) GB { + return func(t1 T1) (R, error) { + return UnwrapError(f(t1)) + } +} + +func Uneitherize1[T1, R any](f func(T1) Either[error, R]) func(T1) (R, error) { + return Uneitherize1G[func(T1) Either[error, R], func(T1) (R, error)](f) +} + +func Eitherize2G[GA ~func(t1 T1, t2 T2) (R, error), GB ~func(t1 T1, t2 T2) Either[error, R], T1, T2, R any](f GA) GB { + return func(t1 T1, t2 T2) Either[error, R] { + return TryCatchError(func() (R, error) { + return f(t1, t2) + }) + } +} + +func Eitherize2[T1, T2, R any](f func(t1 T1, t2 T2) (R, error)) func(t1 T1, t2 T2) Either[error, R] { + return Eitherize2G[func(t1 T1, t2 T2) (R, error), func(t1 T1, t2 T2) Either[error, R]](f) +} + +func Uneitherize2G[GA ~func(T1, T2) Either[error, R], GB ~func(T1, T2) (R, error), T1, T2, R any](f GA) GB { + return func(t1 T1, t2 T2) (R, error) { + return UnwrapError(f(t1, t2)) + } +} + +func Uneitherize2[T1, T2, R any](f func(T1, T2) Either[error, R]) func(T1, T2) (R, error) { + return Uneitherize2G[func(T1, T2) Either[error, R], func(T1, T2) (R, error)](f) +} + +func Eitherize3G[GA ~func(t1 T1, t2 T2, t3 T3) (R, error), GB ~func(t1 T1, t2 T2, t3 T3) Either[error, R], T1, T2, T3, R any](f GA) GB { + return func(t1 T1, t2 T2, t3 T3) Either[error, R] { + return TryCatchError(func() (R, error) { + return f(t1, t2, t3) + }) + } +} + +func Eitherize3[T1, T2, T3, R any](f func(t1 T1, t2 T2, t3 T3) (R, error)) func(t1 T1, t2 T2, t3 T3) Either[error, R] { + return Eitherize3G[func(t1 T1, t2 T2, t3 T3) (R, error), func(t1 T1, t2 T2, t3 T3) Either[error, R]](f) +} + +func Uneitherize3G[GA ~func(T1, T2, T3) Either[error, R], GB ~func(T1, T2, T3) (R, error), T1, T2, T3, R any](f GA) GB { + return func(t1 T1, t2 T2, t3 T3) (R, error) { + return UnwrapError(f(t1, t2, t3)) + } +} + +func Uneitherize3[T1, T2, T3, R any](f func(T1, T2, T3) Either[error, R]) func(T1, T2, T3) (R, error) { + return Uneitherize3G[func(T1, T2, T3) Either[error, R], func(T1, T2, T3) (R, error)](f) +} + +func Eitherize4G[GA ~func(t1 T1, t2 T2, t3 T3, t4 T4) (R, error), GB ~func(t1 T1, t2 T2, t3 T3, t4 T4) Either[error, R], T1, T2, T3, T4, R any](f GA) GB { + return func(t1 T1, t2 T2, t3 T3, t4 T4) Either[error, R] { + return TryCatchError(func() (R, error) { + return f(t1, t2, t3, t4) + }) + } +} + +func Eitherize4[T1, T2, T3, T4, R any](f func(t1 T1, t2 T2, t3 T3, t4 T4) (R, error)) func(t1 T1, t2 T2, t3 T3, t4 T4) Either[error, R] { + return Eitherize4G[func(t1 T1, t2 T2, t3 T3, t4 T4) (R, error), func(t1 T1, t2 T2, t3 T3, t4 T4) Either[error, R]](f) +} + +func Uneitherize4G[GA ~func(T1, T2, T3, T4) Either[error, R], GB ~func(T1, T2, T3, T4) (R, error), T1, T2, T3, T4, R any](f GA) GB { + return func(t1 T1, t2 T2, t3 T3, t4 T4) (R, error) { + return UnwrapError(f(t1, t2, t3, t4)) + } +} + +func Uneitherize4[T1, T2, T3, T4, R any](f func(T1, T2, T3, T4) Either[error, R]) func(T1, T2, T3, T4) (R, error) { + return Uneitherize4G[func(T1, T2, T3, T4) Either[error, R], func(T1, T2, T3, T4) (R, error)](f) +} + +func MonadFold[E, A, B any](ma Either[E, A], onLeft func(e E) B, onRight func(a A) B) B { + return fold(ma, onLeft, onRight) +} + +func Fold[E, A, B any](onLeft func(E) B, onRight func(A) B) func(Either[E, A]) B { + return func(ma Either[E, A]) B { + return fold(ma, onLeft, onRight) + } +} + +func UnwrapError[A any](ma Either[error, A]) (A, error) { + return Unwrap[error](ma) +} + +func FromPredicate[E, A any](pred func(A) bool, onFalse func(A) E) func(A) Either[E, A] { + return func(a A) Either[E, A] { + if pred(a) { + return Right[E](a) + } + return Left[E, A](onFalse(a)) + } +} + +func FromNillable[E, A any](e E) func(*A) Either[E, *A] { + return FromPredicate(F.IsNonNil[A], F.Constant1[*A](e)) +} + +func GetOrElse[E, A any](onLeft func(E) A) func(Either[E, A]) A { + return Fold(onLeft, F.Identity[A]) +} + +func Reduce[E, A, B any](f func(B, A) B, initial B) func(Either[E, A]) B { + return Fold( + F.Constant1[E](initial), + F.Bind1st(f, initial), + ) +} + +func AltW[E, E1, A any](that func() Either[E1, A]) func(Either[E, A]) Either[E1, A] { + return Fold(F.Ignore1[E](that), Right[E1, A]) +} + +func Alt[E, A any](that func() Either[E, A]) func(Either[E, A]) Either[E, A] { + return AltW[E](that) +} + +func OrElse[E, A any](onLeft func(e E) Either[E, A]) func(Either[E, A]) Either[E, A] { + return Fold(onLeft, Of[E, A]) +} + +func ToType[E, A any](onError func(any) E) func(any) Either[E, A] { + return func(value any) Either[E, A] { + return F.Pipe2( + value, + O.ToType[A], + O.Fold(F.Nullary3(F.Constant(value), onError, Left[E, A]), Right[E, A]), + ) + } +} + +func Memoize[E, A any](val Either[E, A]) Either[E, A] { + return val +} + +func MonadSequence2[E, T1, T2, R any](e1 Either[E, T1], e2 Either[E, T2], f func(T1, T2) Either[E, R]) Either[E, R] { + return fold(e1, Left[E, R], func(t1 T1) Either[E, R] { + return fold(e2, Left[E, R], func(t2 T2) Either[E, R] { + return f(t1, t2) + }) + }) +} + +func MonadSequence3[E, T1, T2, T3, R any](e1 Either[E, T1], e2 Either[E, T2], e3 Either[E, T3], f func(T1, T2, T3) Either[E, R]) Either[E, R] { + return fold(e1, Left[E, R], func(t1 T1) Either[E, R] { + return fold(e2, Left[E, R], func(t2 T2) Either[E, R] { + return fold(e3, Left[E, R], func(t3 T3) Either[E, R] { + return f(t1, t2, t3) + }) + }) + }) +} + +// Swap changes the order of type parameters +func Swap[E, A any](val Either[E, A]) Either[A, E] { + return fold(val, Right[A, E], Left[A, E]) +} diff --git a/either/either_test.go b/either/either_test.go new file mode 100644 index 0000000..dc416ed --- /dev/null +++ b/either/either_test.go @@ -0,0 +1,96 @@ +package either + +import ( + "errors" + "testing" + + F "github.com/ibm/fp-go/function" + "github.com/ibm/fp-go/internal/utils" + O "github.com/ibm/fp-go/option" + S "github.com/ibm/fp-go/string" + "github.com/stretchr/testify/assert" +) + +func TestIsLeft(t *testing.T) { + err := errors.New("Some error") + withError := Left[error, string](err) + + assert.True(t, IsLeft(withError)) + assert.False(t, IsRight(withError)) +} + +func TestIsRight(t *testing.T) { + noError := Right[error]("Carsten") + + assert.True(t, IsRight(noError)) + assert.False(t, IsLeft(noError)) +} + +func TestMapEither(t *testing.T) { + + assert.Equal(t, F.Pipe1(Right[error]("abc"), Map[error](utils.StringLen)), Right[error](3)) + + val2 := F.Pipe1(Left[string, string]("s"), Map[string](utils.StringLen)) + exp2 := Left[string, int]("s") + + assert.Equal(t, val2, exp2) +} + +func TestUnwrapError(t *testing.T) { + a := "" + err := errors.New("Some error") + withError := Left[error, string](err) + + res, extracted := UnwrapError(withError) + assert.Equal(t, a, res) + assert.Equal(t, extracted, err) + +} + +func TestReduce(t *testing.T) { + + s := S.Semigroup() + + assert.Equal(t, "foobar", F.Pipe1(Right[string]("bar"), Reduce[string](s.Concat, "foo"))) + assert.Equal(t, "foo", F.Pipe1(Left[string, string]("bar"), Reduce[string](s.Concat, "foo"))) + +} + +func TestAp(t *testing.T) { + f := S.Size + + assert.Equal(t, Right[string](3), F.Pipe1(Right[string](f), Ap[string, string, int](Right[string]("abc")))) + assert.Equal(t, Left[string, int]("maError"), F.Pipe1(Right[string](f), Ap[string, string, int](Left[string, string]("maError")))) + assert.Equal(t, Left[string, int]("mabError"), F.Pipe1(Left[string, func(string) int]("mabError"), Ap[string, string, int](Left[string, string]("maError")))) +} + +func TestAlt(t *testing.T) { + assert.Equal(t, Right[string](1), F.Pipe1(Right[string](1), Alt(F.Constant(Right[string](2))))) + assert.Equal(t, Right[string](1), F.Pipe1(Right[string](1), Alt(F.Constant(Left[string, int]("a"))))) + assert.Equal(t, Right[string](2), F.Pipe1(Left[string, int]("b"), Alt(F.Constant(Right[string](2))))) + assert.Equal(t, Left[string, int]("b"), F.Pipe1(Left[string, int]("a"), Alt(F.Constant(Left[string, int]("b"))))) +} + +func TestChainFirst(t *testing.T) { + f := F.Flow2(S.Size, Right[string, int]) + + assert.Equal(t, Right[string]("abc"), F.Pipe1(Right[string]("abc"), ChainFirst(f))) + assert.Equal(t, Left[string, string]("maError"), F.Pipe1(Left[string, string]("maError"), ChainFirst(f))) +} + +func TestChainOptionK(t *testing.T) { + f := ChainOptionK[string, int, int](F.Constant("a"))(func(n int) O.Option[int] { + if n > 0 { + return O.Some(n) + } + return O.None[int]() + }) + assert.Equal(t, Right[string](1), f(Right[string](1))) + assert.Equal(t, Left[string, int]("a"), f(Right[string](-1))) + assert.Equal(t, Left[string, int]("b"), f(Left[string, int]("b"))) +} + +func TestFromOption(t *testing.T) { + assert.Equal(t, Left[string, int]("none"), FromOption[string, int](F.Constant("none"))(O.None[int]())) + assert.Equal(t, Right[string](1), FromOption[string, int](F.Constant("none"))(O.Some(1))) +} diff --git a/either/eq.go b/either/eq.go new file mode 100644 index 0000000..9eda518 --- /dev/null +++ b/either/eq.go @@ -0,0 +1,25 @@ +package either + +import ( + EQ "github.com/ibm/fp-go/eq" + F "github.com/ibm/fp-go/function" +) + +// Constructs an equal predicate for an `Either` +func Eq[E, A any](e EQ.Eq[E], a EQ.Eq[A]) EQ.Eq[Either[E, A]] { + // some convenient shortcuts + eqa := F.Curry2(a.Equals) + eqe := F.Curry2(e.Equals) + + fca := F.Bind2nd(Fold[E, A, bool], F.Constant1[A](false)) + fce := F.Bind1st(Fold[E, A, bool], F.Constant1[E](false)) + + fld := Fold(F.Flow2(eqe, fca), F.Flow2(eqa, fce)) + + return EQ.FromEquals(F.Uncurry2(fld)) +} + +// FromStrictEquals constructs an `Eq` from the canonical comparison function +func FromStrictEquals[E, A comparable]() EQ.Eq[Either[E, A]] { + return Eq(EQ.FromStrictEquals[E](), EQ.FromStrictEquals[A]()) +} diff --git a/either/eq_test.go b/either/eq_test.go new file mode 100644 index 0000000..314a92f --- /dev/null +++ b/either/eq_test.go @@ -0,0 +1,30 @@ +package either + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestEq(t *testing.T) { + + r1 := Of[string](1) + r2 := Of[string](1) + r3 := Of[string](2) + + e1 := Left[string, int]("a") + e2 := Left[string, int]("a") + e3 := Left[string, 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)) +} diff --git a/either/exec/exec.go b/either/exec/exec.go new file mode 100644 index 0000000..a52e595 --- /dev/null +++ b/either/exec/exec.go @@ -0,0 +1,23 @@ +package Exec + +import ( + "context" + + E "github.com/ibm/fp-go/either" + "github.com/ibm/fp-go/exec" + F "github.com/ibm/fp-go/function" + GE "github.com/ibm/fp-go/internal/exec" +) + +var ( + // Command executes a command + // use this version if the command does not produce any side effect, i.e. if the output is uniquely determined by by the input + // typically you'd rather use the IOEither version of the command + Command = F.Curry3(command) +) + +func command(name string, args []string, in []byte) E.Either[error, exec.CommandOutput] { + return E.TryCatchError(func() (exec.CommandOutput, error) { + return GE.Exec(context.Background(), name, args, in) + }) +} diff --git a/either/http/request.go b/either/http/request.go new file mode 100644 index 0000000..c9556de --- /dev/null +++ b/either/http/request.go @@ -0,0 +1,36 @@ +package Http + +import ( + "bytes" + "net/http" + + E "github.com/ibm/fp-go/either" +) + +var ( + PostRequest = bodyRequest("POST") + PutRequest = bodyRequest("PUT") + + GetRequest = noBodyRequest("GET") + DeleteRequest = noBodyRequest("DELETE") + OptionsRequest = noBodyRequest("OPTIONS") + HeadRequest = noBodyRequest("HEAD") +) + +func bodyRequest(method string) func(string) func([]byte) E.Either[error, *http.Request] { + return func(url string) func([]byte) E.Either[error, *http.Request] { + return func(body []byte) E.Either[error, *http.Request] { + return E.TryCatchError(func() (*http.Request, error) { + return http.NewRequest(method, url, bytes.NewReader(body)) + }) + } + } +} + +func noBodyRequest(method string) func(string) E.Either[error, *http.Request] { + return func(url string) E.Either[error, *http.Request] { + return E.TryCatchError(func() (*http.Request, error) { + return http.NewRequest(method, url, nil) + }) + } +} diff --git a/either/legacy.go b/either/legacy.go new file mode 100644 index 0000000..fd12f08 --- /dev/null +++ b/either/legacy.go @@ -0,0 +1,55 @@ +package either + +import "fmt" + +type EitherTag int + +const ( + LeftTag EitherTag = iota + RightTag +) + +// Either defines a data structure that logically holds either an E or an A. The tag discriminates the cases +type Either[E, A any] struct { + Tag EitherTag + Left E + Right A +} + +// String prints some debug info for the object +func (s Either[E, A]) String() string { + switch s.Tag { + case LeftTag: + return fmt.Sprintf("Left[%T, %T](%v)", s.Left, s.Right, s.Left) + case RightTag: + return fmt.Sprintf("Right[%T, %T](%v)", s.Left, s.Right, s.Right) + } + return "Invalid" +} + +func IsLeft[E, A any](val Either[E, A]) bool { + return val.Tag == LeftTag +} + +func IsRight[E, A any](val Either[E, A]) bool { + return val.Tag == RightTag +} + +func Left[E, A any](value E) Either[E, A] { + return Either[E, A]{Tag: LeftTag, Left: value} +} + +func Right[E, A any](value A) Either[E, A] { + return Either[E, A]{Tag: RightTag, Right: value} +} + +func fold[E, A, B any](ma Either[E, A], onLeft func(e E) B, onRight func(a A) B) B { + if IsLeft(ma) { + return onLeft(ma.Left) + } + return onRight(ma.Right) +} + +func Unwrap[E, A any](ma Either[E, A]) (A, E) { + return ma.Right, ma.Left +} diff --git a/either/logger.go b/either/logger.go new file mode 100644 index 0000000..8dc6824 --- /dev/null +++ b/either/logger.go @@ -0,0 +1,33 @@ +package either + +import ( + "log" + + F "github.com/ibm/fp-go/function" + L "github.com/ibm/fp-go/logging" +) + +func _log[E, A any](left func(string, ...any), right func(string, ...any), prefix string) func(Either[E, A]) Either[E, A] { + return Fold( + func(e E) Either[E, A] { + left("%s: %v", prefix, e) + return Left[E, A](e) + }, + func(a A) Either[E, A] { + right("%s: %v", prefix, a) + return Right[E](a) + }) +} + +func Logger[E, A any](loggers ...*log.Logger) func(string) func(Either[E, A]) Either[E, A] { + left, right := L.LoggingCallbacks(loggers...) + return func(prefix string) func(Either[E, A]) Either[E, A] { + delegate := _log[E, A](left, right, prefix) + return func(ma Either[E, A]) Either[E, A] { + return F.Pipe1( + delegate(ma), + ChainTo[E, A](ma), + ) + } + } +} diff --git a/either/logger_test.go b/either/logger_test.go new file mode 100644 index 0000000..1097d94 --- /dev/null +++ b/either/logger_test.go @@ -0,0 +1,22 @@ +package either + +import ( + "testing" + + F "github.com/ibm/fp-go/function" + "github.com/stretchr/testify/assert" +) + +func TestLogger(t *testing.T) { + + l := Logger[error, string]() + + r := Right[error]("test") + + res := F.Pipe1( + r, + l("out"), + ) + + assert.Equal(t, r, res) +} diff --git a/either/modern._go b/either/modern._go new file mode 100644 index 0000000..7cb0fff --- /dev/null +++ b/either/modern._go @@ -0,0 +1,69 @@ +//go:build disabled + +package either + +// Either will either be E or A +type Either[E, A any] interface { + fmt.Stringer +} + +type left[E any] struct { + e E +} + +func (left[E]) IsLeft() bool { + return true +} + +type right[A any] struct { + a A +} + +func (right[A]) IsLeft() bool { + return false +} + +func (l left[E]) String() string { + return fmt.Sprintf("Left[%v]", l.e) +} + +func (r right[A]) String() string { + return fmt.Sprintf("Right[%v]", r.a) +} + +func IsLeft[E, A any](val Either[E, A]) bool { + switch any(val).(type) { + case left[E]: + return true + default: + return false + } +} + +func IsRight[E, A any](val Either[E, A]) bool { + return !IsLeft(val) +} + +func Left[E, A any](value E) Either[E, A] { + return left[E]{e: value} +} + +func Right[E, A any](value A) Either[E, A] { + return right[A]{a: value} +} + +func fold[E, A, B any](ma Either[E, A], onLeft func(e E) B, onRight func(a A) B) B { + if IsLeft(ma) { + return onLeft(ma.(left[E]).e) + } + return onRight(ma.(right[A]).a) +} + +func Unwrap[E, A any](ma Either[E, A]) (A, E) { + if IsLeft(ma) { + var a A + return a, ma.(left[E]).e + } + var E e + return ma.(right[A]).a, e +} diff --git a/either/record.go b/either/record.go new file mode 100644 index 0000000..7c458d2 --- /dev/null +++ b/either/record.go @@ -0,0 +1,30 @@ +package either + +import ( + F "github.com/ibm/fp-go/function" + RR "github.com/ibm/fp-go/internal/record" +) + +// TraverseRecord transforms a record of options into an option of a record +func TraverseRecordG[GA ~map[K]A, GB ~map[K]B, K comparable, E, A, B any](f func(A) Either[E, B]) func(GA) Either[E, GB] { + return RR.Traverse[GA]( + Of[E, GB], + MonadMap[E, GB, func(B) GB], + MonadAp[E, B, GB], + f, + ) +} + +// TraverseRecord transforms a record of eithers into an either of a record +func TraverseRecord[K comparable, E, A, B any](f func(A) Either[E, B]) func(map[K]A) Either[E, map[K]B] { + return TraverseRecordG[map[K]A, map[K]B](f) +} + +func SequenceRecordG[GA ~map[K]A, GOA ~map[K]Either[E, A], K comparable, E, A any](ma GOA) Either[E, GA] { + return TraverseRecordG[GOA, GA](F.Identity[Either[E, A]])(ma) +} + +// SequenceRecord converts a homogeneous sequence of either into an either of sequence +func SequenceRecord[K comparable, E, A any](ma map[K]Either[E, A]) Either[E, map[K]A] { + return SequenceRecordG[map[K]A](ma) +} diff --git a/either/resource.go b/either/resource.go new file mode 100644 index 0000000..6958d19 --- /dev/null +++ b/either/resource.go @@ -0,0 +1,29 @@ +package either + +import ( + F "github.com/ibm/fp-go/function" +) + +// constructs a function that creates a resource, then operates on it and then releases the resource +func WithResource[E, R, A any](onCreate func() Either[E, R], onRelease func(R) Either[E, any]) func(func(R) Either[E, A]) Either[E, A] { + + return func(f func(R) Either[E, A]) Either[E, A] { + return MonadChain( + onCreate(), func(r R) Either[E, A] { + // run the code and make sure to release as quickly as possible + res := f(r) + released := onRelease(r) + // handle the errors + return fold( + res, + Left[E, A], + func(a A) Either[E, A] { + return F.Pipe1( + released, + MapTo[E, any](a), + ) + }) + }, + ) + } +} diff --git a/either/resource_test.go b/either/resource_test.go new file mode 100644 index 0000000..a68e92b --- /dev/null +++ b/either/resource_test.go @@ -0,0 +1,39 @@ +package either + +import ( + "os" + "testing" + + F "github.com/ibm/fp-go/function" + "github.com/stretchr/testify/assert" +) + +func TestWithResource(t *testing.T) { + onCreate := func() Either[error, *os.File] { + return TryCatchError(func() (*os.File, error) { + return os.CreateTemp("", "*") + }) + } + onDelete := F.Flow2( + func(f *os.File) Either[error, string] { + return TryCatchError(func() (string, error) { + return f.Name(), f.Close() + }) + }, + Chain(func(name string) Either[error, any] { + return TryCatchError(func() (any, error) { + return name, os.Remove(name) + }) + }), + ) + + onHandler := func(f *os.File) Either[error, string] { + return Of[error](f.Name()) + } + + tempFile := WithResource[error, *os.File, string](onCreate, onDelete) + + resE := tempFile(onHandler) + + assert.True(t, IsRight(resE)) +} diff --git a/either/sequence.go b/either/sequence.go new file mode 100644 index 0000000..a8c8224 --- /dev/null +++ b/either/sequence.go @@ -0,0 +1,45 @@ +package either + +import ( + Apply "github.com/ibm/fp-go/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[E, A any](a Either[E, A]) Either[E, T.Tuple1[A]] { + return Apply.SequenceT1( + MonadMap[E, A, T.Tuple1[A]], + a, + ) +} + +func SequenceT2[E, A, B any](a Either[E, A], b Either[E, B]) Either[E, T.Tuple2[A, B]] { + return Apply.SequenceT2( + MonadMap[E, A, func(B) T.Tuple2[A, B]], + MonadAp[E, B, T.Tuple2[A, B]], + + a, b, + ) +} + +func SequenceT3[E, A, B, C any](a Either[E, A], b Either[E, B], c Either[E, C]) Either[E, T.Tuple3[A, B, C]] { + return Apply.SequenceT3( + MonadMap[E, A, func(B) func(C) T.Tuple3[A, B, C]], + MonadAp[E, B, func(C) T.Tuple3[A, B, C]], + MonadAp[E, C, T.Tuple3[A, B, C]], + + a, b, c, + ) +} + +func SequenceT4[E, A, B, C, D any](a Either[E, A], b Either[E, B], c Either[E, C], d Either[E, D]) Either[E, T.Tuple4[A, B, C, D]] { + return Apply.SequenceT4( + MonadMap[E, A, func(B) func(C) func(D) T.Tuple4[A, B, C, D]], + MonadAp[E, B, func(C) func(D) T.Tuple4[A, B, C, D]], + MonadAp[E, C, func(D) T.Tuple4[A, B, C, D]], + MonadAp[E, D, T.Tuple4[A, B, C, D]], + + a, b, c, d, + ) +} diff --git a/either/testing/laws.go b/either/testing/laws.go new file mode 100644 index 0000000..0782cd0 --- /dev/null +++ b/either/testing/laws.go @@ -0,0 +1,60 @@ +package testing + +import ( + "testing" + + ET "github.com/ibm/fp-go/either" + EQ "github.com/ibm/fp-go/eq" + L "github.com/ibm/fp-go/internal/monad/testing" +) + +// AssertLaws asserts the apply monad laws for the `Either` monad +func AssertLaws[E, A, B, C any](t *testing.T, + eqe EQ.Eq[E], + eqa EQ.Eq[A], + eqb EQ.Eq[B], + eqc EQ.Eq[C], + + ab func(A) B, + bc func(B) C, +) func(a A) bool { + + return L.AssertLaws(t, + ET.Eq(eqe, eqa), + ET.Eq(eqe, eqb), + ET.Eq(eqe, eqc), + + ET.Of[E, A], + ET.Of[E, B], + ET.Of[E, C], + + ET.Of[E, func(A) A], + ET.Of[E, func(A) B], + ET.Of[E, func(B) C], + ET.Of[E, func(func(A) B) B], + + ET.MonadMap[E, A, A], + ET.MonadMap[E, A, B], + ET.MonadMap[E, A, C], + ET.MonadMap[E, B, C], + + ET.MonadMap[E, func(B) C, func(func(A) B) func(A) C], + + ET.MonadChain[E, A, A], + ET.MonadChain[E, A, B], + ET.MonadChain[E, A, C], + ET.MonadChain[E, B, C], + + ET.MonadAp[E, A, A], + ET.MonadAp[E, A, B], + ET.MonadAp[E, B, C], + ET.MonadAp[E, A, C], + + ET.MonadAp[E, func(A) B, B], + ET.MonadAp[E, func(A) B, func(A) C], + + ab, + bc, + ) + +} diff --git a/either/testing/laws_test.go b/either/testing/laws_test.go new file mode 100644 index 0000000..d3a35c8 --- /dev/null +++ b/either/testing/laws_test.go @@ -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)) +} diff --git a/either/traverse.go b/either/traverse.go new file mode 100644 index 0000000..fb242d7 --- /dev/null +++ b/either/traverse.go @@ -0,0 +1,54 @@ +package either + +import ( + F "github.com/ibm/fp-go/function" +) + +/* +* +We need to pass the members of the applicative explicitly, because golang does neither support higher kinded types nor template methods on structs or interfaces + +HKTRB = HKT +HKTA = HKT +HKTB = HKT +*/ +func traverse[E, A, B, HKTA, HKTB, HKTRB any]( + _of func(Either[E, B]) HKTRB, + _map func(HKTB, func(B) Either[E, B]) HKTRB, +) func(Either[E, A], func(A) HKTB) HKTRB { + + left := F.Flow2(Left[E, B], _of) + right := F.Bind2nd(_map, Right[E, B]) + + return func(ta Either[E, A], f func(A) HKTB) HKTRB { + return fold(ta, + left, + F.Flow2(f, right), + ) + } +} + +func Traverse[E, A, B, HKTA, HKTB, HKTRB any]( + _of func(Either[E, B]) HKTRB, + _map func(HKTB, func(B) Either[E, B]) HKTRB, +) func(func(A) HKTB) func(Either[E, A]) HKTRB { + delegate := traverse[E, A, B, HKTA](_of, _map) + return func(f func(A) HKTB) func(Either[E, A]) HKTRB { + return F.Bind2nd(delegate, f) + } +} + +/* +* +We need to pass the members of the applicative explicitly, because golang does neither support higher kinded types nor template methods on structs or interfaces + +HKTRA = HKT +HKTA = HKT +HKTB = HKT +*/ +func Sequence[E, A, HKTA, HKTRA any]( + _of func(Either[E, A]) HKTRA, + _map func(HKTA, func(A) Either[E, A]) HKTRA, +) func(Either[E, HKTA]) HKTRA { + return Fold(F.Flow2(Left[E, A], _of), F.Bind2nd(_map, Right[E, A])) +} diff --git a/either/traverse_test.go b/either/traverse_test.go new file mode 100644 index 0000000..51e2f6d --- /dev/null +++ b/either/traverse_test.go @@ -0,0 +1,38 @@ +package either + +import ( + "testing" + + F "github.com/ibm/fp-go/function" + O "github.com/ibm/fp-go/option" + "github.com/stretchr/testify/assert" +) + +func TestTraverse(t *testing.T) { + f := func(n int) O.Option[int] { + if n >= 2 { + return O.Of(n) + } + return O.None[int]() + } + trav := Traverse[string, int, int, O.Option[Either[string, int]]]( + O.Of[Either[string, int]], + O.MonadMap[int, Either[string, int]], + )(f) + + assert.Equal(t, O.Of(Left[string, int]("a")), F.Pipe1(Left[string, int]("a"), trav)) + assert.Equal(t, O.None[Either[string, int]](), F.Pipe1(Right[string](1), trav)) + assert.Equal(t, O.Of(Right[string](3)), F.Pipe1(Right[string](3), trav)) +} + +func TestSequence(t *testing.T) { + + seq := Sequence( + O.Of[Either[string, int]], + O.MonadMap[int, Either[string, int]], + ) + + assert.Equal(t, O.Of(Right[string](1)), seq(Right[string](O.Of(1)))) + assert.Equal(t, O.Of(Left[string, int]("a")), seq(Left[string, O.Option[int]]("a"))) + assert.Equal(t, O.None[Either[string, int]](), seq(Right[string](O.None[int]()))) +} diff --git a/either/variadic.go b/either/variadic.go new file mode 100644 index 0000000..c2975d8 --- /dev/null +++ b/either/variadic.go @@ -0,0 +1,81 @@ +package either + +func Variadic0[V, R any](f func([]V) (R, error)) func(...V) Either[error, R] { + return func(v ...V) Either[error, R] { + return TryCatchError(func() (R, error) { + return f(v) + }) + } +} + +func Variadic1[T1, V, R any](f func(T1, []V) (R, error)) func(T1, ...V) Either[error, R] { + return func(t1 T1, v ...V) Either[error, R] { + return TryCatchError(func() (R, error) { + return f(t1, v) + }) + } +} + +func Variadic2[T1, T2, V, R any](f func(T1, T2, []V) (R, error)) func(T1, T2, ...V) Either[error, R] { + return func(t1 T1, t2 T2, v ...V) Either[error, R] { + return TryCatchError(func() (R, error) { + return f(t1, t2, v) + }) + } +} + +func Variadic3[T1, T2, T3, V, R any](f func(T1, T2, T3, []V) (R, error)) func(T1, T2, T3, ...V) Either[error, R] { + return func(t1 T1, t2 T2, t3 T3, v ...V) Either[error, R] { + return TryCatchError(func() (R, error) { + return f(t1, t2, t3, v) + }) + } +} + +func Variadic4[T1, T2, T3, T4, V, R any](f func(T1, T2, T3, T4, []V) (R, error)) func(T1, T2, T3, T4, ...V) Either[error, R] { + return func(t1 T1, t2 T2, t3 T3, t4 T4, v ...V) Either[error, R] { + return TryCatchError(func() (R, error) { + return f(t1, t2, t3, t4, v) + }) + } +} + +func Unvariadic0[V, R any](f func(...V) (R, error)) func([]V) Either[error, R] { + return func(v []V) Either[error, R] { + return TryCatchError(func() (R, error) { + return f(v...) + }) + } +} + +func Unvariadic1[T1, V, R any](f func(T1, ...V) (R, error)) func(T1, []V) Either[error, R] { + return func(t1 T1, v []V) Either[error, R] { + return TryCatchError(func() (R, error) { + return f(t1, v...) + }) + } +} + +func Unvariadic2[T1, T2, V, R any](f func(T1, T2, ...V) (R, error)) func(T1, T2, []V) Either[error, R] { + return func(t1 T1, t2 T2, v []V) Either[error, R] { + return TryCatchError(func() (R, error) { + return f(t1, t2, v...) + }) + } +} + +func Unvariadic3[T1, T2, T3, V, R any](f func(T1, T2, T3, ...V) (R, error)) func(T1, T2, T3, []V) Either[error, R] { + return func(t1 T1, t2 T2, t3 T3, v []V) Either[error, R] { + return TryCatchError(func() (R, error) { + return f(t1, t2, t3, v...) + }) + } +} + +func Unvariadic4[T1, T2, T3, T4, V, R any](f func(T1, T2, T3, T4, ...V) (R, error)) func(T1, T2, T3, T4, []V) Either[error, R] { + return func(t1 T1, t2 T2, t3 T3, t4 T4, v []V) Either[error, R] { + return TryCatchError(func() (R, error) { + return f(t1, t2, t3, t4, v...) + }) + } +} diff --git a/eq/contramap.go b/eq/contramap.go new file mode 100644 index 0000000..5295d94 --- /dev/null +++ b/eq/contramap.go @@ -0,0 +1,11 @@ +package eq + +// Contramap implements an Equals predicate based on a mapping +func Contramap[A, B any](f func(b B) A) func(Eq[A]) Eq[B] { + return func(fa Eq[A]) Eq[B] { + equals := fa.Equals + return FromEquals(func(x, y B) bool { + return equals(f(x), f(y)) + }) + } +} diff --git a/eq/eq.go b/eq/eq.go new file mode 100644 index 0000000..019479a --- /dev/null +++ b/eq/eq.go @@ -0,0 +1,43 @@ +package eq + +import ( + F "github.com/ibm/fp-go/function" +) + +type Eq[T any] interface { + Equals(x, y T) bool +} + +type eq[T any] struct { + c func(x, y T) bool +} + +func (self eq[T]) Equals(x, y T) bool { + return self.c(x, y) +} + +func strictEq[A comparable](a, b A) bool { + return a == b +} + +// FromStrictEquals constructs an `Eq` from the canonical comparison function +func FromStrictEquals[T comparable]() Eq[T] { + return FromEquals(strictEq[T]) +} + +// FromEquals constructs an `Eq` from the comparison function +func FromEquals[T any](c func(x, y T) bool) Eq[T] { + return eq[T]{c: c} +} + +// Empty returns the equals predicate that is always true +func Empty[T any]() Eq[T] { + return FromEquals(F.Constant2[T, T](true)) +} + +// Equals returns a predicate to test if one value equals the other under an equals predicate +func Equals[T any](eq Eq[T]) func(T) func(T) bool { + return func(other T) func(T) bool { + return F.Bind2nd(eq.Equals, other) + } +} diff --git a/eq/monoid.go b/eq/monoid.go new file mode 100644 index 0000000..9e221c9 --- /dev/null +++ b/eq/monoid.go @@ -0,0 +1,18 @@ +package eq + +import ( + M "github.com/ibm/fp-go/monoid" + S "github.com/ibm/fp-go/semigroup" +) + +func Semigroup[A any]() S.Semigroup[Eq[A]] { + return S.MakeSemigroup(func(x, y Eq[A]) Eq[A] { + return FromEquals(func(a, b A) bool { + return x.Equals(a, b) && y.Equals(a, b) + }) + }) +} + +func Monoid[A any]() M.Monoid[Eq[A]] { + return M.MakeMonoid(Semigroup[A]().Concat, Empty[A]()) +} diff --git a/eq/testing/eq.go b/eq/testing/eq.go new file mode 100644 index 0000000..4f7d81d --- /dev/null +++ b/eq/testing/eq.go @@ -0,0 +1,13 @@ +package testing + +import ( + EQ "github.com/ibm/fp-go/eq" + "github.com/stretchr/testify/assert" +) + +// Eq implements the equal operation based on `ObjectsAreEqualValues` from the assertion library +func Eq[A any]() EQ.Eq[A] { + return EQ.FromEquals(func(l, r A) bool { + return assert.ObjectsAreEqualValues(l, r) + }) +} diff --git a/errors/conv.go b/errors/conv.go new file mode 100644 index 0000000..0e88c32 --- /dev/null +++ b/errors/conv.go @@ -0,0 +1,16 @@ +package errors + +import ( + "errors" + + O "github.com/ibm/fp-go/option" +) + +// As tries to extract the error of desired type from the given error +func As[A error]() func(error) O.Option[A] { + return O.FromValidation(func(err error) (A, bool) { + var a A + ok := errors.As(err, &a) + return a, ok + }) +} diff --git a/errors/conv_test.go b/errors/conv_test.go new file mode 100644 index 0000000..4fbfdd0 --- /dev/null +++ b/errors/conv_test.go @@ -0,0 +1,39 @@ +package errors + +import ( + "fmt" + "testing" + + F "github.com/ibm/fp-go/function" + O "github.com/ibm/fp-go/option" + "github.com/stretchr/testify/assert" +) + +type MyError struct{} + +func (m *MyError) Error() string { + return "boom" +} + +func TestAs(t *testing.T) { + root := &MyError{} + err := fmt.Errorf("This is my custom error, %w", root) + + errO := F.Pipe1( + err, + As[*MyError](), + ) + + assert.Equal(t, O.Of(root), errO) +} + +func TestNotAs(t *testing.T) { + err := fmt.Errorf("This is my custom error") + + errO := F.Pipe1( + err, + As[*MyError](), + ) + + assert.Equal(t, O.None[*MyError](), errO) +} diff --git a/errors/identity.go b/errors/identity.go new file mode 100644 index 0000000..2e11e69 --- /dev/null +++ b/errors/identity.go @@ -0,0 +1,7 @@ +package errors + +import ( + F "github.com/ibm/fp-go/function" +) + +var IdentityError = F.Identity[error] diff --git a/errors/message_test.go b/errors/message_test.go new file mode 100644 index 0000000..44cf3ac --- /dev/null +++ b/errors/message_test.go @@ -0,0 +1,18 @@ +package errors + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestOnError(t *testing.T) { + onError := OnError("Failed to process [%s]", "filename") + + err := fmt.Errorf("Cause") + + err1 := onError(err) + + assert.Equal(t, "Failed to process [filename], Caused By: Cause", err1.Error()) +} diff --git a/errors/messages.go b/errors/messages.go new file mode 100644 index 0000000..553d0b0 --- /dev/null +++ b/errors/messages.go @@ -0,0 +1,23 @@ +package errors + +import ( + "fmt" + + A "github.com/ibm/fp-go/array" +) + +// OnNone generates a nullary function that produces a formatted error +func OnNone(msg string, args ...any) func() error { + return func() error { + return fmt.Errorf(msg, args...) + } +} + +// OnError generates a unary function that produces a formatted error. The argument +// to that function is the root cause of the error and the message will be augmented with +// a format string containing %w +func OnError(msg string, args ...any) func(error) error { + return func(err error) error { + return fmt.Errorf(msg+", Caused By: %w", A.ArrayConcatAll(args, A.Of[any](err))...) + } +} diff --git a/exec/exec.go b/exec/exec.go new file mode 100644 index 0000000..b196292 --- /dev/null +++ b/exec/exec.go @@ -0,0 +1,15 @@ +package exec + +import ( + T "github.com/ibm/fp-go/tuple" +) + +type ( + // command output + CommandOutput = T.Tuple2[[]byte, []byte] +) + +var ( + StdOut = T.First[[]byte, []byte] + StdErr = T.Second[[]byte, []byte] +) diff --git a/fp-go.exe b/fp-go.exe new file mode 100644 index 0000000000000000000000000000000000000000..b69c035eebb7528de07058fcea0c9b6f27a58e40 GIT binary patch literal 2005504 zcmeEvdwf*Y)%GN0V1UFkNPr+91CAQJK*G&Lh)%ekfe8kS7=hM!Ax1@sFauFg5+{KS z!_iUETCtW|wQ6mJzBY2Pm~cq~DspKNNLx{>?s2>X^@RY{e9yD@naK=^w(s};zW+Wn zbN1PnwbxpE?X}ll`*I5Age+YwDHi@~YqMB_GGwv1%;%3iDv)^2;4SA^UhDqOkWERR zcZSR=y?wExY|+Bo7Tt80&i6sW*=mEpu5G=Vi}s;Gr5b!^0m!kc_2xq)t$6jEOrK6@?D0LEtam9B>bnI zGGRR+cU4)sSc)qx7KeI~Wf?RBvG-Vv%2zkk5>o6??`Qw+8)~V@Iy>^;zgJ}Uw&;qK zPBRbp&M)^O<;~-unYfX5la6Qshs9Dne9^5pd2g~<#JN3?56nu(vlV&c{<*-U;VPS@ zM$$ml}8Pw-sR zNxo4dWxf$}@yHrzvCPABQC}Gs{gLoSkCm^Il_BI~y?Z;!H+GE7ci?fz!+PX<0?&P& z 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ɝGcG9f>4y?ABQcn`-Xk`Ru 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Ǝk02Fj|x1Ia$^&f}>wv$sHE8 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