diff --git a/v2/io/applicative.go b/v2/io/applicative.go index f6030ae..7f6d342 100644 --- a/v2/io/applicative.go +++ b/v2/io/applicative.go @@ -28,11 +28,11 @@ func (o *ioApplicative[A, B]) Of(a A) IO[A] { return Of(a) } -func (o *ioApplicative[A, B]) Map(f func(A) B) func(IO[A]) IO[B] { +func (o *ioApplicative[A, B]) Map(f func(A) B) Mapper[A, B] { return Map(f) } -func (o *ioApplicative[A, B]) Ap(fa IO[A]) func(IO[func(A) B]) IO[B] { +func (o *ioApplicative[A, B]) Ap(fa IO[A]) Mapper[func(A) B, B] { return Ap[B](fa) } diff --git a/v2/io/apply.go b/v2/io/apply.go index e7c9909..ff92e86 100644 --- a/v2/io/apply.go +++ b/v2/io/apply.go @@ -20,10 +20,10 @@ import ( S "github.com/IBM/fp-go/v2/semigroup" ) -func ApplySemigroup[A any](s S.Semigroup[A]) S.Semigroup[IO[A]] { +func ApplySemigroup[A any](s S.Semigroup[A]) Semigroup[A] { return S.ApplySemigroup(MonadMap[A, func(A) A], MonadAp[A, A], s) } -func ApplicativeMonoid[A any](m M.Monoid[A]) M.Monoid[IO[A]] { +func ApplicativeMonoid[A any](m M.Monoid[A]) Monoid[A] { return M.ApplicativeMonoid(Of[A], MonadMap[A, func(A) A], MonadAp[A, A], m) } diff --git a/v2/io/bind.go b/v2/io/bind.go index 5364499..559ff02 100644 --- a/v2/io/bind.go +++ b/v2/io/bind.go @@ -16,51 +16,74 @@ package io import ( - G "github.com/IBM/fp-go/v2/io/generic" + INTA "github.com/IBM/fp-go/v2/internal/apply" + INTC "github.com/IBM/fp-go/v2/internal/chain" + INTF "github.com/IBM/fp-go/v2/internal/functor" ) // Bind creates an empty context of type [S] to be used with the [Bind] operation func Do[S any]( empty S, ) IO[S] { - return G.Do[IO[S], S](empty) + return Of(empty) } // Bind attaches the result of a computation to a context [S1] to produce a context [S2] func Bind[S1, S2, T any]( setter func(T) func(S1) S2, f func(S1) IO[T], -) func(IO[S1]) IO[S2] { - return G.Bind[IO[S1], IO[S2], IO[T], S1, S2, T](setter, f) +) Mapper[S1, S2] { + return INTC.Bind( + Chain[S1, S2], + Map[T, S2], + setter, + f, + ) } // Let attaches the result of a computation to a context [S1] to produce a context [S2] func Let[S1, S2, T any]( setter func(T) func(S1) S2, f func(S1) T, -) func(IO[S1]) IO[S2] { - return G.Let[IO[S1], IO[S2], S1, S2, T](setter, f) +) Mapper[S1, S2] { + return INTF.Let( + Map[S1, S2], + setter, + f, + ) } // LetTo attaches the a value to a context [S1] to produce a context [S2] func LetTo[S1, S2, T any]( setter func(T) func(S1) S2, b T, -) func(IO[S1]) IO[S2] { - return G.LetTo[IO[S1], IO[S2], S1, S2, T](setter, b) +) Mapper[S1, S2] { + return INTF.LetTo( + Map[S1, S2], + setter, + b, + ) } // BindTo initializes a new state [S1] from a value [T] func BindTo[S1, T any]( setter func(T) S1, -) func(IO[T]) IO[S1] { - return G.BindTo[IO[S1], IO[T], S1, T](setter) +) Mapper[T, S1] { + return INTC.BindTo( + Map[T, S1], + setter, + ) } // ApS attaches a value to a context [S1] to produce a context [S2] by considering the context and the value concurrently func ApS[S1, S2, T any]( setter func(T) func(S1) S2, fa IO[T], -) func(IO[S1]) IO[S2] { - return G.ApS[IO[S1], IO[S2], IO[T], S1, S2, T](setter, fa) +) Mapper[S1, S2] { + return INTA.ApS( + Ap[S2, T], + Map[S1, func(T) S2], + setter, + fa, + ) } diff --git a/v2/io/functor.go b/v2/io/functor.go index 79714a7..b558e7c 100644 --- a/v2/io/functor.go +++ b/v2/io/functor.go @@ -25,7 +25,7 @@ type ( IOFunctor[A, B any] = functor.Functor[A, B, IO[A], IO[B]] ) -func (o *ioFunctor[A, B]) Map(f func(A) B) func(IO[A]) IO[B] { +func (o *ioFunctor[A, B]) Map(f func(A) B) Mapper[A, B] { return Map(f) } diff --git a/v2/io/generic/ap.go b/v2/io/generic/ap.go index bcf0a70..3b7e2b0 100644 --- a/v2/io/generic/ap.go +++ b/v2/io/generic/ap.go @@ -25,13 +25,15 @@ const ( useParallel = true ) -// MonadApSeq implements the applicative on a single thread by first executing mab and the ma +//go:deprecate MonadApSeq implements the applicative on a single thread by first executing mab and the ma func MonadApSeq[GA ~func() A, GB ~func() B, GAB ~func() func(A) B, A, B any](mab GAB, ma GA) GB { return MonadChain(mab, F.Bind1st(MonadMap[GA, GB], ma)) } // MonadApPar implements the applicative on two threads, the main thread executes mab and the actuall // apply operation and the second thread computes ma. Communication between the threads happens via a channel +// +//go:deprecate func MonadApPar[GA ~func() A, GB ~func() B, GAB ~func() func(A) B, A, B any](mab GAB, ma GA) GB { return MakeIO[GB](func() B { c := make(chan A) @@ -45,6 +47,8 @@ func MonadApPar[GA ~func() A, GB ~func() B, GAB ~func() func(A) B, A, B any](mab // MonadAp implements the `ap` operation. Depending on a feature flag this will be sequential or parallel, the preferred implementation // is parallel +// +//go:deprecate func MonadAp[GA ~func() A, GB ~func() B, GAB ~func() func(A) B, A, B any](mab GAB, ma GA) GB { if useParallel { return MonadApPar[GA, GB](mab, ma) @@ -53,6 +57,8 @@ func MonadAp[GA ~func() A, GB ~func() B, GAB ~func() func(A) B, A, B any](mab GA } // MonadApFirst combines two effectful actions, keeping only the result of the first. +// +//go:deprecate func MonadApFirst[GA ~func() A, GB ~func() B, GBA ~func() func(B) A, A, B any](first GA, second GB) GA { return G.MonadApFirst( MonadAp[GB, GA, GBA, B, A], @@ -64,6 +70,8 @@ func MonadApFirst[GA ~func() A, GB ~func() B, GBA ~func() func(B) A, A, B any](f } // MonadApFirstPar combines two effectful actions, keeping only the result of the first. +// +//go:deprecate func MonadApFirstPar[GA ~func() A, GB ~func() B, GBA ~func() func(B) A, A, B any](first GA, second GB) GA { return G.MonadApFirst( MonadApPar[GB, GA, GBA, B, A], @@ -75,6 +83,8 @@ func MonadApFirstPar[GA ~func() A, GB ~func() B, GBA ~func() func(B) A, A, B any } // MonadApFirstSeq combines two effectful actions, keeping only the result of the first. +// +//go:deprecate func MonadApFirstSeq[GA ~func() A, GB ~func() B, GBA ~func() func(B) A, A, B any](first GA, second GB) GA { return G.MonadApFirst( MonadApSeq[GB, GA, GBA, B, A], @@ -86,6 +96,8 @@ func MonadApFirstSeq[GA ~func() A, GB ~func() B, GBA ~func() func(B) A, A, B any } // ApFirst combines two effectful actions, keeping only the result of the first. +// +//go:deprecate func ApFirst[GA ~func() A, GB ~func() B, GBA ~func() func(B) A, A, B any](second GB) func(GA) GA { return G.ApFirst( MonadAp[GB, GA, GBA, B, A], @@ -96,6 +108,8 @@ func ApFirst[GA ~func() A, GB ~func() B, GBA ~func() func(B) A, A, B any](second } // ApFirstPar combines two effectful actions, keeping only the result of the first. +// +//go:deprecate func ApFirstPar[GA ~func() A, GB ~func() B, GBA ~func() func(B) A, A, B any](second GB) func(GA) GA { return G.ApFirst( MonadApPar[GB, GA, GBA, B, A], @@ -106,6 +120,8 @@ func ApFirstPar[GA ~func() A, GB ~func() B, GBA ~func() func(B) A, A, B any](sec } // ApFirstSeq combines two effectful actions, keeping only the result of the first. +// +//go:deprecate func ApFirstSeq[GA ~func() A, GB ~func() B, GBA ~func() func(B) A, A, B any](second GB) func(GA) GA { return G.ApFirst( MonadApSeq[GB, GA, GBA, B, A], @@ -116,6 +132,8 @@ func ApFirstSeq[GA ~func() A, GB ~func() B, GBA ~func() func(B) A, A, B any](sec } // MonadApSecond combines two effectful actions, keeping only the result of the second. +// +//go:deprecate func MonadApSecond[GA ~func() A, GB ~func() B, GBB ~func() func(B) B, A, B any](first GA, second GB) GB { return G.MonadApSecond( MonadAp[GB, GB, GBB, B, B], @@ -127,6 +145,8 @@ func MonadApSecond[GA ~func() A, GB ~func() B, GBB ~func() func(B) B, A, B any]( } // ApSecond combines two effectful actions, keeping only the result of the second. +// +//go:deprecate func ApSecond[GA ~func() A, GB ~func() B, GBB ~func() func(B) B, A, B any](second GB) func(GA) GB { return G.ApSecond( MonadAp[GB, GB, GBB, B, B], diff --git a/v2/io/generic/bind.go b/v2/io/generic/bind.go index 9185436..294ed55 100644 --- a/v2/io/generic/bind.go +++ b/v2/io/generic/bind.go @@ -22,6 +22,8 @@ import ( ) // Bind creates an empty context of type [S] to be used with the [Bind] operation +// +//go:deprecate func Do[GS ~func() S, S any]( empty S, ) GS { @@ -29,6 +31,8 @@ func Do[GS ~func() S, S any]( } // Bind attaches the result of a computation to a context [S1] to produce a context [S2] +// +//go:deprecate func Bind[GS1 ~func() S1, GS2 ~func() S2, GT ~func() T, S1, S2, T any]( setter func(T) func(S1) S2, f func(S1) GT, @@ -42,6 +46,8 @@ func Bind[GS1 ~func() S1, GS2 ~func() S2, GT ~func() T, S1, S2, T any]( } // Let attaches the result of a computation to a context [S1] to produce a context [S2] +// +//go:deprecate func Let[GS1 ~func() S1, GS2 ~func() S2, S1, S2, T any]( key func(T) func(S1) S2, f func(S1) T, @@ -54,6 +60,8 @@ func Let[GS1 ~func() S1, GS2 ~func() S2, S1, S2, T any]( } // LetTo attaches the a value to a context [S1] to produce a context [S2] +// +//go:deprecate func LetTo[GS1 ~func() S1, GS2 ~func() S2, S1, S2, B any]( key func(B) func(S1) S2, b B, @@ -66,6 +74,8 @@ func LetTo[GS1 ~func() S1, GS2 ~func() S2, S1, S2, B any]( } // BindTo initializes a new state [S1] from a value [T] +// +//go:deprecate func BindTo[GS1 ~func() S1, GT ~func() T, S1, T any]( setter func(T) S1, ) func(GT) GS1 { @@ -76,6 +86,8 @@ func BindTo[GS1 ~func() S1, GT ~func() T, S1, T any]( } // ApS attaches a value to a context [S1] to produce a context [S2] by considering the context and the value concurrently +// +//go:deprecate func ApS[GS1 ~func() S1, GS2 ~func() S2, GT ~func() T, S1, S2, T any]( setter func(T) func(S1) S2, fa GT, diff --git a/v2/io/generic/bracket.go b/v2/io/generic/bracket.go index 7015a57..14427da 100644 --- a/v2/io/generic/bracket.go +++ b/v2/io/generic/bracket.go @@ -21,6 +21,8 @@ import ( // Bracket makes sure that a resource is cleaned up in the event of an error. The release action is called regardless of // whether the body action returns and error or not. +// +//go:deprecate func Bracket[ GA ~func() A, GB ~func() B, diff --git a/v2/io/generic/logging.go b/v2/io/generic/logging.go deleted file mode 100644 index 4747a1d..0000000 --- a/v2/io/generic/logging.go +++ /dev/null @@ -1,50 +0,0 @@ -// 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 ( - "fmt" - "log" - - Logging "github.com/IBM/fp-go/v2/logging" -) - -func Logger[GA ~func() any, A any](loggers ...*log.Logger) func(string) func(A) GA { - _, right := Logging.LoggingCallbacks(loggers...) - return func(prefix string) func(A) GA { - return func(a A) GA { - return FromImpure[GA](func() { - right("%s: %v", prefix, a) - }) - } - } -} - -func Logf[GA ~func() any, A any](prefix string) func(A) GA { - return func(a A) GA { - return FromImpure[GA](func() { - log.Printf(prefix, a) - }) - } -} - -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) - }) - } -} diff --git a/v2/io/generic/resource.go b/v2/io/generic/resource.go index dab2307..2294f0c 100644 --- a/v2/io/generic/resource.go +++ b/v2/io/generic/resource.go @@ -20,6 +20,8 @@ import ( ) // WithResource constructs a function that creates a resource, then operates on it and then releases the resource +// +//go:deprecate func WithResource[ GA ~func() A, GR ~func() R, diff --git a/v2/io/generic/retry.go b/v2/io/generic/retry.go index d46bf37..7593234 100644 --- a/v2/io/generic/retry.go +++ b/v2/io/generic/retry.go @@ -29,6 +29,8 @@ type retryStatusIO = func() R.RetryStatus // 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 +//go:deprecate + func Retrying[GA ~func() A, A any]( policy R.RetryPolicy, action func(R.RetryStatus) GA, diff --git a/v2/io/generic/sync.go b/v2/io/generic/sync.go index f9811b2..049da9f 100644 --- a/v2/io/generic/sync.go +++ b/v2/io/generic/sync.go @@ -20,6 +20,8 @@ import ( ) // WithLock executes the provided IO operation in the scope of a lock +// +//go:deprecate func WithLock[GA ~func() A, A any](lock func() context.CancelFunc) func(fa GA) GA { return func(fa GA) GA { return func() A { diff --git a/v2/io/io.go b/v2/io/io.go index 49c0833..869250f 100644 --- a/v2/io/io.go +++ b/v2/io/io.go @@ -23,6 +23,9 @@ import ( INTC "github.com/IBM/fp-go/v2/internal/chain" INTF "github.com/IBM/fp-go/v2/internal/functor" INTL "github.com/IBM/fp-go/v2/internal/lazy" + M "github.com/IBM/fp-go/v2/monoid" + R "github.com/IBM/fp-go/v2/reader" + S "github.com/IBM/fp-go/v2/semigroup" T "github.com/IBM/fp-go/v2/tuple" ) @@ -36,9 +39,15 @@ var ( undefined = struct{}{} ) -// IO represents a synchronous computation that cannot fail -// refer to [https://andywhite.xyz/posts/2021-01-27-rte-foundations/#ioltagt] for more details -type IO[A any] = func() A +type ( + // IO represents a synchronous computation that cannot fail + // refer to [https://andywhite.xyz/posts/2021-01-27-rte-foundations/#ioltagt] for more details + IO[A any] = func() A + + Mapper[A, B any] = R.Reader[IO[A], IO[B]] + Monoid[A any] = M.Monoid[IO[A]] + Semigroup[A any] = S.Semigroup[IO[A]] +) func Of[A any](a A) IO[A] { return F.Constant(a) @@ -66,7 +75,7 @@ func MonadMap[A, B any](fa IO[A], f func(A) B) IO[B] { } } -func Map[A, B any](f func(A) B) func(fa IO[A]) IO[B] { +func Map[A, B any](f func(A) B) Mapper[A, B] { return F.Bind2nd(MonadMap[A, B], f) } @@ -74,7 +83,7 @@ func MonadMapTo[A, B any](fa IO[A], b B) IO[B] { return MonadMap(fa, F.Constant1[A](b)) } -func MapTo[A, B any](b B) func(IO[A]) IO[B] { +func MapTo[A, B any](b B) Mapper[A, B] { return Map(F.Constant1[A](b)) } @@ -86,7 +95,7 @@ func MonadChain[A, B any](fa IO[A], f func(A) IO[B]) IO[B] { } // Chain composes computations in sequence, using the return value of one computation to determine the next computation. -func Chain[A, B any](f func(A) IO[B]) func(IO[A]) IO[B] { +func Chain[A, B any](f func(A) IO[B]) Mapper[A, B] { return F.Bind2nd(MonadChain[A, B], f) } @@ -117,11 +126,11 @@ func MonadAp[A, B any](mab IO[func(A) B], ma IO[A]) IO[B] { return MonadApSeq(mab, ma) } -func Ap[B, A any](ma IO[A]) func(IO[func(A) B]) IO[B] { +func Ap[B, A any](ma IO[A]) Mapper[func(A) B, B] { return F.Bind2nd(MonadAp[A, B], ma) } -func ApSeq[B, A any](ma IO[A]) func(IO[func(A) B]) IO[B] { +func ApSeq[B, A any](ma IO[A]) Mapper[func(A) B, B] { return Chain(F.Bind1st(MonadMap[A, B], ma)) } @@ -142,7 +151,7 @@ func MonadChainFirst[A, B any](fa IO[A], f func(A) IO[B]) IO[A] { // ChainFirst composes computations in sequence, using the return value of one computation to determine the next computation and // keeping only the result of the first. -func ChainFirst[A, B any](f func(A) IO[B]) func(IO[A]) IO[A] { +func ChainFirst[A, B any](f func(A) IO[B]) Mapper[A, A] { return INTC.ChainFirst( Chain[A, A], Map[B, A], @@ -162,7 +171,7 @@ func MonadApFirst[A, B any](first IO[A], second IO[B]) IO[A] { } // ApFirst combines two effectful actions, keeping only the result of the first. -func ApFirst[A, B any](second IO[B]) func(IO[A]) IO[A] { +func ApFirst[A, B any](second IO[B]) Mapper[A, A] { return INTA.ApFirst( MonadAp[B, A], MonadMap[A, func(B) A], @@ -183,7 +192,7 @@ func MonadApSecond[A, B any](first IO[A], second IO[B]) IO[B] { } // ApSecond combines two effectful actions, keeping only the result of the second. -func ApSecond[A, B any](second IO[B]) func(IO[A]) IO[B] { +func ApSecond[A, B any](second IO[B]) Mapper[A, B] { return INTA.ApSecond( MonadAp[B, B], MonadMap[A, func(B) B], @@ -198,7 +207,7 @@ func MonadChainTo[A, B any](fa IO[A], fb IO[B]) IO[B] { } // ChainTo composes computations in sequence, ignoring the return value of the first computation -func ChainTo[A, B any](fb IO[B]) func(IO[A]) IO[B] { +func ChainTo[A, B any](fb IO[B]) Mapper[A, B] { return Chain(F.Constant1[A](fb)) } @@ -216,12 +225,12 @@ func MonadFlap[B, A any](fab IO[func(A) B], a A) IO[B] { return INTF.MonadFlap(MonadMap[func(A) B, B], fab, a) } -func Flap[B, A any](a A) func(IO[func(A) B]) IO[B] { +func Flap[B, A any](a A) Mapper[func(A) B, B] { return INTF.Flap(Map[func(A) B, B], a) } // Delay creates an operation that passes in the value after some delay -func Delay[A any](delay time.Duration) func(IO[A]) IO[A] { +func Delay[A any](delay time.Duration) Mapper[A, A] { return func(ga IO[A]) IO[A] { return func() A { time.Sleep(delay) @@ -241,7 +250,7 @@ func after(timestamp time.Time) func() { } // After creates an operation that passes after the given timestamp -func After[A any](timestamp time.Time) func(IO[A]) IO[A] { +func After[A any](timestamp time.Time) Mapper[A, A] { aft := after(timestamp) return func(ga IO[A]) IO[A] { return func() A { diff --git a/v2/io/logging.go b/v2/io/logging.go index f570f62..f3c7928 100644 --- a/v2/io/logging.go +++ b/v2/io/logging.go @@ -16,24 +16,40 @@ package io import ( + "fmt" "log" - G "github.com/IBM/fp-go/v2/io/generic" + L "github.com/IBM/fp-go/v2/logging" ) // Logger constructs a logger function that can be used with ChainXXXIOK func Logger[A any](loggers ...*log.Logger) func(string) func(A) IO[any] { - return G.Logger[IO[any], A](loggers...) + _, right := L.LoggingCallbacks(loggers...) + return func(prefix string) func(A) IO[any] { + return func(a A) IO[any] { + return FromImpure(func() { + right("%s: %v", prefix, a) + }) + } + } } // Logf constructs a logger function that can be used with ChainXXXIOK // the string prefix contains the format string for the log value func Logf[A any](prefix string) func(A) IO[any] { - return G.Logf[IO[any], A](prefix) + return func(a A) IO[any] { + return FromImpure(func() { + log.Printf(prefix, a) + }) + } } // 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) + return func(a A) IO[any] { + return FromImpure(func() { + fmt.Printf(prefix, a) + }) + } } diff --git a/v2/io/monad.go b/v2/io/monad.go index a5986f6..320177f 100644 --- a/v2/io/monad.go +++ b/v2/io/monad.go @@ -28,15 +28,15 @@ func (o *ioMonad[A, B]) Of(a A) IO[A] { return Of(a) } -func (o *ioMonad[A, B]) Map(f func(A) B) func(IO[A]) IO[B] { +func (o *ioMonad[A, B]) Map(f func(A) B) Mapper[A, B] { return Map(f) } -func (o *ioMonad[A, B]) Chain(f func(A) IO[B]) func(IO[A]) IO[B] { +func (o *ioMonad[A, B]) Chain(f func(A) IO[B]) Mapper[A, B] { return Chain(f) } -func (o *ioMonad[A, B]) Ap(fa IO[A]) func(IO[func(A) B]) IO[B] { +func (o *ioMonad[A, B]) Ap(fa IO[A]) Mapper[func(A) B, B] { return Ap[B](fa) } diff --git a/v2/io/resource.go b/v2/io/resource.go index 98ce80a..5f3b581 100644 --- a/v2/io/resource.go +++ b/v2/io/resource.go @@ -16,12 +16,12 @@ package io import ( - G "github.com/IBM/fp-go/v2/io/generic" + F "github.com/IBM/fp-go/v2/function" ) // WithResource constructs a function that creates a resource, then operates on it and then releases the resource func WithResource[ R, A, ANY any](onCreate IO[R], onRelease func(R) IO[ANY]) func(func(R) IO[A]) IO[A] { - // just dispatch - return G.WithResource[IO[A], IO[R], IO[ANY]](onCreate, onRelease) + // simply map to implementation of bracket + return F.Bind13of3(Bracket[R, A, ANY])(onCreate, F.Ignore2of2[A](onRelease)) } diff --git a/v2/io/retry.go b/v2/io/retry.go index 4e31cb5..bf7fab9 100644 --- a/v2/io/retry.go +++ b/v2/io/retry.go @@ -16,8 +16,12 @@ package io import ( - G "github.com/IBM/fp-go/v2/io/generic" R "github.com/IBM/fp-go/v2/retry" + RG "github.com/IBM/fp-go/v2/retry/generic" +) + +type ( + RetryStatus = IO[R.RetryStatus] ) // Retrying will retry the actions according to the check policy @@ -30,5 +34,16 @@ func Retrying[A any]( action func(R.RetryStatus) IO[A], check func(A) bool, ) IO[A] { - return G.Retrying(policy, action, check) + // get an implementation for the types + return RG.Retrying( + Chain[A, A], + Chain[R.RetryStatus, A], + Of[A], + Of[R.RetryStatus], + Delay[R.RetryStatus], + + policy, + action, + check, + ) } diff --git a/v2/io/traverse_test.go b/v2/io/traverse_test.go new file mode 100644 index 0000000..d7e06b6 --- /dev/null +++ b/v2/io/traverse_test.go @@ -0,0 +1,38 @@ +package io + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/assert" +) + +func toUpper(s string) IO[string] { + return Of(strings.ToUpper(s)) +} + +func TestTraverseArray(t *testing.T) { + + src := []string{"a", "b"} + + trv := TraverseArray(toUpper) + + res := trv(src) + + assert.Equal(t, res(), []string{"A", "B"}) +} + +type ( + customSlice []string +) + +func TestTraverseCustomSlice(t *testing.T) { + + src := customSlice{"a", "b"} + + trv := TraverseArray(toUpper) + + res := trv(src) + + assert.Equal(t, res(), []string{"A", "B"}) +} diff --git a/v2/reader/generic/reader.go b/v2/reader/generic/reader.go index a634277..eb518ac 100644 --- a/v2/reader/generic/reader.go +++ b/v2/reader/generic/reader.go @@ -25,36 +25,47 @@ import ( // Reader[R, A] = func(R) A // MakeReader creates a reader, i.e. a method that accepts a context and that returns a value +// +//go:deprecate func MakeReader[GA ~func(R) A, R, A any](r GA) GA { return r } // Ask reads the current context +// +//go:deprecate func Ask[GR ~func(R) R, R any]() GR { return MakeReader(F.Identity[R]) } // Asks projects a value from the global context in a Reader +// +//go:deprecate func Asks[GA ~func(R) A, R, A any](f GA) GA { return MakeReader(f) } +//go:deprecate func AsksReader[GA ~func(R) A, R, A any](f func(R) GA) GA { return MakeReader(func(r R) A { return f(r)(r) }) } +//go:deprecate func MonadMap[GA ~func(E) A, GB ~func(E) B, E, A, B any](fa GA, f func(A) B) GB { return MakeReader(F.Flow2(fa, f)) } // Map can be used to turn functions `func(A)B` into functions `(fa F[A])F[B]` whose argument and return types // use the type constructor `F` to represent some computational context. +// +//go:deprecate func Map[GA ~func(E) A, GB ~func(E) B, E, A, B any](f func(A) B) func(GA) GB { return F.Bind2nd(MonadMap[GA, GB, E, A, B], f) } +//go:deprecate func MonadAp[GA ~func(R) A, GB ~func(R) B, GAB ~func(R) func(A) B, R, A, B any](fab GAB, fa GA) GB { return MakeReader(func(r R) B { return fab(r)(fa(r)) @@ -62,14 +73,18 @@ func MonadAp[GA ~func(R) A, GB ~func(R) B, GAB ~func(R) func(A) B, R, A, B any]( } // Ap applies a function to an argument under a type constructor. +// +//go:deprecate func Ap[GA ~func(R) A, GB ~func(R) B, GAB ~func(R) func(A) B, R, A, B any](fa GA) func(GAB) GB { return F.Bind2nd(MonadAp[GA, GB, GAB, R, A, B], fa) } +//go:deprecate func Of[GA ~func(R) A, R, A any](a A) GA { return F.Constant1[R](a) } +//go:deprecate func MonadChain[GA ~func(R) A, GB ~func(R) B, R, A, B any](ma GA, f func(A) GB) GB { return MakeReader(func(r R) B { return f(ma(r))(r) @@ -77,32 +92,39 @@ func MonadChain[GA ~func(R) A, GB ~func(R) B, R, A, B any](ma GA, f func(A) GB) } // Chain composes computations in sequence, using the return value of one computation to determine the next computation. +// +//go:deprecate func Chain[GA ~func(R) A, GB ~func(R) B, R, A, B any](f func(A) GB) func(GA) GB { return F.Bind2nd(MonadChain[GA, GB, R, A, B], f) } +//go:deprecate func Flatten[GA ~func(R) A, GGA ~func(R) GA, R, A any](mma GGA) GA { return MonadChain(mma, F.Identity[GA]) } +//go:deprecate func Compose[AB ~func(A) B, BC ~func(B) C, AC ~func(A) C, A, B, C any](ab AB) func(BC) AC { return func(bc BC) AC { return F.Flow2(ab, bc) } } +//go:deprecate func First[GAB ~func(A) B, GABC ~func(T.Tuple2[A, C]) T.Tuple2[B, C], A, B, C any](pab GAB) GABC { return MakeReader(func(tac T.Tuple2[A, C]) T.Tuple2[B, C] { return T.MakeTuple2(pab(tac.F1), tac.F2) }) } +//go:deprecate func Second[GBC ~func(B) C, GABC ~func(T.Tuple2[A, B]) T.Tuple2[A, C], A, B, C any](pbc GBC) GABC { return MakeReader(func(tab T.Tuple2[A, B]) T.Tuple2[A, C] { return T.MakeTuple2(tab.F1, pbc(tab.F2)) }) } +//go:deprecate func Promap[GA ~func(E) A, GB ~func(D) B, E, A, D, B any](f func(D) E, g func(A) B) func(GA) GB { return func(fea GA) GB { return MakeReader(F.Flow3(f, fea, g)) @@ -111,6 +133,8 @@ func Promap[GA ~func(E) A, GB ~func(D) B, E, A, D, B any](f func(D) E, g func(A) // Local changes the value of the local context during the execution of the action `ma` (similar to `Contravariant`'s // `contramap`). +// +//go:deprecate func Local[GA1 ~func(R1) A, GA2 ~func(R2) A, R2, R1, A any](f func(R2) R1) func(GA1) GA2 { return func(r1 GA1) GA2 { return F.Flow2(f, r1) @@ -118,14 +142,18 @@ func Local[GA1 ~func(R1) A, GA2 ~func(R2) A, R2, R1, A any](f func(R2) R1) func( } // Read applies a context to a reader to obtain its value +// +//go:deprecate func Read[GA ~func(E) A, E, A any](e E) func(GA) A { return I.Ap[GA](e) } +//go:deprecate func MonadFlap[GAB ~func(R) func(A) B, GB ~func(R) B, R, A, B any](fab GAB, a A) GB { return FC.MonadFlap(MonadMap[GAB, GB], fab, a) } +//go:deprecate func Flap[GAB ~func(R) func(A) B, GB ~func(R) B, R, A, B any](a A) func(GAB) GB { return FC.Flap(Map[GAB, GB], a) } diff --git a/v2/reader/reader.go b/v2/reader/reader.go index faeff73..06b52ee 100644 --- a/v2/reader/reader.go +++ b/v2/reader/reader.go @@ -16,100 +16,118 @@ package reader import ( - G "github.com/IBM/fp-go/v2/reader/generic" + F "github.com/IBM/fp-go/v2/function" + I "github.com/IBM/fp-go/v2/identity" + INTF "github.com/IBM/fp-go/v2/internal/functor" T "github.com/IBM/fp-go/v2/tuple" ) // The purpose of the `Reader` monad is to avoid threading arguments through multiple functions in order to only get them where they are needed. // The first template argument `R` is the the context to read from, the second argument `A` is the return value of the monad -type Reader[R, A any] func(R) A +type Reader[R, A any] = func(R) A // MakeReader creates a reader, i.e. a method that accepts a context and that returns a value +// +//go:deprecate func MakeReader[R, A any](r Reader[R, A]) Reader[R, A] { - return G.MakeReader(r) + return r } // Ask reads the current context func Ask[R any]() Reader[R, R] { - return G.Ask[Reader[R, R]]() + return F.Identity[R] } // Asks projects a value from the global context in a Reader func Asks[R, A any](f Reader[R, A]) Reader[R, A] { - return G.Asks(f) + return f } func AsksReader[R, A any](f func(R) Reader[R, A]) Reader[R, A] { - return G.AsksReader(f) + return func(r R) A { + return f(r)(r) + } } func MonadMap[E, A, B any](fa Reader[E, A], f func(A) B) Reader[E, B] { - return G.MonadMap[Reader[E, A], Reader[E, B]](fa, f) + return F.Flow2(fa, f) } // Map can be used to turn functions `func(A)B` into functions `(fa F[A])F[B]` whose argument and return types // use the type constructor `F` to represent some computational context. func Map[E, A, B any](f func(A) B) func(Reader[E, A]) Reader[E, B] { - return G.Map[Reader[E, A], Reader[E, B]](f) + return F.Bind2nd(MonadMap[E, A, B], f) } func MonadAp[B, R, A any](fab Reader[R, func(A) B], fa Reader[R, A]) Reader[R, B] { - return G.MonadAp[Reader[R, A], Reader[R, B]](fab, fa) + return func(r R) B { + return fab(r)(fa(r)) + } } // Ap applies a function to an argument under a type constructor. func Ap[B, R, A any](fa Reader[R, A]) func(Reader[R, func(A) B]) Reader[R, B] { - return G.Ap[Reader[R, A], Reader[R, B], Reader[R, func(A) B]](fa) + return F.Bind2nd(MonadAp[B, R, A], fa) } func Of[R, A any](a A) Reader[R, A] { - return G.Of[Reader[R, A]](a) + return F.Constant1[R](a) } func MonadChain[R, A, B any](ma Reader[R, A], f func(A) Reader[R, B]) Reader[R, B] { - return G.MonadChain(ma, f) + return func(r R) B { + return f(ma(r))(r) + } } // Chain composes computations in sequence, using the return value of one computation to determine the next computation. func Chain[R, A, B any](f func(A) Reader[R, B]) func(Reader[R, A]) Reader[R, B] { - return G.Chain[Reader[R, A]](f) + return F.Bind2nd(MonadChain[R, A, B], f) } func Flatten[R, A any](mma func(R) Reader[R, A]) Reader[R, A] { - return G.Flatten(mma) + return MonadChain(mma, F.Identity[Reader[R, A]]) } func Compose[R, B, C any](ab Reader[R, B]) func(Reader[B, C]) Reader[R, C] { - return G.Compose[Reader[R, B], Reader[B, C], Reader[R, C]](ab) + return func(bc Reader[B, C]) Reader[R, C] { + return F.Flow2(ab, bc) + } } func First[A, B, C any](pab Reader[A, B]) Reader[T.Tuple2[A, C], T.Tuple2[B, C]] { - return G.First[Reader[A, B], Reader[T.Tuple2[A, C], T.Tuple2[B, C]]](pab) + return func(tac T.Tuple2[A, C]) T.Tuple2[B, C] { + return T.MakeTuple2(pab(tac.F1), tac.F2) + } } func Second[A, B, C any](pbc Reader[B, C]) Reader[T.Tuple2[A, B], T.Tuple2[A, C]] { - return G.Second[Reader[B, C], Reader[T.Tuple2[A, B], T.Tuple2[A, C]]](pbc) + return func(tab T.Tuple2[A, B]) T.Tuple2[A, C] { + return T.MakeTuple2(tab.F1, pbc(tab.F2)) + } } func Promap[E, A, D, B any](f func(D) E, g func(A) B) func(Reader[E, A]) Reader[D, B] { - return G.Promap[Reader[E, A], Reader[D, B]](f, g) + return func(fea Reader[E, A]) Reader[D, B] { + return F.Flow3(f, fea, g) + } } // Local changes the value of the local context during the execution of the action `ma` (similar to `Contravariant`'s // `contramap`). func Local[R2, R1, A any](f func(R2) R1) func(Reader[R1, A]) Reader[R2, A] { - return G.Local[Reader[R1, A], Reader[R2, A]](f) + return Compose[R2, R1, A](f) } // Read applies a context to a reader to obtain its value func Read[E, A any](e E) func(Reader[E, A]) A { - return G.Read[Reader[E, A]](e) + return I.Ap[A](e) } func MonadFlap[R, A, B any](fab Reader[R, func(A) B], a A) Reader[R, B] { - return G.MonadFlap[Reader[R, func(A) B], Reader[R, B]](fab, a) + return INTF.MonadFlap(MonadMap[R, func(A) B, B], fab, a) } func Flap[R, A, B any](a A) func(Reader[R, func(A) B]) Reader[R, B] { - return G.Flap[Reader[R, func(A) B], Reader[R, B]](a) + return INTF.Flap(Map[R, func(A) B, B], a) }