mirror of
https://github.com/IBM/fp-go.git
synced 2025-06-23 00:27:49 +02:00
Merge pull request #11 from IBM/cleue-better-doc
fix: introduce stateless iterator
This commit is contained in:
@ -144,7 +144,7 @@ func Append[A any](as []A, a A) []A {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func IsEmpty[A any](as []A) bool {
|
func IsEmpty[A any](as []A) bool {
|
||||||
return array.IsEmpty(as)
|
return G.IsEmpty(as)
|
||||||
}
|
}
|
||||||
|
|
||||||
func IsNonEmpty[A any](as []A) bool {
|
func IsNonEmpty[A any](as []A) bool {
|
||||||
@ -181,12 +181,11 @@ func Ap[B, A any](fa []A) func([]func(A) B) []B {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Match[A, B any](onEmpty func() B, onNonEmpty func([]A) B) func([]A) B {
|
func Match[A, B any](onEmpty func() B, onNonEmpty func([]A) B) func([]A) B {
|
||||||
return func(as []A) B {
|
return G.Match[[]A](onEmpty, onNonEmpty)
|
||||||
if IsEmpty(as) {
|
}
|
||||||
return onEmpty()
|
|
||||||
}
|
func MatchLeft[A, B any](onEmpty func() B, onNonEmpty func(A, []A) B) func([]A) B {
|
||||||
return onNonEmpty(as)
|
return G.MatchLeft[[]A](onEmpty, onNonEmpty)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Tail[A any](as []A) O.Option[[]A] {
|
func Tail[A any](as []A) O.Option[[]A] {
|
||||||
|
@ -158,3 +158,25 @@ func MonadAp[BS ~[]B, ABS ~[]func(A) B, AS ~[]A, B, A any](fab ABS, fa AS) BS {
|
|||||||
func Ap[BS ~[]B, ABS ~[]func(A) B, AS ~[]A, B, A any](fa AS) func(ABS) BS {
|
func Ap[BS ~[]B, ABS ~[]func(A) B, AS ~[]A, B, A any](fa AS) func(ABS) BS {
|
||||||
return F.Bind2nd(MonadAp[BS, ABS, AS], fa)
|
return F.Bind2nd(MonadAp[BS, ABS, AS], fa)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func IsEmpty[AS ~[]A, A any](as AS) bool {
|
||||||
|
return array.IsEmpty(as)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Match[AS ~[]A, A, B any](onEmpty func() B, onNonEmpty func(AS) B) func(AS) B {
|
||||||
|
return func(as AS) B {
|
||||||
|
if IsEmpty(as) {
|
||||||
|
return onEmpty()
|
||||||
|
}
|
||||||
|
return onNonEmpty(as)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func MatchLeft[AS ~[]A, A, B any](onEmpty func() B, onNonEmpty func(A, AS) B) func(AS) B {
|
||||||
|
return func(as AS) B {
|
||||||
|
if IsEmpty(as) {
|
||||||
|
return onEmpty()
|
||||||
|
}
|
||||||
|
return onNonEmpty(as[0], as[1:])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
64
bounded/bounded.go
Normal file
64
bounded/bounded.go
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package bounded
|
||||||
|
|
||||||
|
import (
|
||||||
|
O "github.com/IBM/fp-go/ord"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Bounded[T any] interface {
|
||||||
|
O.Ord[T]
|
||||||
|
Top() T
|
||||||
|
Bottom() T
|
||||||
|
}
|
||||||
|
|
||||||
|
type bounded[T any] struct {
|
||||||
|
c func(x, y T) int
|
||||||
|
e func(x, y T) bool
|
||||||
|
t T
|
||||||
|
b T
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self bounded[T]) Equals(x, y T) bool {
|
||||||
|
return self.e(x, y)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self bounded[T]) Compare(x, y T) int {
|
||||||
|
return self.c(x, y)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self bounded[T]) Top() T {
|
||||||
|
return self.t
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self bounded[T]) Bottom() T {
|
||||||
|
return self.b
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeBounded creates an instance of a bounded type
|
||||||
|
func MakeBounded[T any](o O.Ord[T], t, b T) Bounded[T] {
|
||||||
|
return bounded[T]{c: o.Compare, e: o.Equals, t: t, b: b}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clamp returns a function that clamps against the bounds defined in the bounded type
|
||||||
|
func Clamp[T any](b Bounded[T]) func(T) T {
|
||||||
|
return O.Clamp[T](b)(b.Bottom(), b.Top())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reverse reverses the ordering and swaps the bounds
|
||||||
|
func Reverse[T any](b Bounded[T]) Bounded[T] {
|
||||||
|
return MakeBounded(O.Reverse[T](b), b.Bottom(), b.Top())
|
||||||
|
}
|
@ -18,3 +18,7 @@ package bytes
|
|||||||
func ToString(a []byte) string {
|
func ToString(a []byte) string {
|
||||||
return string(a)
|
return string(a)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Size(as []byte) int {
|
||||||
|
return len(as)
|
||||||
|
}
|
||||||
|
@ -19,7 +19,7 @@ import (
|
|||||||
G "github.com/IBM/fp-go/context/readerioeither/generic"
|
G "github.com/IBM/fp-go/context/readerioeither/generic"
|
||||||
)
|
)
|
||||||
|
|
||||||
// WithContext wraps an existing ReaderIOEither and performs a context check for cancellation before delegating
|
// WithContext wraps an existing [ReaderIOEither] and performs a context check for cancellation before delegating
|
||||||
func WithContext[A any](ma ReaderIOEither[A]) ReaderIOEither[A] {
|
func WithContext[A any](ma ReaderIOEither[A]) ReaderIOEither[A] {
|
||||||
return G.WithContext(ma)
|
return G.WithContext(ma)
|
||||||
}
|
}
|
||||||
|
1
context/readerioeither/data/file.txt
Normal file
1
context/readerioeither/data/file.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
Carsten
|
@ -23,7 +23,7 @@ import (
|
|||||||
EQ "github.com/IBM/fp-go/eq"
|
EQ "github.com/IBM/fp-go/eq"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Eq implements the equals predicate for values contained in the IOEither monad
|
// Eq implements the equals predicate for values contained in the [ReaderIOEither] monad
|
||||||
func Eq[A any](eq EQ.Eq[ET.Either[error, A]]) func(context.Context) EQ.Eq[ReaderIOEither[A]] {
|
func Eq[A any](eq EQ.Eq[ET.Either[error, A]]) func(context.Context) EQ.Eq[ReaderIOEither[A]] {
|
||||||
return G.Eq[ReaderIOEither[A]](eq)
|
return G.Eq[ReaderIOEither[A]](eq)
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,7 @@ func Close[C io.Closer](c C) RIOE.ReaderIOEither[any] {
|
|||||||
|
|
||||||
// ReadFile reads a file in the scope of a context
|
// ReadFile reads a file in the scope of a context
|
||||||
func ReadFile(path string) RIOE.ReaderIOEither[[]byte] {
|
func ReadFile(path string) RIOE.ReaderIOEither[[]byte] {
|
||||||
return RIOE.WithResource[*os.File, []byte](Open(path), Close[*os.File])(func(r *os.File) RIOE.ReaderIOEither[[]byte] {
|
return RIOE.WithResource[[]byte](Open(path), Close[*os.File])(func(r *os.File) RIOE.ReaderIOEither[[]byte] {
|
||||||
return func(ctx context.Context) IOE.IOEither[error, []byte] {
|
return func(ctx context.Context) IOE.IOEither[error, []byte] {
|
||||||
return IOE.MakeIO(func() ET.Either[error, []byte] {
|
return IOE.MakeIO(func() ET.Either[error, []byte] {
|
||||||
return file.ReadAll(ctx, r)
|
return file.ReadAll(ctx, r)
|
||||||
|
@ -20,7 +20,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// WithResource constructs a function that creates a resource, then operates on it and then releases the resource
|
// WithResource constructs a function that creates a resource, then operates on it and then releases the resource
|
||||||
func WithResource[R, A, ANY any](onCreate ReaderIOEither[R], onRelease func(R) ReaderIOEither[ANY]) func(func(R) ReaderIOEither[A]) ReaderIOEither[A] {
|
func WithResource[A, R, ANY any](onCreate ReaderIOEither[R], onRelease func(R) ReaderIOEither[ANY]) func(func(R) ReaderIOEither[A]) ReaderIOEither[A] {
|
||||||
// wraps the callback functions with a context check
|
// wraps the callback functions with a context check
|
||||||
return G.WithResource[ReaderIOEither[A]](onCreate, onRelease)
|
return G.WithResource[ReaderIOEither[A]](onCreate, onRelease)
|
||||||
}
|
}
|
||||||
|
79
context/readerioeither/resource_test.go
Normal file
79
context/readerioeither/resource_test.go
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package readerioeither
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
B "github.com/IBM/fp-go/bytes"
|
||||||
|
F "github.com/IBM/fp-go/function"
|
||||||
|
IO "github.com/IBM/fp-go/io"
|
||||||
|
IOE "github.com/IBM/fp-go/ioeither"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
openFile = F.Flow3(
|
||||||
|
IOE.Eitherize1(os.Open),
|
||||||
|
FromIOEither[*os.File],
|
||||||
|
ChainFirstIOK(F.Flow2(
|
||||||
|
(*os.File).Name,
|
||||||
|
IO.Logf[string]("Opened file [%s]"),
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
func closeFile(f *os.File) ReaderIOEither[string] {
|
||||||
|
return F.Pipe1(
|
||||||
|
TryCatch(func(_ context.Context) func() (string, error) {
|
||||||
|
return func() (string, error) {
|
||||||
|
return f.Name(), f.Close()
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
ChainFirstIOK(IO.Logf[string]("Closed file [%s]")),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleWithResource() {
|
||||||
|
|
||||||
|
stringReader := WithResource[string](openFile("data/file.txt"), closeFile)
|
||||||
|
|
||||||
|
rdr := stringReader(func(f *os.File) ReaderIOEither[string] {
|
||||||
|
return F.Pipe2(
|
||||||
|
TryCatch(func(_ context.Context) func() ([]byte, error) {
|
||||||
|
return func() ([]byte, error) {
|
||||||
|
return io.ReadAll(f)
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
ChainFirstIOK(F.Flow2(
|
||||||
|
B.Size,
|
||||||
|
IO.Logf[int]("Read content of length [%d]"),
|
||||||
|
)),
|
||||||
|
Map(B.ToString),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
contentIOE := F.Pipe2(
|
||||||
|
context.Background(),
|
||||||
|
rdr,
|
||||||
|
IOE.ChainFirstIOK[error](IO.Printf[string]("Content: %s")),
|
||||||
|
)
|
||||||
|
|
||||||
|
contentIOE()
|
||||||
|
|
||||||
|
// Output: Content: Carsten
|
||||||
|
}
|
@ -21,5 +21,6 @@ import (
|
|||||||
RE "github.com/IBM/fp-go/readerioeither"
|
RE "github.com/IBM/fp-go/readerioeither"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ReaderIOEither is a specialization of the Reader monad for the typical golang scenario
|
// ReaderIOEither is a specialization of the [RE.ReaderIOEither] monad for the typical golang scenario in which the
|
||||||
|
// left value is an [error] and the context is a [context.Context]
|
||||||
type ReaderIOEither[A any] RE.ReaderIOEither[context.Context, error, A]
|
type ReaderIOEither[A any] RE.ReaderIOEither[context.Context, error, A]
|
||||||
|
51
erasure/erasure.go
Normal file
51
erasure/erasure.go
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package erasure
|
||||||
|
|
||||||
|
import (
|
||||||
|
F "github.com/IBM/fp-go/function"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Erase converts a variable of type T to an any by returning a pointer to that variable
|
||||||
|
func Erase[T any](t T) any {
|
||||||
|
return &t
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unerase converts an erased variable back to its original value
|
||||||
|
func Unerase[T any](t any) T {
|
||||||
|
return *t.(*T)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Erase0 converts a type safe function into an erased function
|
||||||
|
func Erase0[T1 any](f func() T1) func() any {
|
||||||
|
return F.Nullary2(f, Erase[T1])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Erase1 converts a type safe function into an erased function
|
||||||
|
func Erase1[T1, T2 any](f func(T1) T2) func(any) any {
|
||||||
|
return F.Flow3(
|
||||||
|
Unerase[T1],
|
||||||
|
f,
|
||||||
|
Erase[T2],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Erase2 converts a type safe function into an erased function
|
||||||
|
func Erase2[T1, T2, T3 any](f func(T1, T2) T3) func(any, any) any {
|
||||||
|
return func(t1, t2 any) any {
|
||||||
|
return Erase(f(Unerase[T1](t1), Unerase[T2](t2)))
|
||||||
|
}
|
||||||
|
}
|
39
erasure/erasure_test.go
Normal file
39
erasure/erasure_test.go
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package erasure
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
E "github.com/IBM/fp-go/either"
|
||||||
|
F "github.com/IBM/fp-go/function"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestEither(t *testing.T) {
|
||||||
|
|
||||||
|
e1 := F.Pipe3(
|
||||||
|
E.Of[error](Erase("Carsten")),
|
||||||
|
E.Map[error](Erase1(strings.ToUpper)),
|
||||||
|
E.GetOrElse(func(e error) any {
|
||||||
|
return Erase("Error")
|
||||||
|
}),
|
||||||
|
Unerase[string],
|
||||||
|
)
|
||||||
|
|
||||||
|
fmt.Println(e1)
|
||||||
|
}
|
80
internal/optiont/option.go
Normal file
80
internal/optiont/option.go
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package optiont
|
||||||
|
|
||||||
|
import (
|
||||||
|
F "github.com/IBM/fp-go/function"
|
||||||
|
"github.com/IBM/fp-go/internal/apply"
|
||||||
|
FC "github.com/IBM/fp-go/internal/functor"
|
||||||
|
O "github.com/IBM/fp-go/option"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Of[A, HKTA any](fof func(O.Option[A]) HKTA, a A) HKTA {
|
||||||
|
return F.Pipe2(a, O.Of[A], fof)
|
||||||
|
}
|
||||||
|
|
||||||
|
func None[A, HKTA any](fof func(O.Option[A]) HKTA) HKTA {
|
||||||
|
return F.Pipe1(O.None[A](), fof)
|
||||||
|
}
|
||||||
|
|
||||||
|
func OfF[A, HKTA, HKTEA any](fmap func(HKTA, func(A) O.Option[A]) HKTEA, fa HKTA) HKTEA {
|
||||||
|
return fmap(fa, O.Of[A])
|
||||||
|
}
|
||||||
|
|
||||||
|
func MonadMap[A, B, HKTFA, HKTFB any](fmap func(HKTFA, func(O.Option[A]) O.Option[B]) HKTFB, fa HKTFA, f func(A) B) HKTFB {
|
||||||
|
// HKTGA = Either[E, A]
|
||||||
|
// HKTGB = Either[E, B]
|
||||||
|
return FC.MonadMap(fmap, O.MonadMap[A, B], fa, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func MonadChain[A, B, HKTFA, HKTFB any](
|
||||||
|
fchain func(HKTFA, func(O.Option[A]) HKTFB) HKTFB,
|
||||||
|
fof func(O.Option[B]) HKTFB,
|
||||||
|
ma HKTFA,
|
||||||
|
f func(A) HKTFB) HKTFB {
|
||||||
|
// dispatch to the even more generic implementation
|
||||||
|
return fchain(ma, O.Fold(F.Nullary2(O.None[B], fof), f))
|
||||||
|
}
|
||||||
|
|
||||||
|
func MonadAp[A, B, HKTFAB, HKTFGAB, HKTFA, HKTFB any](
|
||||||
|
fap func(HKTFGAB, HKTFA) HKTFB,
|
||||||
|
fmap func(HKTFAB, func(O.Option[func(A) B]) func(O.Option[A]) O.Option[B]) HKTFGAB,
|
||||||
|
fab HKTFAB,
|
||||||
|
fa HKTFA) HKTFB {
|
||||||
|
// HKTGA = O.Option[A]
|
||||||
|
// HKTGB = O.Option[B]
|
||||||
|
// HKTGAB = O.Option[func(a A) B]
|
||||||
|
return apply.MonadAp(fap, fmap, O.MonadAp[B, A], fab, fa)
|
||||||
|
}
|
||||||
|
|
||||||
|
func MatchE[A, HKTEA, HKTB any](mchain func(HKTEA, func(O.Option[A]) HKTB) HKTB, onNone func() HKTB, onSome func(A) HKTB) func(HKTEA) HKTB {
|
||||||
|
return F.Bind2nd(mchain, O.Fold(onNone, onSome))
|
||||||
|
}
|
||||||
|
|
||||||
|
func FromOptionK[A, B, HKTB any](
|
||||||
|
fof func(O.Option[B]) HKTB,
|
||||||
|
f func(A) O.Option[B]) func(A) HKTB {
|
||||||
|
return F.Flow2(f, fof)
|
||||||
|
}
|
||||||
|
|
||||||
|
func MonadChainOptionK[A, B, HKTA, HKTB any](
|
||||||
|
fchain func(HKTA, func(O.Option[A]) HKTB) HKTB,
|
||||||
|
fof func(O.Option[B]) HKTB,
|
||||||
|
ma HKTA,
|
||||||
|
f func(A) O.Option[B],
|
||||||
|
) HKTB {
|
||||||
|
return MonadChain(fchain, fof, ma, FromOptionK(fof, f))
|
||||||
|
}
|
@ -22,6 +22,14 @@ import (
|
|||||||
|
|
||||||
var Upper = strings.ToUpper
|
var Upper = strings.ToUpper
|
||||||
|
|
||||||
|
func Inc(i int) int {
|
||||||
|
return i + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func Dec(i int) int {
|
||||||
|
return i - 1
|
||||||
|
}
|
||||||
|
|
||||||
func Sum(left, right int) int {
|
func Sum(left, right int) int {
|
||||||
return left + right
|
return left + right
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
package generic
|
package generic
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
Logging "github.com/IBM/fp-go/logging"
|
Logging "github.com/IBM/fp-go/logging"
|
||||||
@ -39,3 +40,11 @@ func Logf[GA ~func() any, A any](prefix string) func(A) GA {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Printf[GA ~func() any, A any](prefix string) func(A) GA {
|
||||||
|
return func(a A) GA {
|
||||||
|
return FromImpure[GA](func() {
|
||||||
|
fmt.Printf(prefix, a)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -31,3 +31,9 @@ func Logger[A any](loggers ...*log.Logger) func(string) func(A) IO[any] {
|
|||||||
func Logf[A any](prefix string) func(A) IO[any] {
|
func Logf[A any](prefix string) func(A) IO[any] {
|
||||||
return G.Logf[IO[any], A](prefix)
|
return G.Logf[IO[any], A](prefix)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Printf constructs a printer function that can be used with ChainXXXIOK
|
||||||
|
// the string prefix contains the format string for the log value
|
||||||
|
func Printf[A any](prefix string) func(A) IO[any] {
|
||||||
|
return G.Printf[IO[any], A](prefix)
|
||||||
|
}
|
||||||
|
@ -20,21 +20,21 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// MonadApFirst combines two effectful actions, keeping only the result of the first.
|
// 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] {
|
func MonadApFirst[A, E, 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)
|
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.
|
// 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] {
|
func ApFirst[A, E, 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)
|
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.
|
// 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] {
|
func MonadApSecond[A, E, 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)
|
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.
|
// 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] {
|
func ApSecond[A, E, 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)
|
return G.ApSecond[IOEither[E, A], IOEither[E, B], IOEither[E, func(B) B]](second)
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ import (
|
|||||||
O "github.com/IBM/fp-go/option"
|
O "github.com/IBM/fp-go/option"
|
||||||
)
|
)
|
||||||
|
|
||||||
// IO represents a synchronous computation that may fail
|
// IOEither represents a synchronous computation that may fail
|
||||||
// refer to [https://andywhite.xyz/posts/2021-01-27-rte-foundations/#ioeitherlte-agt] for more details
|
// 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]]
|
type IOEither[E, A any] I.IO[ET.Either[E, A]]
|
||||||
|
|
||||||
|
@ -119,7 +119,7 @@ func TestApFirst(t *testing.T) {
|
|||||||
|
|
||||||
x := F.Pipe1(
|
x := F.Pipe1(
|
||||||
Of[error]("a"),
|
Of[error]("a"),
|
||||||
ApFirst[error, string](Of[error]("b")),
|
ApFirst[string](Of[error]("b")),
|
||||||
)
|
)
|
||||||
|
|
||||||
assert.Equal(t, E.Of[error]("a"), x())
|
assert.Equal(t, E.Of[error]("a"), x())
|
||||||
@ -129,7 +129,7 @@ func TestApSecond(t *testing.T) {
|
|||||||
|
|
||||||
x := F.Pipe1(
|
x := F.Pipe1(
|
||||||
Of[error]("a"),
|
Of[error]("a"),
|
||||||
ApSecond[error, string](Of[error]("b")),
|
ApSecond[string](Of[error]("b")),
|
||||||
)
|
)
|
||||||
|
|
||||||
assert.Equal(t, E.Of[error]("b"), x())
|
assert.Equal(t, E.Of[error]("b"), x())
|
||||||
|
30
iooption/array.go
Normal file
30
iooption/array.go
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package iooption
|
||||||
|
|
||||||
|
import (
|
||||||
|
G "github.com/IBM/fp-go/iooption/generic"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TraverseArray transforms an array
|
||||||
|
func TraverseArray[A, B any](f func(A) IOOption[B]) func([]A) IOOption[[]B] {
|
||||||
|
return G.TraverseArray[IOOption[B], IOOption[[]B], []A](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SequenceArray converts a homogeneous sequence of either into an either of sequence
|
||||||
|
func SequenceArray[A any](ma []IOOption[A]) IOOption[[]A] {
|
||||||
|
return G.SequenceArray[IOOption[A], IOOption[[]A], []IOOption[A], []A, A](ma)
|
||||||
|
}
|
180
iooption/generic/iooption.go
Normal file
180
iooption/generic/iooption.go
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package generic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
F "github.com/IBM/fp-go/function"
|
||||||
|
FI "github.com/IBM/fp-go/internal/fromio"
|
||||||
|
"github.com/IBM/fp-go/internal/optiont"
|
||||||
|
IO "github.com/IBM/fp-go/io/generic"
|
||||||
|
O "github.com/IBM/fp-go/option"
|
||||||
|
)
|
||||||
|
|
||||||
|
// type IOOption[A any] = func() Option[A]
|
||||||
|
|
||||||
|
func MakeIO[GA ~func() O.Option[A], A any](f GA) GA {
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
func Of[GA ~func() O.Option[A], A any](r A) GA {
|
||||||
|
return MakeIO(optiont.Of(IO.MonadOf[GA, O.Option[A]], r))
|
||||||
|
}
|
||||||
|
|
||||||
|
func Some[GA ~func() O.Option[A], A any](r A) GA {
|
||||||
|
return Of[GA](r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func None[GA ~func() O.Option[A], A any]() GA {
|
||||||
|
return MakeIO(optiont.None(IO.MonadOf[GA, O.Option[A]]))
|
||||||
|
}
|
||||||
|
|
||||||
|
func MonadOf[GA ~func() O.Option[A], A any](r A) GA {
|
||||||
|
return Of[GA](r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func FromIO[GA ~func() O.Option[A], GR ~func() A, A any](mr GR) GA {
|
||||||
|
return MakeIO(optiont.OfF(IO.MonadMap[GR, GA, A, O.Option[A]], mr))
|
||||||
|
}
|
||||||
|
|
||||||
|
func FromOption[GA ~func() O.Option[A], A any](o O.Option[A]) GA {
|
||||||
|
return IO.Of[GA](o)
|
||||||
|
}
|
||||||
|
|
||||||
|
func MonadMap[GA ~func() O.Option[A], GB ~func() O.Option[B], A, B any](fa GA, f func(A) B) GB {
|
||||||
|
return optiont.MonadMap(IO.MonadMap[GA, GB, O.Option[A], O.Option[B]], fa, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Map[GA ~func() O.Option[A], GB ~func() O.Option[B], A, B any](f func(A) B) func(GA) GB {
|
||||||
|
return F.Bind2nd(MonadMap[GA, GB, A, B], f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func MonadChain[GA ~func() O.Option[A], GB ~func() O.Option[B], A, B any](fa GA, f func(A) GB) GB {
|
||||||
|
return optiont.MonadChain(IO.MonadChain[GA, GB, O.Option[A], O.Option[B]], IO.MonadOf[GB, O.Option[B]], fa, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Chain[GA ~func() O.Option[A], GB ~func() O.Option[B], A, B any](f func(A) GB) func(GA) GB {
|
||||||
|
return F.Bind2nd(MonadChain[GA, GB, A, B], f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func MonadChainOptionK[GA ~func() O.Option[A], GB ~func() O.Option[B], A, B any](ma GA, f func(A) O.Option[B]) GB {
|
||||||
|
return optiont.MonadChainOptionK(
|
||||||
|
IO.MonadChain[GA, GB, O.Option[A], O.Option[B]],
|
||||||
|
FromOption[GB, B],
|
||||||
|
ma,
|
||||||
|
f,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ChainOptionK[GA ~func() O.Option[A], GB ~func() O.Option[B], A, B any](f func(A) O.Option[B]) func(GA) GB {
|
||||||
|
return F.Bind2nd(MonadChainOptionK[GA, GB, A, B], f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func MonadChainIOK[GA ~func() O.Option[A], GB ~func() O.Option[B], GR ~func() B, A, B any](ma GA, f func(A) GR) GB {
|
||||||
|
return FI.MonadChainIOK(
|
||||||
|
MonadChain[GA, GB, A, B],
|
||||||
|
FromIO[GB, GR, B],
|
||||||
|
ma,
|
||||||
|
f,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ChainIOK[GA ~func() O.Option[A], GB ~func() O.Option[B], GR ~func() B, A, B any](f func(A) GR) func(GA) GB {
|
||||||
|
return FI.ChainIOK(
|
||||||
|
MonadChain[GA, GB, A, B],
|
||||||
|
FromIO[GB, GR, B],
|
||||||
|
f,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func MonadAp[GA ~func() O.Option[A], GB ~func() O.Option[B], GAB ~func() O.Option[func(A) B], A, B any](mab GAB, ma GA) GB {
|
||||||
|
return optiont.MonadAp(
|
||||||
|
IO.MonadAp[GA, GB, func() func(O.Option[A]) O.Option[B], O.Option[A], O.Option[B]],
|
||||||
|
IO.MonadMap[GAB, func() func(O.Option[A]) O.Option[B], O.Option[func(A) B], func(O.Option[A]) O.Option[B]],
|
||||||
|
mab, ma)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Ap[GA ~func() O.Option[A], GB ~func() O.Option[B], GAB ~func() O.Option[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() O.Option[A], GAA ~func() O.Option[GA], A any](mma GAA) GA {
|
||||||
|
return MonadChain(mma, F.Identity[GA])
|
||||||
|
}
|
||||||
|
|
||||||
|
func Optionize0[GA ~func() O.Option[A], A any](f func() (A, bool)) func() GA {
|
||||||
|
ef := O.Optionize0(f)
|
||||||
|
return func() GA {
|
||||||
|
return MakeIO[GA](ef)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Optionize1[GA ~func() O.Option[A], T1, A any](f func(t1 T1) (A, bool)) func(T1) GA {
|
||||||
|
ef := O.Optionize1(f)
|
||||||
|
return func(t1 T1) GA {
|
||||||
|
return MakeIO[GA](func() O.Option[A] {
|
||||||
|
return ef(t1)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Optionize2[GA ~func() O.Option[A], T1, T2, A any](f func(t1 T1, t2 T2) (A, bool)) func(T1, T2) GA {
|
||||||
|
ef := O.Optionize2(f)
|
||||||
|
return func(t1 T1, t2 T2) GA {
|
||||||
|
return MakeIO[GA](func() O.Option[A] {
|
||||||
|
return ef(t1, t2)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Optionize3[GA ~func() O.Option[A], T1, T2, T3, A any](f func(t1 T1, t2 T2, t3 T3) (A, bool)) func(T1, T2, T3) GA {
|
||||||
|
ef := O.Optionize3(f)
|
||||||
|
return func(t1 T1, t2 T2, t3 T3) GA {
|
||||||
|
return MakeIO[GA](func() O.Option[A] {
|
||||||
|
return ef(t1, t2, t3)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Optionize4[GA ~func() O.Option[A], T1, T2, T3, T4, A any](f func(t1 T1, t2 T2, t3 T3, t4 T4) (A, bool)) func(T1, T2, T3, T4) GA {
|
||||||
|
ef := O.Optionize4(f)
|
||||||
|
return func(t1 T1, t2 T2, t3 T3, t4 T4) GA {
|
||||||
|
return MakeIO[GA](func() O.Option[A] {
|
||||||
|
return ef(t1, t2, t3, t4)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Memoize computes the value of the provided IO monad lazily but exactly once
|
||||||
|
func Memoize[GA ~func() O.Option[A], A any](ma GA) GA {
|
||||||
|
return IO.Memoize(ma)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delay creates an operation that passes in the value after some delay
|
||||||
|
func Delay[GA ~func() O.Option[A], A any](delay time.Duration) func(GA) GA {
|
||||||
|
return IO.Delay[GA](delay)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fold convers an IOOption into an IO
|
||||||
|
func Fold[GA ~func() O.Option[A], GB ~func() B, A, B any](onNone func() GB, onSome func(A) GB) func(GA) GB {
|
||||||
|
return optiont.MatchE(IO.MonadChain[GA, GB, O.Option[A], B], onNone, onSome)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Defer creates an IO by creating a brand new IO via a generator function, each time
|
||||||
|
func Defer[GA ~func() O.Option[A], A any](gen func() GA) GA {
|
||||||
|
return IO.Defer[GA](gen)
|
||||||
|
}
|
41
iooption/generic/retry.go
Normal file
41
iooption/generic/retry.go
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package generic
|
||||||
|
|
||||||
|
import (
|
||||||
|
O "github.com/IBM/fp-go/option"
|
||||||
|
R "github.com/IBM/fp-go/retry"
|
||||||
|
G "github.com/IBM/fp-go/retry/generic"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
func Retrying[GA ~func() O.Option[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[func() O.Option[R.RetryStatus], GA, R.RetryStatus, A],
|
||||||
|
Of[GA, A],
|
||||||
|
Of[func() O.Option[R.RetryStatus], R.RetryStatus],
|
||||||
|
Delay[func() O.Option[R.RetryStatus], R.RetryStatus],
|
||||||
|
|
||||||
|
policy, action, check)
|
||||||
|
}
|
62
iooption/generic/sequence.go
Normal file
62
iooption/generic/sequence.go
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package generic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/IBM/fp-go/internal/apply"
|
||||||
|
O "github.com/IBM/fp-go/option"
|
||||||
|
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() O.Option[A], GTA ~func() O.Option[T.Tuple1[A]], A any](a GA) GTA {
|
||||||
|
return apply.SequenceT1(
|
||||||
|
Map[GA, GTA, A, T.Tuple1[A]],
|
||||||
|
|
||||||
|
a,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SequenceT2[GA ~func() O.Option[A], GB ~func() O.Option[B], GTAB ~func() O.Option[T.Tuple2[A, B]], A, B any](a GA, b GB) GTAB {
|
||||||
|
return apply.SequenceT2(
|
||||||
|
Map[GA, func() O.Option[func(B) T.Tuple2[A, B]], A, func(B) T.Tuple2[A, B]],
|
||||||
|
Ap[GB, GTAB, func() O.Option[func(B) T.Tuple2[A, B]], B, T.Tuple2[A, B]],
|
||||||
|
|
||||||
|
a, b,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SequenceT3[GA ~func() O.Option[A], GB ~func() O.Option[B], GC ~func() O.Option[C], GTABC ~func() O.Option[T.Tuple3[A, B, C]], A, B, C any](a GA, b GB, c GC) GTABC {
|
||||||
|
return apply.SequenceT3(
|
||||||
|
Map[GA, func() O.Option[func(B) func(C) T.Tuple3[A, B, C]], A, func(B) func(C) T.Tuple3[A, B, C]],
|
||||||
|
Ap[GB, func() O.Option[func(C) T.Tuple3[A, B, C]], func() O.Option[func(B) func(C) T.Tuple3[A, B, C]], B, func(C) T.Tuple3[A, B, C]],
|
||||||
|
Ap[GC, GTABC, func() O.Option[func(C) T.Tuple3[A, B, C]], C, T.Tuple3[A, B, C]],
|
||||||
|
|
||||||
|
a, b, c,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SequenceT4[GA ~func() O.Option[A], GB ~func() O.Option[B], GC ~func() O.Option[C], GD ~func() O.Option[D], GTABCD ~func() O.Option[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() O.Option[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() O.Option[func(C) func(D) T.Tuple4[A, B, C, D]], func() O.Option[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() O.Option[func(D) T.Tuple4[A, B, C, D]], func() O.Option[func(C) func(D) T.Tuple4[A, B, C, D]], C, func(D) T.Tuple4[A, B, C, D]],
|
||||||
|
Ap[GD, GTABCD, func() O.Option[func(D) T.Tuple4[A, B, C, D]], D, T.Tuple4[A, B, C, D]],
|
||||||
|
|
||||||
|
a, b, c, d,
|
||||||
|
)
|
||||||
|
}
|
33
iooption/generic/traverse.go
Normal file
33
iooption/generic/traverse.go
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package generic
|
||||||
|
|
||||||
|
import (
|
||||||
|
F "github.com/IBM/fp-go/function"
|
||||||
|
I "github.com/IBM/fp-go/io/generic"
|
||||||
|
O "github.com/IBM/fp-go/option"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TraverseArray[TB ~func() O.Option[B], TBS ~func() O.Option[GB], GA ~[]A, GB ~[]B, A, B any](f func(A) TB) func(GA) TBS {
|
||||||
|
return F.Flow2(
|
||||||
|
I.TraverseArray[TB, func() []O.Option[B], GA](f),
|
||||||
|
I.Map[func() []O.Option[B], TBS](O.SequenceArrayG[GB, []O.Option[B], B]),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SequenceArray[TB ~func() O.Option[B], TBS ~func() O.Option[GB], GA ~[]TB, GB ~[]B, A, B any](ma GA) TBS {
|
||||||
|
return TraverseArray[TB, TBS, GA](F.Identity[TB])(ma)
|
||||||
|
}
|
128
iooption/iooption.go
Normal file
128
iooption/iooption.go
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package iooption
|
||||||
|
|
||||||
|
import (
|
||||||
|
I "github.com/IBM/fp-go/io"
|
||||||
|
G "github.com/IBM/fp-go/iooption/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 IOOption[A any] I.IO[O.Option[A]]
|
||||||
|
|
||||||
|
func MakeIO[A any](f IOOption[A]) IOOption[A] {
|
||||||
|
return G.MakeIO(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Of[A any](r A) IOOption[A] {
|
||||||
|
return G.Of[IOOption[A]](r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Some[A any](r A) IOOption[A] {
|
||||||
|
return G.Some[IOOption[A]](r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func None[A any]() IOOption[A] {
|
||||||
|
return G.None[IOOption[A]]()
|
||||||
|
}
|
||||||
|
|
||||||
|
func MonadOf[A any](r A) IOOption[A] {
|
||||||
|
return G.MonadOf[IOOption[A]](r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func FromOption[A any](o O.Option[A]) IOOption[A] {
|
||||||
|
return G.FromOption[IOOption[A]](o)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ChainOptionK[A, B any](f func(A) O.Option[B]) func(IOOption[A]) IOOption[B] {
|
||||||
|
return G.ChainOptionK[IOOption[A], IOOption[B]](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func MonadChainIOK[A, B any](ma IOOption[A], f func(A) I.IO[B]) IOOption[B] {
|
||||||
|
return G.MonadChainIOK[IOOption[A], IOOption[B]](ma, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ChainIOK[A, B any](f func(A) I.IO[B]) func(IOOption[A]) IOOption[B] {
|
||||||
|
return G.ChainIOK[IOOption[A], IOOption[B]](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func FromIO[A any](mr I.IO[A]) IOOption[A] {
|
||||||
|
return G.FromIO[IOOption[A]](mr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func MonadMap[A, B any](fa IOOption[A], f func(A) B) IOOption[B] {
|
||||||
|
return G.MonadMap[IOOption[A], IOOption[B]](fa, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Map[A, B any](f func(A) B) func(IOOption[A]) IOOption[B] {
|
||||||
|
return G.Map[IOOption[A], IOOption[B]](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func MonadChain[A, B any](fa IOOption[A], f func(A) IOOption[B]) IOOption[B] {
|
||||||
|
return G.MonadChain(fa, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Chain[A, B any](f func(A) IOOption[B]) func(IOOption[A]) IOOption[B] {
|
||||||
|
return G.Chain[IOOption[A]](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func MonadAp[B, A any](mab IOOption[func(A) B], ma IOOption[A]) IOOption[B] {
|
||||||
|
return G.MonadAp[IOOption[A], IOOption[B]](mab, ma)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Ap[B, A any](ma IOOption[A]) func(IOOption[func(A) B]) IOOption[B] {
|
||||||
|
return G.Ap[IOOption[A], IOOption[B], IOOption[func(A) B]](ma)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Flatten[A any](mma IOOption[IOOption[A]]) IOOption[A] {
|
||||||
|
return G.Flatten(mma)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Optionize0[A any](f func() (A, bool)) func() IOOption[A] {
|
||||||
|
return G.Optionize0[IOOption[A]](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Optionize1[T1, A any](f func(t1 T1) (A, bool)) func(T1) IOOption[A] {
|
||||||
|
return G.Optionize1[IOOption[A]](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Optionize2[T1, T2, A any](f func(t1 T1, t2 T2) (A, bool)) func(T1, T2) IOOption[A] {
|
||||||
|
return G.Optionize2[IOOption[A]](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Optionize3[T1, T2, T3, A any](f func(t1 T1, t2 T2, t3 T3) (A, bool)) func(T1, T2, T3) IOOption[A] {
|
||||||
|
return G.Optionize3[IOOption[A]](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Optionize4[T1, T2, T3, T4, A any](f func(t1 T1, t2 T2, t3 T3, t4 T4) (A, bool)) func(T1, T2, T3, T4) IOOption[A] {
|
||||||
|
return G.Optionize4[IOOption[A]](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Memoize[A any](ma IOOption[A]) IOOption[A] {
|
||||||
|
return G.Memoize(ma)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fold convers an IOOption into an IO
|
||||||
|
func Fold[A, B any](onNone func() I.IO[B], onSome func(A) I.IO[B]) func(IOOption[A]) I.IO[B] {
|
||||||
|
return G.Fold[IOOption[A]](onNone, onSome)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Defer creates an IO by creating a brand new IO via a generator function, each time
|
||||||
|
func Defer[A any](gen func() IOOption[A]) IOOption[A] {
|
||||||
|
return G.Defer[IOOption[A]](gen)
|
||||||
|
}
|
73
iooption/iooption_test.go
Normal file
73
iooption/iooption_test.go
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package iooption
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
F "github.com/IBM/fp-go/function"
|
||||||
|
"github.com/IBM/fp-go/internal/utils"
|
||||||
|
I "github.com/IBM/fp-go/io"
|
||||||
|
O "github.com/IBM/fp-go/option"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMap(t *testing.T) {
|
||||||
|
assert.Equal(t, O.Of(2), F.Pipe1(
|
||||||
|
Of(1),
|
||||||
|
Map(utils.Double),
|
||||||
|
)())
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestChainOptionK(t *testing.T) {
|
||||||
|
f := ChainOptionK(func(n int) O.Option[int] {
|
||||||
|
if n > 0 {
|
||||||
|
return O.Of(n)
|
||||||
|
}
|
||||||
|
return O.None[int]()
|
||||||
|
|
||||||
|
})
|
||||||
|
assert.Equal(t, O.Of(1), f(Of(1))())
|
||||||
|
assert.Equal(t, O.None[int](), f(Of(-1))())
|
||||||
|
assert.Equal(t, O.None[int](), f(None[int]())())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFromOption(t *testing.T) {
|
||||||
|
f := FromOption[int]
|
||||||
|
assert.Equal(t, O.Of(1), f(O.Some(1))())
|
||||||
|
assert.Equal(t, O.None[int](), f(O.None[int]())())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestChainIOK(t *testing.T) {
|
||||||
|
f := ChainIOK(func(n int) I.IO[string] {
|
||||||
|
return I.MakeIO(func() string {
|
||||||
|
return fmt.Sprintf("%d", n)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.Equal(t, O.Of("1"), f(Of(1))())
|
||||||
|
assert.Equal(t, O.None[string](), f(None[int]())())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEnv(t *testing.T) {
|
||||||
|
env := Optionize1(os.LookupEnv)
|
||||||
|
|
||||||
|
assert.True(t, O.IsSome(env("PATH")()))
|
||||||
|
assert.False(t, O.IsSome(env("PATHxyz")()))
|
||||||
|
}
|
30
iooption/retry.go
Normal file
30
iooption/retry.go
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package iooption
|
||||||
|
|
||||||
|
import (
|
||||||
|
G "github.com/IBM/fp-go/iooption/generic"
|
||||||
|
R "github.com/IBM/fp-go/retry"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Retrying will retry the actions according to the check policy
|
||||||
|
func Retrying[A any](
|
||||||
|
policy R.RetryPolicy,
|
||||||
|
action func(R.RetryStatus) IOOption[A],
|
||||||
|
check func(A) bool,
|
||||||
|
) IOOption[A] {
|
||||||
|
return G.Retrying(policy, action, check)
|
||||||
|
}
|
69
iooption/sequence.go
Normal file
69
iooption/sequence.go
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package iooption
|
||||||
|
|
||||||
|
import (
|
||||||
|
G "github.com/IBM/fp-go/iooption/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 IOOption[A]) IOOption[T.Tuple1[A]] {
|
||||||
|
return G.SequenceT1[
|
||||||
|
IOOption[A],
|
||||||
|
IOOption[T.Tuple1[A]],
|
||||||
|
](a)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SequenceT2[A, B any](
|
||||||
|
a IOOption[A],
|
||||||
|
b IOOption[B],
|
||||||
|
) IOOption[T.Tuple2[A, B]] {
|
||||||
|
return G.SequenceT2[
|
||||||
|
IOOption[A],
|
||||||
|
IOOption[B],
|
||||||
|
IOOption[T.Tuple2[A, B]],
|
||||||
|
](a, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SequenceT3[A, B, C any](
|
||||||
|
a IOOption[A],
|
||||||
|
b IOOption[B],
|
||||||
|
c IOOption[C],
|
||||||
|
) IOOption[T.Tuple3[A, B, C]] {
|
||||||
|
return G.SequenceT3[
|
||||||
|
IOOption[A],
|
||||||
|
IOOption[B],
|
||||||
|
IOOption[C],
|
||||||
|
IOOption[T.Tuple3[A, B, C]],
|
||||||
|
](a, b, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SequenceT4[A, B, C, D any](
|
||||||
|
a IOOption[A],
|
||||||
|
b IOOption[B],
|
||||||
|
c IOOption[C],
|
||||||
|
d IOOption[D],
|
||||||
|
) IOOption[T.Tuple4[A, B, C, D]] {
|
||||||
|
return G.SequenceT4[
|
||||||
|
IOOption[A],
|
||||||
|
IOOption[B],
|
||||||
|
IOOption[C],
|
||||||
|
IOOption[D],
|
||||||
|
IOOption[T.Tuple4[A, B, C, D]],
|
||||||
|
](a, b, c, d)
|
||||||
|
}
|
18
iterator/stateless/doc.go
Normal file
18
iterator/stateless/doc.go
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// Package stateless defines a stateless (pure) iterator, i.e. one that can be iterated over multiple times without
|
||||||
|
// side effects, it is threadsafe
|
||||||
|
package stateless
|
169
iterator/stateless/generic/iterator.go
Normal file
169
iterator/stateless/generic/iterator.go
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package generic
|
||||||
|
|
||||||
|
import (
|
||||||
|
A "github.com/IBM/fp-go/array/generic"
|
||||||
|
F "github.com/IBM/fp-go/function"
|
||||||
|
"github.com/IBM/fp-go/internal/utils"
|
||||||
|
IO "github.com/IBM/fp-go/iooption/generic"
|
||||||
|
N "github.com/IBM/fp-go/number/integer"
|
||||||
|
O "github.com/IBM/fp-go/option"
|
||||||
|
T "github.com/IBM/fp-go/tuple"
|
||||||
|
)
|
||||||
|
|
||||||
|
// From constructs an array from a set of variadic arguments
|
||||||
|
func From[GU ~func() O.Option[T.Tuple2[GU, U]], U any](data ...U) GU {
|
||||||
|
return FromArray[GU](data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Empty returns the empty iterator
|
||||||
|
func Empty[GU ~func() O.Option[T.Tuple2[GU, U]], U any]() GU {
|
||||||
|
return IO.None[GU]()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Of returns an iterator with one single element
|
||||||
|
func Of[GU ~func() O.Option[T.Tuple2[GU, U]], U any](a U) GU {
|
||||||
|
return IO.Of[GU](T.MakeTuple2(Empty[GU](), a))
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromArray returns an iterator from multiple elements
|
||||||
|
func FromArray[GU ~func() O.Option[T.Tuple2[GU, U]], US ~[]U, U any](as US) GU {
|
||||||
|
return A.MatchLeft(Empty[GU], func(head U, tail US) GU {
|
||||||
|
return func() O.Option[T.Tuple2[GU, U]] {
|
||||||
|
return O.Of(T.MakeTuple2(FromArray[GU](tail), head))
|
||||||
|
}
|
||||||
|
})(as)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reduce applies a function for each value of the iterator with a floating result
|
||||||
|
func Reduce[GU ~func() O.Option[T.Tuple2[GU, U]], U, V any](f func(V, U) V, initial V) func(GU) V {
|
||||||
|
return func(as GU) V {
|
||||||
|
next, ok := O.Unwrap(as())
|
||||||
|
current := initial
|
||||||
|
for ok {
|
||||||
|
// next (with bad side effect)
|
||||||
|
current = f(current, next.F2)
|
||||||
|
next, ok = O.Unwrap(next.F1())
|
||||||
|
}
|
||||||
|
return current
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToArray converts the iterator to an array
|
||||||
|
func ToArray[GU ~func() O.Option[T.Tuple2[GU, U]], US ~[]U, U any](u GU) US {
|
||||||
|
return Reduce[GU](A.Append[US], A.Empty[US]())(u)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Map[GV ~func() O.Option[T.Tuple2[GV, V]], GU ~func() O.Option[T.Tuple2[GU, U]], U, V any](f func(U) V) func(ma GU) GV {
|
||||||
|
// pre-declare to avoid cyclic reference
|
||||||
|
var m func(O.Option[T.Tuple2[GU, U]]) O.Option[T.Tuple2[GV, V]]
|
||||||
|
|
||||||
|
recurse := func(ma GU) GV {
|
||||||
|
return F.Nullary2(
|
||||||
|
ma,
|
||||||
|
m,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
m = O.Map(T.Map2(recurse, f))
|
||||||
|
|
||||||
|
return recurse
|
||||||
|
}
|
||||||
|
|
||||||
|
func MonadMap[GV ~func() O.Option[T.Tuple2[GV, V]], GU ~func() O.Option[T.Tuple2[GU, U]], U, V any](ma GU, f func(U) V) GV {
|
||||||
|
return Map[GV, GU](f)(ma)
|
||||||
|
}
|
||||||
|
|
||||||
|
func concat[GU ~func() O.Option[T.Tuple2[GU, U]], U any](right, left GU) GU {
|
||||||
|
var m func(ma O.Option[T.Tuple2[GU, U]]) O.Option[T.Tuple2[GU, U]]
|
||||||
|
|
||||||
|
recurse := func(left GU) GU {
|
||||||
|
return F.Nullary2(left, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
m = O.Fold(
|
||||||
|
right,
|
||||||
|
F.Flow2(
|
||||||
|
T.Map2(recurse, F.Identity[U]),
|
||||||
|
O.Some[T.Tuple2[GU, U]],
|
||||||
|
))
|
||||||
|
|
||||||
|
return recurse(left)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Chain[GV ~func() O.Option[T.Tuple2[GV, V]], GU ~func() O.Option[T.Tuple2[GU, U]], U, V any](f func(U) GV) func(GU) GV {
|
||||||
|
// pre-declare to avoid cyclic reference
|
||||||
|
var m func(O.Option[T.Tuple2[GU, U]]) O.Option[T.Tuple2[GV, V]]
|
||||||
|
|
||||||
|
recurse := func(ma GU) GV {
|
||||||
|
return F.Nullary2(
|
||||||
|
ma,
|
||||||
|
m,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
m = O.Chain(
|
||||||
|
F.Flow3(
|
||||||
|
T.Map2(recurse, f),
|
||||||
|
T.Tupled2(concat[GV]),
|
||||||
|
func(v GV) O.Option[T.Tuple2[GV, V]] {
|
||||||
|
return v()
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
return recurse
|
||||||
|
}
|
||||||
|
|
||||||
|
func MonadChain[GV ~func() O.Option[T.Tuple2[GV, V]], GU ~func() O.Option[T.Tuple2[GU, U]], U, V any](ma GU, f func(U) GV) GV {
|
||||||
|
return Chain[GV, GU](f)(ma)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Flatten[GV ~func() O.Option[T.Tuple2[GV, GU]], GU ~func() O.Option[T.Tuple2[GU, U]], U any](ma GV) GU {
|
||||||
|
return MonadChain(ma, F.Identity[GU])
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeBy returns an [Iterator] with `n` elements initialized with `f(i)`
|
||||||
|
func MakeBy[GU ~func() O.Option[T.Tuple2[GU, U]], FCT ~func(int) U, U any](n int, f FCT) GU {
|
||||||
|
|
||||||
|
var m func(int) O.Option[T.Tuple2[GU, U]]
|
||||||
|
|
||||||
|
recurse := func(i int) GU {
|
||||||
|
return func() O.Option[T.Tuple2[GU, U]] {
|
||||||
|
return F.Pipe1(
|
||||||
|
i,
|
||||||
|
m,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m = F.Flow2(
|
||||||
|
O.FromPredicate(N.Between(0, n)),
|
||||||
|
O.Map(F.Flow2(
|
||||||
|
T.Replicate2[int],
|
||||||
|
T.Map2(F.Flow2(
|
||||||
|
utils.Inc,
|
||||||
|
recurse),
|
||||||
|
f),
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
|
||||||
|
return recurse(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replicate creates an [Iterator] containing a value repeated the specified number of times.
|
||||||
|
func Replicate[GU ~func() O.Option[T.Tuple2[GU, U]], U any](n int, a U) GU {
|
||||||
|
return MakeBy[GU](n, F.Constant1[int](a))
|
||||||
|
}
|
30
iterator/stateless/generic/monoid.go
Normal file
30
iterator/stateless/generic/monoid.go
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package generic
|
||||||
|
|
||||||
|
import (
|
||||||
|
F "github.com/IBM/fp-go/function"
|
||||||
|
M "github.com/IBM/fp-go/monoid"
|
||||||
|
O "github.com/IBM/fp-go/option"
|
||||||
|
T "github.com/IBM/fp-go/tuple"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Monoid[GU ~func() O.Option[T.Tuple2[GU, U]], U any]() M.Monoid[GU] {
|
||||||
|
return M.MakeMonoid(
|
||||||
|
F.Swap(concat[GU]),
|
||||||
|
Empty[GU](),
|
||||||
|
)
|
||||||
|
}
|
84
iterator/stateless/iterator.go
Normal file
84
iterator/stateless/iterator.go
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package stateless
|
||||||
|
|
||||||
|
import (
|
||||||
|
G "github.com/IBM/fp-go/iterator/stateless/generic"
|
||||||
|
L "github.com/IBM/fp-go/lazy"
|
||||||
|
O "github.com/IBM/fp-go/option"
|
||||||
|
T "github.com/IBM/fp-go/tuple"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Iterator represents a stateless, pure way to iterate over a sequence
|
||||||
|
type Iterator[U any] L.Lazy[O.Option[T.Tuple2[Iterator[U], U]]]
|
||||||
|
|
||||||
|
// Empty returns the empty iterator
|
||||||
|
func Empty[U any]() Iterator[U] {
|
||||||
|
return G.Empty[Iterator[U]]()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Of returns an iterator with one single element
|
||||||
|
func Of[GU ~func() O.Option[T.Tuple2[GU, U]], U any](a U) Iterator[U] {
|
||||||
|
return G.Of[Iterator[U]](a)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromArray returns an iterator from multiple elements
|
||||||
|
func FromArray[U any](as []U) Iterator[U] {
|
||||||
|
return G.FromArray[Iterator[U]](as)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToArray converts the iterator to an array
|
||||||
|
func ToArray[U any](u Iterator[U]) []U {
|
||||||
|
return G.ToArray[Iterator[U], []U](u)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reduce applies a function for each value of the iterator with a floating result
|
||||||
|
func Reduce[U, V any](f func(V, U) V, initial V) func(Iterator[U]) V {
|
||||||
|
return G.Reduce[Iterator[U]](f, initial)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MonadMap transforms an [Iterator] of type [U] into an [Iterator] of type [V] via a mapping function
|
||||||
|
func MonadMap[U, V any](ma Iterator[U], f func(U) V) Iterator[V] {
|
||||||
|
return G.MonadMap[Iterator[V], Iterator[U]](ma, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map transforms an [Iterator] of type [U] into an [Iterator] of type [V] via a mapping function
|
||||||
|
func Map[U, V any](f func(U) V) func(ma Iterator[U]) Iterator[V] {
|
||||||
|
return G.Map[Iterator[V], Iterator[U]](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func MonadChain[U, V any](ma Iterator[U], f func(U) Iterator[V]) Iterator[V] {
|
||||||
|
return G.MonadChain[Iterator[V], Iterator[U]](ma, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Chain[U, V any](f func(U) Iterator[V]) func(Iterator[U]) Iterator[V] {
|
||||||
|
return G.Chain[Iterator[V], Iterator[U]](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flatten converts an [Iterator] of [Iterator] into a simple [Iterator]
|
||||||
|
func Flatten[U any](ma Iterator[Iterator[U]]) Iterator[U] {
|
||||||
|
return G.Flatten[Iterator[Iterator[U]], Iterator[U]](ma)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeBy returns an [Iterator] with `n` elements initialized with `f(i)`
|
||||||
|
func MakeBy[FCT ~func(int) U, U any](n int, f FCT) Iterator[U] {
|
||||||
|
return G.MakeBy[Iterator[U]](n, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replicate creates an [Iterator] containing a value repeated the specified number of times.
|
||||||
|
func Replicate[U any](n int, a U) Iterator[U] {
|
||||||
|
return G.Replicate[Iterator[U]](n, a)
|
||||||
|
}
|
60
iterator/stateless/iterator_test.go
Normal file
60
iterator/stateless/iterator_test.go
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package stateless
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
A "github.com/IBM/fp-go/array"
|
||||||
|
F "github.com/IBM/fp-go/function"
|
||||||
|
"github.com/IBM/fp-go/internal/utils"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestIterator(t *testing.T) {
|
||||||
|
|
||||||
|
result := F.Pipe2(
|
||||||
|
A.From(1, 2, 3),
|
||||||
|
FromArray[int],
|
||||||
|
Reduce(utils.Sum, 0),
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.Equal(t, 6, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestChain(t *testing.T) {
|
||||||
|
|
||||||
|
outer := FromArray[int](A.From(1, 2, 3))
|
||||||
|
|
||||||
|
inner := func(data int) Iterator[string] {
|
||||||
|
return F.Pipe2(
|
||||||
|
A.From(0, 1),
|
||||||
|
FromArray[int],
|
||||||
|
Map(func(idx int) string {
|
||||||
|
return fmt.Sprintf("item[%d][%d]", data, idx)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
total := F.Pipe2(
|
||||||
|
outer,
|
||||||
|
Chain(inner),
|
||||||
|
ToArray[string],
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.Equal(t, A.From("item[1][0]", "item[1][1]", "item[2][0]", "item[2][1]", "item[3][0]", "item[3][1]"), total)
|
||||||
|
}
|
26
iterator/stateless/monoid.go
Normal file
26
iterator/stateless/monoid.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package stateless
|
||||||
|
|
||||||
|
import (
|
||||||
|
G "github.com/IBM/fp-go/iterator/stateless/generic"
|
||||||
|
M "github.com/IBM/fp-go/monoid"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Monoid contructs a [M.Monoid] that concatenates two [Iterator]s
|
||||||
|
func Monoid[U any]() M.Monoid[Iterator[U]] {
|
||||||
|
return G.Monoid[Iterator[U]]()
|
||||||
|
}
|
36
lambda/y.go
Normal file
36
lambda/y.go
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package lambda
|
||||||
|
|
||||||
|
type (
|
||||||
|
// RecFct is the function called recursively
|
||||||
|
RecFct[T, R any] func(T) R
|
||||||
|
|
||||||
|
// transformer
|
||||||
|
Transformer[T, R any] func(RecFct[T, R]) RecFct[T, R]
|
||||||
|
|
||||||
|
internalCombinator[T, R any] func(internalCombinator[T, R]) RecFct[T, R]
|
||||||
|
)
|
||||||
|
|
||||||
|
// Y is the Y-combinator based on https://dreamsongs.com/Files/WhyOfY.pdf
|
||||||
|
func Y[T, R any](f Transformer[T, R]) RecFct[T, R] {
|
||||||
|
g := func(h internalCombinator[T, R]) RecFct[T, R] {
|
||||||
|
return func(t T) R {
|
||||||
|
return f(h(h))(t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return g(g)
|
||||||
|
}
|
34
lambda/y_test.go
Normal file
34
lambda/y_test.go
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package lambda
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFactorial(t *testing.T) {
|
||||||
|
fct := Y(func(r RecFct[int, int]) RecFct[int, int] {
|
||||||
|
return func(n int) int {
|
||||||
|
if n <= 0 {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return n * r(n-1)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
fmt.Println(fct(10))
|
||||||
|
}
|
30
lazy/apply.go
Normal file
30
lazy/apply.go
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package lazy
|
||||||
|
|
||||||
|
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[Lazy[A]] {
|
||||||
|
return G.ApplySemigroup[Lazy[A]](s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ApplicativeMonoid[A any](m M.Monoid[A]) M.Monoid[Lazy[A]] {
|
||||||
|
return G.ApplicativeMonoid[Lazy[A]](m)
|
||||||
|
}
|
26
lazy/eq.go
Normal file
26
lazy/eq.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package lazy
|
||||||
|
|
||||||
|
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[Lazy[A]] {
|
||||||
|
return G.Eq[Lazy[A]](e)
|
||||||
|
}
|
139
lazy/lazy.go
Normal file
139
lazy/lazy.go
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package lazy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
G "github.com/IBM/fp-go/io/generic"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Lazy represents a synchronous computation without side effects
|
||||||
|
type Lazy[A any] func() A
|
||||||
|
|
||||||
|
func MakeLazy[A any](f func() A) Lazy[A] {
|
||||||
|
return G.MakeIO[Lazy[A]](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Of[A any](a A) Lazy[A] {
|
||||||
|
return G.Of[Lazy[A]](a)
|
||||||
|
}
|
||||||
|
|
||||||
|
func FromLazy[A any](a Lazy[A]) Lazy[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()) Lazy[any] {
|
||||||
|
return G.FromImpure[Lazy[any]](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func MonadOf[A any](a A) Lazy[A] {
|
||||||
|
return G.MonadOf[Lazy[A]](a)
|
||||||
|
}
|
||||||
|
|
||||||
|
func MonadMap[A, B any](fa Lazy[A], f func(A) B) Lazy[B] {
|
||||||
|
return G.MonadMap[Lazy[A], Lazy[B]](fa, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Map[A, B any](f func(A) B) func(fa Lazy[A]) Lazy[B] {
|
||||||
|
return G.Map[Lazy[A], Lazy[B]](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func MonadMapTo[A, B any](fa Lazy[A], b B) Lazy[B] {
|
||||||
|
return G.MonadMapTo[Lazy[A], Lazy[B]](fa, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func MapTo[A, B any](b B) func(Lazy[A]) Lazy[B] {
|
||||||
|
return G.MapTo[Lazy[A], Lazy[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 Lazy[A], f func(A) Lazy[B]) Lazy[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) Lazy[B]) func(Lazy[A]) Lazy[B] {
|
||||||
|
return G.Chain[Lazy[A]](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func MonadAp[B, A any](mab Lazy[func(A) B], ma Lazy[A]) Lazy[B] {
|
||||||
|
return G.MonadAp[Lazy[A], Lazy[B]](mab, ma)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Ap[B, A any](ma Lazy[A]) func(Lazy[func(A) B]) Lazy[B] {
|
||||||
|
return G.Ap[Lazy[B], Lazy[func(A) B], Lazy[A]](ma)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Flatten[A any](mma Lazy[Lazy[A]]) Lazy[A] {
|
||||||
|
return G.Flatten(mma)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Memoize computes the value of the provided IO monad lazily but exactly once
|
||||||
|
func Memoize[A any](ma Lazy[A]) Lazy[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 Lazy[A], f func(A) Lazy[B]) Lazy[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) Lazy[B]) func(Lazy[A]) Lazy[A] {
|
||||||
|
return G.ChainFirst[Lazy[A]](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MonadApFirst combines two effectful actions, keeping only the result of the first.
|
||||||
|
func MonadApFirst[A, B any](first Lazy[A], second Lazy[B]) Lazy[A] {
|
||||||
|
return G.MonadApFirst[Lazy[A], Lazy[B], Lazy[func(B) A]](first, second)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApFirst combines two effectful actions, keeping only the result of the first.
|
||||||
|
func ApFirst[A, B any](second Lazy[B]) func(Lazy[A]) Lazy[A] {
|
||||||
|
return G.ApFirst[Lazy[A], Lazy[B], Lazy[func(B) A]](second)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MonadApSecond combines two effectful actions, keeping only the result of the second.
|
||||||
|
func MonadApSecond[A, B any](first Lazy[A], second Lazy[B]) Lazy[B] {
|
||||||
|
return G.MonadApSecond[Lazy[A], Lazy[B], Lazy[func(B) B]](first, second)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApSecond combines two effectful actions, keeping only the result of the second.
|
||||||
|
func ApSecond[A, B any](second Lazy[B]) func(Lazy[A]) Lazy[B] {
|
||||||
|
return G.ApSecond[Lazy[A], Lazy[B], Lazy[func(B) B]](second)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MonadChainTo composes computations in sequence, ignoring the return value of the first computation
|
||||||
|
func MonadChainTo[A, B any](fa Lazy[A], fb Lazy[B]) Lazy[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 Lazy[B]) func(Lazy[A]) Lazy[B] {
|
||||||
|
return G.ChainTo[Lazy[A]](fb)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now returns the current timestamp
|
||||||
|
var Now = G.Now[Lazy[time.Time]]()
|
||||||
|
|
||||||
|
// Defer creates an IO by creating a brand new IO via a generator function, each time
|
||||||
|
func Defer[A any](gen func() Lazy[A]) Lazy[A] {
|
||||||
|
return G.Defer[Lazy[A]](gen)
|
||||||
|
}
|
73
lazy/lazy_test.go
Normal file
73
lazy/lazy_test.go
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package lazy
|
||||||
|
|
||||||
|
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) Lazy[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(MakeLazy(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())
|
||||||
|
}
|
34
lazy/retry.go
Normal file
34
lazy/retry.go
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package lazy
|
||||||
|
|
||||||
|
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) Lazy[A],
|
||||||
|
check func(A) bool,
|
||||||
|
) Lazy[A] {
|
||||||
|
return G.Retrying(policy, action, check)
|
||||||
|
}
|
47
lazy/retry_test.go
Normal file
47
lazy/retry_test.go
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package lazy
|
||||||
|
|
||||||
|
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) Lazy[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())
|
||||||
|
}
|
39
lazy/sequence.go
Normal file
39
lazy/sequence.go
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package lazy
|
||||||
|
|
||||||
|
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 Lazy[A]) Lazy[T.Tuple1[A]] {
|
||||||
|
return G.SequenceT1[Lazy[A], Lazy[T.Tuple1[A]]](a)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SequenceT2[A, B any](a Lazy[A], b Lazy[B]) Lazy[T.Tuple2[A, B]] {
|
||||||
|
return G.SequenceT2[Lazy[A], Lazy[B], Lazy[T.Tuple2[A, B]]](a, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SequenceT3[A, B, C any](a Lazy[A], b Lazy[B], c Lazy[C]) Lazy[T.Tuple3[A, B, C]] {
|
||||||
|
return G.SequenceT3[Lazy[A], Lazy[B], Lazy[C], Lazy[T.Tuple3[A, B, C]]](a, b, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SequenceT4[A, B, C, D any](a Lazy[A], b Lazy[B], c Lazy[C], d Lazy[D]) Lazy[T.Tuple4[A, B, C, D]] {
|
||||||
|
return G.SequenceT4[Lazy[A], Lazy[B], Lazy[C], Lazy[D], Lazy[T.Tuple4[A, B, C, D]]](a, b, c, d)
|
||||||
|
}
|
74
lazy/testing/laws.go
Normal file
74
lazy/testing/laws.go
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package testing
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
EQ "github.com/IBM/fp-go/eq"
|
||||||
|
L "github.com/IBM/fp-go/internal/monad/testing"
|
||||||
|
"github.com/IBM/fp-go/lazy"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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,
|
||||||
|
lazy.Eq(eqa),
|
||||||
|
lazy.Eq(eqb),
|
||||||
|
lazy.Eq(eqc),
|
||||||
|
|
||||||
|
lazy.Of[A],
|
||||||
|
lazy.Of[B],
|
||||||
|
lazy.Of[C],
|
||||||
|
|
||||||
|
lazy.Of[func(A) A],
|
||||||
|
lazy.Of[func(A) B],
|
||||||
|
lazy.Of[func(B) C],
|
||||||
|
lazy.Of[func(func(A) B) B],
|
||||||
|
|
||||||
|
lazy.MonadMap[A, A],
|
||||||
|
lazy.MonadMap[A, B],
|
||||||
|
lazy.MonadMap[A, C],
|
||||||
|
lazy.MonadMap[B, C],
|
||||||
|
|
||||||
|
lazy.MonadMap[func(B) C, func(func(A) B) func(A) C],
|
||||||
|
|
||||||
|
lazy.MonadChain[A, A],
|
||||||
|
lazy.MonadChain[A, B],
|
||||||
|
lazy.MonadChain[A, C],
|
||||||
|
lazy.MonadChain[B, C],
|
||||||
|
|
||||||
|
lazy.MonadAp[A, A],
|
||||||
|
lazy.MonadAp[B, A],
|
||||||
|
lazy.MonadAp[C, B],
|
||||||
|
lazy.MonadAp[C, A],
|
||||||
|
|
||||||
|
lazy.MonadAp[B, func(A) B],
|
||||||
|
lazy.MonadAp[func(A) C, func(A) B],
|
||||||
|
|
||||||
|
ab,
|
||||||
|
bc,
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
47
lazy/testing/laws_test.go
Normal file
47
lazy/testing/laws_test.go
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package 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))
|
||||||
|
}
|
50
lazy/traverse.go
Normal file
50
lazy/traverse.go
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package lazy
|
||||||
|
|
||||||
|
import (
|
||||||
|
G "github.com/IBM/fp-go/io/generic"
|
||||||
|
)
|
||||||
|
|
||||||
|
func MonadTraverseArray[A, B any](tas []A, f func(A) Lazy[B]) Lazy[[]B] {
|
||||||
|
return G.MonadTraverseArray[Lazy[B], Lazy[[]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) Lazy[B]) func([]A) Lazy[[]B] {
|
||||||
|
return G.TraverseArray[Lazy[B], Lazy[[]B], []A](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SequenceArray converts an array of [IO] to an [IO] of an array
|
||||||
|
func SequenceArray[A any](tas []Lazy[A]) Lazy[[]A] {
|
||||||
|
return G.SequenceArray[Lazy[A], Lazy[[]A]](tas)
|
||||||
|
}
|
||||||
|
|
||||||
|
func MonadTraverseRecord[K comparable, A, B any](tas map[K]A, f func(A) Lazy[B]) Lazy[map[K]B] {
|
||||||
|
return G.MonadTraverseRecord[Lazy[B], Lazy[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) Lazy[B]) func(map[K]A) Lazy[map[K]B] {
|
||||||
|
return G.TraverseRecord[Lazy[B], Lazy[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]Lazy[A]) Lazy[map[K]A] {
|
||||||
|
return G.SequenceRecord[Lazy[A], Lazy[map[K]A]](tas)
|
||||||
|
}
|
26
number/integer/ord.go
Normal file
26
number/integer/ord.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package integer
|
||||||
|
|
||||||
|
import (
|
||||||
|
O "github.com/IBM/fp-go/ord"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Ord is the strict ordering for integers
|
||||||
|
var Ord = O.FromStrictCompare[int]()
|
||||||
|
|
||||||
|
// Between checks if an integer is between two values
|
||||||
|
var Between = O.Between[int](Ord)
|
@ -170,7 +170,7 @@ func Geq[A any](O Ord[A]) func(A) func(A) bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test whether a value is between a minimum (inclusive) and a maximum (exclusive)
|
// Between tests whether a value is between a minimum (inclusive) and a maximum (exclusive)
|
||||||
func Between[A any](O Ord[A]) func(A, A) func(A) bool {
|
func Between[A any](O Ord[A]) func(A, A) func(A) bool {
|
||||||
lt := Lt(O)
|
lt := Lt(O)
|
||||||
geq := Geq(O)
|
geq := Geq(O)
|
||||||
|
@ -20,21 +20,21 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// MonadApFirst combines two effectful actions, keeping only the result of the first.
|
// MonadApFirst combines two effectful actions, keeping only the result of the first.
|
||||||
func MonadApFirst[R, A, B any](first ReaderIO[R, A], second ReaderIO[R, B]) ReaderIO[R, A] {
|
func MonadApFirst[A, R, B any](first ReaderIO[R, A], second ReaderIO[R, B]) ReaderIO[R, A] {
|
||||||
return G.MonadApFirst[ReaderIO[R, A], ReaderIO[R, B], ReaderIO[R, func(B) A]](first, second)
|
return G.MonadApFirst[ReaderIO[R, A], ReaderIO[R, B], ReaderIO[R, func(B) A]](first, second)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ApFirst combines two effectful actions, keeping only the result of the first.
|
// ApFirst combines two effectful actions, keeping only the result of the first.
|
||||||
func ApFirst[R, A, B any](second ReaderIO[R, B]) func(ReaderIO[R, A]) ReaderIO[R, A] {
|
func ApFirst[A, R, B any](second ReaderIO[R, B]) func(ReaderIO[R, A]) ReaderIO[R, A] {
|
||||||
return G.ApFirst[ReaderIO[R, A], ReaderIO[R, B], ReaderIO[R, func(B) A]](second)
|
return G.ApFirst[ReaderIO[R, A], ReaderIO[R, B], ReaderIO[R, func(B) A]](second)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MonadApSecond combines two effectful actions, keeping only the result of the second.
|
// MonadApSecond combines two effectful actions, keeping only the result of the second.
|
||||||
func MonadApSecond[R, A, B any](first ReaderIO[R, A], second ReaderIO[R, B]) ReaderIO[R, B] {
|
func MonadApSecond[A, R, B any](first ReaderIO[R, A], second ReaderIO[R, B]) ReaderIO[R, B] {
|
||||||
return G.MonadApSecond[ReaderIO[R, A], ReaderIO[R, B], ReaderIO[R, func(B) B]](first, second)
|
return G.MonadApSecond[ReaderIO[R, A], ReaderIO[R, B], ReaderIO[R, func(B) B]](first, second)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ApSecond combines two effectful actions, keeping only the result of the second.
|
// ApSecond combines two effectful actions, keeping only the result of the second.
|
||||||
func ApSecond[R, A, B any](second ReaderIO[R, B]) func(ReaderIO[R, A]) ReaderIO[R, B] {
|
func ApSecond[A, R, B any](second ReaderIO[R, B]) func(ReaderIO[R, A]) ReaderIO[R, B] {
|
||||||
return G.ApSecond[ReaderIO[R, A], ReaderIO[R, B], ReaderIO[R, func(B) B]](second)
|
return G.ApSecond[ReaderIO[R, A], ReaderIO[R, B], ReaderIO[R, func(B) B]](second)
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,6 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// WithResource constructs a function that creates a resource, then operates on it and then releases the resource
|
// WithResource constructs a function that creates a resource, then operates on it and then releases the resource
|
||||||
func WithResource[L, E, R, A any](onCreate ReaderIOEither[L, E, R], onRelease func(R) ReaderIOEither[L, E, any]) func(func(R) ReaderIOEither[L, E, A]) ReaderIOEither[L, E, A] {
|
func WithResource[A, L, E, R any](onCreate ReaderIOEither[L, E, R], onRelease func(R) ReaderIOEither[L, E, any]) func(func(R) ReaderIOEither[L, E, A]) ReaderIOEither[L, E, A] {
|
||||||
return G.WithResource[ReaderIOEither[L, E, A]](onCreate, onRelease)
|
return G.WithResource[ReaderIOEither[L, E, A]](onCreate, onRelease)
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user