1
0
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:
Carsten Leue
2023-07-24 16:44:12 +02:00
committed by GitHub
51 changed files with 2076 additions and 25 deletions

View File

@ -144,7 +144,7 @@ func Append[A any](as []A, a A) []A {
}
func IsEmpty[A any](as []A) bool {
return array.IsEmpty(as)
return G.IsEmpty(as)
}
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 {
return func(as []A) B {
if IsEmpty(as) {
return onEmpty()
}
return onNonEmpty(as)
}
return G.Match[[]A](onEmpty, onNonEmpty)
}
func MatchLeft[A, B any](onEmpty func() B, onNonEmpty func(A, []A) B) func([]A) B {
return G.MatchLeft[[]A](onEmpty, onNonEmpty)
}
func Tail[A any](as []A) O.Option[[]A] {

View File

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

View File

@ -18,3 +18,7 @@ package bytes
func ToString(a []byte) string {
return string(a)
}
func Size(as []byte) int {
return len(as)
}

View File

@ -19,7 +19,7 @@ import (
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] {
return G.WithContext(ma)
}

View File

@ -0,0 +1 @@
Carsten

View File

@ -23,7 +23,7 @@ import (
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]] {
return G.Eq[ReaderIOEither[A]](eq)
}

View File

@ -48,7 +48,7 @@ func Close[C io.Closer](c C) RIOE.ReaderIOEither[any] {
// ReadFile reads a file in the scope of a context
func ReadFile(path string) RIOE.ReaderIOEither[[]byte] {
return RIOE.WithResource[*os.File, []byte](Open(path), Close[*os.File])(func(r *os.File) RIOE.ReaderIOEither[[]byte] {
return 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 IOE.MakeIO(func() ET.Either[error, []byte] {
return file.ReadAll(ctx, r)

View File

@ -20,7 +20,7 @@ import (
)
// 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
return G.WithResource[ReaderIOEither[A]](onCreate, onRelease)
}

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

View File

@ -21,5 +21,6 @@ import (
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]

51
erasure/erasure.go Normal file
View 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
View 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)
}

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

View File

@ -22,6 +22,14 @@ import (
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 {
return left + right
}

View File

@ -16,6 +16,7 @@
package generic
import (
"fmt"
"log"
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)
})
}
}

View File

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

View File

@ -20,21 +20,21 @@ import (
)
// 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)
}
// 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)
}
// 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)
}
// 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)
}

View File

@ -22,7 +22,7 @@ import (
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
type IOEither[E, A any] I.IO[ET.Either[E, A]]

View File

@ -119,7 +119,7 @@ func TestApFirst(t *testing.T) {
x := F.Pipe1(
Of[error]("a"),
ApFirst[error, string](Of[error]("b")),
ApFirst[string](Of[error]("b")),
)
assert.Equal(t, E.Of[error]("a"), x())
@ -129,7 +129,7 @@ func TestApSecond(t *testing.T) {
x := F.Pipe1(
Of[error]("a"),
ApSecond[error, string](Of[error]("b")),
ApSecond[string](Of[error]("b")),
)
assert.Equal(t, E.Of[error]("b"), x())

30
iooption/array.go Normal file
View 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)
}

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

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

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

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

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

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

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

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

View File

@ -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 {
lt := Lt(O)
geq := Geq(O)

View File

@ -20,21 +20,21 @@ import (
)
// 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)
}
// 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)
}
// 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)
}
// 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)
}

View File

@ -20,6 +20,6 @@ import (
)
// 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)
}