diff --git a/v2/di/token.go b/v2/di/token.go index 798f353..f0797ac 100644 --- a/v2/di/token.go +++ b/v2/di/token.go @@ -67,9 +67,9 @@ type MultiInjectionToken[T any] interface { // makeID creates a generator of unique string IDs func makeID() IO.IO[string] { var count atomic.Int64 - return IO.MakeIO(func() string { + return func() string { return strconv.FormatInt(count.Add(1), 16) - }) + } } // genID is the common generator of unique string IDs diff --git a/v2/io/generic/functor.go b/v2/io/applicative.go similarity index 52% rename from v2/io/generic/functor.go rename to v2/io/applicative.go index 35840df..f6030ae 100644 --- a/v2/io/generic/functor.go +++ b/v2/io/applicative.go @@ -13,19 +13,30 @@ // See the License for the specific language governing permissions and // limitations under the License. -package generic +package io import ( - "github.com/IBM/fp-go/v2/internal/functor" + "github.com/IBM/fp-go/v2/internal/applicative" ) -type ioFunctor[A, B any, GA ~func() A, GB ~func() B] struct{} +type ( + ioApplicative[A, B any] struct{} + IOApplicative[A, B any] = applicative.Applicative[A, B, IO[A], IO[B], IO[func(A) B]] +) -func (o *ioFunctor[A, B, GA, GB]) Map(f func(A) B) func(GA) GB { - return Map[GA, GB, A, B](f) +func (o *ioApplicative[A, B]) Of(a A) IO[A] { + return Of(a) } -// Functor implements the functoric operations for [IO] -func Functor[A, B any, GA ~func() A, GB ~func() B]() functor.Functor[A, B, GA, GB] { - return &ioFunctor[A, B, GA, GB]{} +func (o *ioApplicative[A, B]) Map(f func(A) B) func(IO[A]) IO[B] { + return Map(f) +} + +func (o *ioApplicative[A, B]) Ap(fa IO[A]) func(IO[func(A) B]) IO[B] { + return Ap[B](fa) +} + +// Applicative implements the applicativeic operations for [IO] +func Applicative[A, B any]() IOApplicative[A, B] { + return &ioApplicative[A, B]{} } diff --git a/v2/io/apply.go b/v2/io/apply.go index ff80dba..e7c9909 100644 --- a/v2/io/apply.go +++ b/v2/io/apply.go @@ -16,15 +16,14 @@ package io import ( - G "github.com/IBM/fp-go/v2/io/generic" M "github.com/IBM/fp-go/v2/monoid" S "github.com/IBM/fp-go/v2/semigroup" ) func ApplySemigroup[A any](s S.Semigroup[A]) S.Semigroup[IO[A]] { - return G.ApplySemigroup[IO[A]](s) + return S.ApplySemigroup(MonadMap[A, func(A) A], MonadAp[A, A], s) } func ApplicativeMonoid[A any](m M.Monoid[A]) M.Monoid[IO[A]] { - return G.ApplicativeMonoid[IO[A]](m) + return M.ApplicativeMonoid(Of[A], MonadMap[A, func(A) A], MonadAp[A, A], m) } diff --git a/v2/io/bracket.go b/v2/io/bracket.go index 2e1a970..ec34e9a 100644 --- a/v2/io/bracket.go +++ b/v2/io/bracket.go @@ -16,7 +16,7 @@ package io import ( - G "github.com/IBM/fp-go/v2/io/generic" + INTB "github.com/IBM/fp-go/v2/internal/bracket" ) // Bracket makes sure that a resource is cleaned up in the event of an error. The release action is called regardless of @@ -26,5 +26,14 @@ func Bracket[A, B, ANY any]( use func(A) IO[B], release func(A, B) IO[ANY], ) IO[B] { - return G.Bracket(acquire, use, release) + return INTB.Bracket[IO[A], IO[B], IO[ANY], B, A, B]( + Of[B], + MonadChain[A, B], + MonadChain[B, B], + MonadChain[ANY, B], + + acquire, + use, + release, + ) } diff --git a/v2/io/eq.go b/v2/io/eq.go index fb76a05..c7ad242 100644 --- a/v2/io/eq.go +++ b/v2/io/eq.go @@ -17,15 +17,24 @@ package io import ( EQ "github.com/IBM/fp-go/v2/eq" - G "github.com/IBM/fp-go/v2/io/generic" + INTE "github.com/IBM/fp-go/v2/internal/eq" ) // Eq implements the equals predicate for values contained in the IO monad func Eq[A any](e EQ.Eq[A]) EQ.Eq[IO[A]] { - return G.Eq[IO[A]](e) + // comparator for the monad + eq := INTE.Eq( + MonadMap[A, func(A) bool], + MonadAp[A, bool], + e, + ) + // eagerly execute + return EQ.FromEquals(func(l, r IO[A]) bool { + return eq(l, r)() + }) } // FromStrictEquals constructs an [EQ.Eq] from the canonical comparison function func FromStrictEquals[A comparable]() EQ.Eq[IO[A]] { - return G.FromStrictEquals[IO[A]]() + return Eq(EQ.FromStrictEquals[A]()) } diff --git a/v2/io/file/file.go b/v2/io/file/file.go index 934ff79..6d78e31 100644 --- a/v2/io/file/file.go +++ b/v2/io/file/file.go @@ -24,16 +24,16 @@ import ( // Close closes a closeable resource and ignores a potential error func Close[R io.Closer](r R) IO.IO[R] { - return IO.MakeIO[R](func() R { + return func() R { r.Close() // #nosec: G104 return r - }) + } } // Remove removes a resource and ignores a potential error func Remove(name string) IO.IO[string] { - return IO.MakeIO[string](func() string { + return func() string { os.Remove(name) // #nosec: G104 return name - }) + } } diff --git a/v2/io/functor.go b/v2/io/functor.go index b35b591..79714a7 100644 --- a/v2/io/functor.go +++ b/v2/io/functor.go @@ -17,10 +17,19 @@ package io import ( "github.com/IBM/fp-go/v2/internal/functor" - G "github.com/IBM/fp-go/v2/io/generic" ) -// Functor returns the monadic operations for [IO] -func Functor[A, B any]() functor.Functor[A, B, IO[A], IO[B]] { - return G.Functor[A, B, IO[A], IO[B]]() +type ( + ioFunctor[A, B any] struct{} + + 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] { + return Map(f) +} + +// Functor implements the functoric operations for [IO] +func Functor[A, B any]() IOFunctor[A, B] { + return &ioFunctor[A, B]{} } diff --git a/v2/io/generic/apply.go b/v2/io/generic/apply.go deleted file mode 100644 index 331c05c..0000000 --- a/v2/io/generic/apply.go +++ /dev/null @@ -1,29 +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 ( - M "github.com/IBM/fp-go/v2/monoid" - S "github.com/IBM/fp-go/v2/semigroup" -) - -func ApplySemigroup[GA ~func() A, A any](s S.Semigroup[A]) S.Semigroup[GA] { - return S.ApplySemigroup(MonadMap[GA, func() func(A) A, A, func(A) A], MonadAp[GA, GA, func() func(A) A, A, A], s) -} - -func ApplicativeMonoid[GA ~func() A, A any](m M.Monoid[A]) M.Monoid[GA] { - return M.ApplicativeMonoid(Of[GA, A], MonadMap[GA, func() func(A) A, A, func(A) A], MonadAp[GA, GA, func() func(A) A, A, A], m) -} diff --git a/v2/io/generic/eq.go b/v2/io/generic/eq.go deleted file mode 100644 index 404da3a..0000000 --- a/v2/io/generic/eq.go +++ /dev/null @@ -1,40 +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 ( - EQ "github.com/IBM/fp-go/v2/eq" - G "github.com/IBM/fp-go/v2/internal/eq" -) - -// Eq implements the equals predicate for values contained in the IO monad -func Eq[GA ~func() A, A any](e EQ.Eq[A]) EQ.Eq[GA] { - // comparator for the monad - eq := G.Eq( - MonadMap[GA, func() func(A) bool, A, func(A) bool], - MonadAp[GA, func() bool, func() func(A) bool, A, bool], - e, - ) - // eagerly execute - return EQ.FromEquals(func(l, r GA) bool { - return eq(l, r)() - }) -} - -// FromStrictEquals constructs an [EQ.Eq] from the canonical comparison function -func FromStrictEquals[GA ~func() A, A comparable]() EQ.Eq[GA] { - return Eq[GA](EQ.FromStrictEquals[A]()) -} diff --git a/v2/io/generic/monad.go b/v2/io/generic/monad.go deleted file mode 100644 index 41ff613..0000000 --- a/v2/io/generic/monad.go +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) 2024 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/v2/internal/monad" -) - -type ioMonad[A, B any, GA ~func() A, GB ~func() B, GAB ~func() func(A) B] struct{} - -func (o *ioMonad[A, B, GA, GB, GAB]) Of(a A) GA { - return Of[GA, A](a) -} - -func (o *ioMonad[A, B, GA, GB, GAB]) Map(f func(A) B) func(GA) GB { - return Map[GA, GB, A, B](f) -} - -func (o *ioMonad[A, B, GA, GB, GAB]) Chain(f func(A) GB) func(GA) GB { - return Chain[GA, GB, A, B](f) -} - -func (o *ioMonad[A, B, GA, GB, GAB]) Ap(fa GA) func(GAB) GB { - return Ap[GB, GAB, GA, B, A](fa) -} - -// Monad implements the monadic operations for [Option] -func Monad[A, B any, GA ~func() A, GB ~func() B, GAB ~func() func(A) B]() monad.Monad[A, B, GA, GB, GAB] { - return &ioMonad[A, B, GA, GB, GAB]{} -} diff --git a/v2/io/generic/pointed.go b/v2/io/generic/pointed.go deleted file mode 100644 index 79032e4..0000000 --- a/v2/io/generic/pointed.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) 2024 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/v2/internal/pointed" -) - -type ioPointed[A any, GA ~func() A] struct{} - -func (o *ioPointed[A, GA]) Of(a A) GA { - return Of[GA, A](a) -} - -// Pointed implements the pointedic operations for [IO] -func Pointed[A any, GA ~func() A]() pointed.Pointed[A, GA] { - return &ioPointed[A, GA]{} -} diff --git a/v2/io/io.go b/v2/io/io.go index 11ee478..c273f6e 100644 --- a/v2/io/io.go +++ b/v2/io/io.go @@ -18,152 +18,253 @@ package io import ( "time" - G "github.com/IBM/fp-go/v2/io/generic" + F "github.com/IBM/fp-go/v2/function" + 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" + INTL "github.com/IBM/fp-go/v2/internal/lazy" T "github.com/IBM/fp-go/v2/tuple" ) +const ( + // useParallel is the feature flag to control if we use the parallel or the sequential implementation of ap + useParallel = true +) + +var ( + // undefined represents an undefined value + 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 - -func MakeIO[A any](f func() A) IO[A] { - return G.MakeIO[IO[A]](f) -} +type IO[A any] = func() A func Of[A any](a A) IO[A] { - return G.Of[IO[A]](a) + return F.Constant(a) } func FromIO[A any](a IO[A]) IO[A] { - return G.FromIO(a) + return a } // FromImpure converts a side effect without a return value into a side effect that returns any func FromImpure(f func()) IO[any] { - return G.FromImpure[IO[any]](f) + return func() any { + f() + return undefined + } } func MonadOf[A any](a A) IO[A] { - return G.MonadOf[IO[A]](a) + return F.Constant(a) } func MonadMap[A, B any](fa IO[A], f func(A) B) IO[B] { - return G.MonadMap[IO[A], IO[B]](fa, f) + return func() B { + return f(fa()) + } } func Map[A, B any](f func(A) B) func(fa IO[A]) IO[B] { - return G.Map[IO[A], IO[B]](f) + return F.Bind2nd(MonadMap[A, B], f) } func MonadMapTo[A, B any](fa IO[A], b B) IO[B] { - return G.MonadMapTo[IO[A], IO[B]](fa, b) + return MonadMap(fa, F.Constant1[A](b)) } func MapTo[A, B any](b B) func(IO[A]) IO[B] { - return G.MapTo[IO[A], IO[B]](b) + return Map(F.Constant1[A](b)) } // MonadChain composes computations in sequence, using the return value of one computation to determine the next computation. func MonadChain[A, B any](fa IO[A], f func(A) IO[B]) IO[B] { - return G.MonadChain(fa, f) + return func() B { + return f(fa())() + } } // Chain composes computations in sequence, using the return value of one computation to determine the next computation. func Chain[A, B any](f func(A) IO[B]) func(IO[A]) IO[B] { - return G.Chain[IO[A]](f) + return F.Bind2nd(MonadChain[A, B], f) } -func MonadAp[B, A any](mab IO[func(A) B], ma IO[A]) IO[B] { - return G.MonadAp[IO[A], IO[B]](mab, ma) +// MonadApSeq implements the applicative on a single thread by first executing mab and the ma +func MonadApSeq[A, B any](mab IO[func(A) B], ma IO[A]) IO[B] { + return MonadChain(mab, F.Bind1st(MonadMap[A, B], 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 +func MonadApPar[A, B any](mab IO[func(A) B], ma IO[A]) IO[B] { + return func() B { + c := make(chan A) + go func() { + c <- ma() + close(c) + }() + return mab()(<-c) + } +} + +// MonadAp implements the `ap` operation. Depending on a feature flag this will be sequential or parallel, the preferred implementation +// is parallel +func MonadAp[A, B any](mab IO[func(A) B], ma IO[A]) IO[B] { + if useParallel { + return MonadApPar(mab, ma) + } + return MonadApSeq(mab, ma) } func Ap[B, A any](ma IO[A]) func(IO[func(A) B]) IO[B] { - return G.Ap[IO[B], IO[func(A) B], IO[A]](ma) + return F.Bind2nd(MonadAp[A, B], ma) } func Flatten[A any](mma IO[IO[A]]) IO[A] { - return G.Flatten(mma) + return MonadChain(mma, F.Identity) } // Memoize computes the value of the provided [IO] monad lazily but exactly once func Memoize[A any](ma IO[A]) IO[A] { - return G.Memoize(ma) + return INTL.Memoize(ma) } // MonadChainFirst composes computations in sequence, using the return value of one computation to determine the next computation and // keeping only the result of the first. func MonadChainFirst[A, B any](fa IO[A], f func(A) IO[B]) IO[A] { - return G.MonadChainFirst(fa, f) + return INTC.MonadChainFirst(MonadChain[A, A], MonadMap[B, A], fa, f) } // ChainFirst composes computations in sequence, using the return value of one computation to determine the next computation and // keeping only the result of the first. func ChainFirst[A, B any](f func(A) IO[B]) func(IO[A]) IO[A] { - return G.ChainFirst[IO[A]](f) + return INTC.ChainFirst( + Chain[A, A], + Map[B, A], + f, + ) } // MonadApFirst combines two effectful actions, keeping only the result of the first. func MonadApFirst[A, B any](first IO[A], second IO[B]) IO[A] { - return G.MonadApFirst[IO[A], IO[B], IO[func(B) A]](first, second) + return INTA.MonadApFirst( + MonadAp[B, A], + MonadMap[A, func(B) A], + + first, + second, + ) } // ApFirst combines two effectful actions, keeping only the result of the first. func ApFirst[A, B any](second IO[B]) func(IO[A]) IO[A] { - return G.ApFirst[IO[A], IO[B], IO[func(B) A]](second) + return INTA.ApFirst( + MonadAp[B, A], + MonadMap[A, func(B) A], + + second, + ) } // MonadApSecond combines two effectful actions, keeping only the result of the second. func MonadApSecond[A, B any](first IO[A], second IO[B]) IO[B] { - return G.MonadApSecond[IO[A], IO[B], IO[func(B) B]](first, second) + return INTA.MonadApSecond( + MonadAp[B, B], + MonadMap[A, func(B) B], + + first, + second, + ) } // ApSecond combines two effectful actions, keeping only the result of the second. func ApSecond[A, B any](second IO[B]) func(IO[A]) IO[B] { - return G.ApSecond[IO[A], IO[B], IO[func(B) B]](second) + return INTA.ApSecond( + MonadAp[B, B], + MonadMap[A, func(B) B], + + second, + ) } // MonadChainTo composes computations in sequence, ignoring the return value of the first computation func MonadChainTo[A, B any](fa IO[A], fb IO[B]) IO[B] { - return G.MonadChainTo(fa, fb) + return MonadChain(fa, F.Constant1[A](fb)) } // ChainTo composes computations in sequence, ignoring the return value of the first computation func ChainTo[A, B any](fb IO[B]) func(IO[A]) IO[B] { - return G.ChainTo[IO[A]](fb) + return Chain(F.Constant1[A](fb)) } // Now returns the current timestamp -var Now = G.Now[IO[time.Time]]() +var Now IO[time.Time] = time.Now // Defer creates an IO by creating a brand new IO via a generator function, each time func Defer[A any](gen func() IO[A]) IO[A] { - return G.Defer[IO[A]](gen) + return func() A { + return gen()() + } } func MonadFlap[B, A any](fab IO[func(A) B], a A) IO[B] { - return G.MonadFlap[func(A) B, IO[func(A) B], IO[B], A, B](fab, a) + return INTF.MonadFlap(MonadMap[func(A) B, B], fab, a) } func Flap[B, A any](a A) func(IO[func(A) B]) IO[B] { - return G.Flap[func(A) B, IO[func(A) B], IO[B], A, B](a) + 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] { - return G.Delay[IO[A]](delay) + return func(ga IO[A]) IO[A] { + return func() A { + time.Sleep(delay) + return ga() + } + } +} + +func after(timestamp time.Time) func() { + return func() { + // check if we need to wait + current := time.Now() + if current.Before(timestamp) { + time.Sleep(timestamp.Sub(current)) + } + } } // After creates an operation that passes after the given timestamp func After[A any](timestamp time.Time) func(IO[A]) IO[A] { - return G.After[IO[A]](timestamp) + aft := after(timestamp) + return func(ga IO[A]) IO[A] { + return func() A { + // wait as long as necessary + aft() + // execute after wait + return ga() + } + } } // WithTime returns an operation that measures the start and end [time.Time] of the operation func WithTime[A any](a IO[A]) IO[T.Tuple3[A, time.Time, time.Time]] { - return G.WithTime[IO[T.Tuple3[A, time.Time, time.Time]], IO[A]](a) + return func() T.Tuple3[A, time.Time, time.Time] { + t0 := time.Now() + res := a() + t1 := time.Now() + return T.MakeTuple3(res, t0, t1) + } } // WithDuration returns an operation that measures the [time.Duration] func WithDuration[A any](a IO[A]) IO[T.Tuple2[A, time.Duration]] { - return G.WithDuration[IO[T.Tuple2[A, time.Duration]], IO[A]](a) + return func() T.Tuple2[A, time.Duration] { + t0 := time.Now() + res := a() + t1 := time.Now() + return T.MakeTuple2(res, t1.Sub(t0)) + } } diff --git a/v2/io/io_test.go b/v2/io/io_test.go index b0efcb8..0c2ca2e 100644 --- a/v2/io/io_test.go +++ b/v2/io/io_test.go @@ -44,7 +44,7 @@ func TestFlatten(t *testing.T) { } func TestMemoize(t *testing.T) { - data := Memoize(MakeIO(rand.Int)) + data := Memoize(rand.Int) value1 := data() value2 := data() diff --git a/v2/io/monad.go b/v2/io/monad.go index df8cd05..a5986f6 100644 --- a/v2/io/monad.go +++ b/v2/io/monad.go @@ -17,10 +17,30 @@ package io import ( "github.com/IBM/fp-go/v2/internal/monad" - G "github.com/IBM/fp-go/v2/io/generic" ) -// Monad returns the monadic operations for [IO] -func Monad[A, B any]() monad.Monad[A, B, IO[A], IO[B], IO[func(A) B]] { - return G.Monad[A, B, IO[A], IO[B], IO[func(A) B]]() +type ( + ioMonad[A, B any] struct{} + IOMonad[A, B any] = monad.Monad[A, B, IO[A], IO[B], IO[func(A) B]] +) + +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] { + return Map(f) +} + +func (o *ioMonad[A, B]) Chain(f func(A) IO[B]) func(IO[A]) IO[B] { + return Chain(f) +} + +func (o *ioMonad[A, B]) Ap(fa IO[A]) func(IO[func(A) B]) IO[B] { + return Ap[B](fa) +} + +// Monad implements the monadic operations for [IO] +func Monad[A, B any]() IOMonad[A, B] { + return &ioMonad[A, B]{} } diff --git a/v2/io/pointed.go b/v2/io/pointed.go index df78baa..93d7c18 100644 --- a/v2/io/pointed.go +++ b/v2/io/pointed.go @@ -17,10 +17,18 @@ package io import ( "github.com/IBM/fp-go/v2/internal/pointed" - G "github.com/IBM/fp-go/v2/io/generic" ) -// Pointed returns the monadic operations for [IO] -func Pointed[A any]() pointed.Pointed[A, IO[A]] { - return G.Pointed[A, IO[A]]() +type ( + ioPointed[A any] struct{} + IOPointed[A any] = pointed.Pointed[A, IO[A]] +) + +func (o *ioPointed[A]) Of(a A) IO[A] { + return Of(a) +} + +// Pointed implements the pointedic operations for [IO] +func Pointed[A any]() IOPointed[A] { + return &ioPointed[A]{} } diff --git a/v2/io/testing/laws.go b/v2/io/testing/laws.go index fffd0fb..9c7ef93 100644 --- a/v2/io/testing/laws.go +++ b/v2/io/testing/laws.go @@ -33,39 +33,25 @@ func AssertLaws[A, B, C any](t *testing.T, bc func(B) C, ) func(a A) bool { - return L.AssertLaws(t, + return L.MonadAssertLaws(t, io.Eq(eqa), io.Eq(eqb), io.Eq(eqc), - io.Of[A], - io.Of[B], - io.Of[C], + io.Pointed[C](), + io.Pointed[func(A) A](), + io.Pointed[func(B) C](), + io.Pointed[func(func(A) B) B](), - io.Of[func(A) A], - io.Of[func(A) B], - io.Of[func(B) C], - io.Of[func(func(A) B) B], + io.Functor[func(B) C, func(func(A) B) func(A) C](), - io.MonadMap[A, A], - io.MonadMap[A, B], - io.MonadMap[A, C], - io.MonadMap[B, C], + io.Applicative[func(A) B, B](), + io.Applicative[func(A) B, func(A) C](), - io.MonadMap[func(B) C, func(func(A) B) func(A) C], - - io.MonadChain[A, A], - io.MonadChain[A, B], - io.MonadChain[A, C], - io.MonadChain[B, C], - - io.MonadAp[A, A], - io.MonadAp[B, A], - io.MonadAp[C, B], - io.MonadAp[C, A], - - io.MonadAp[B, func(A) B], - io.MonadAp[func(A) C, func(A) B], + io.Monad[A, A](), + io.Monad[A, B](), + io.Monad[A, C](), + io.Monad[B, C](), ab, bc, diff --git a/v2/ioeither/eq.go b/v2/ioeither/eq.go index af0777d..298e0ad 100644 --- a/v2/ioeither/eq.go +++ b/v2/ioeither/eq.go @@ -18,15 +18,15 @@ package ioeither import ( ET "github.com/IBM/fp-go/v2/either" EQ "github.com/IBM/fp-go/v2/eq" - G "github.com/IBM/fp-go/v2/ioeither/generic" + IO "github.com/IBM/fp-go/v2/io" ) // Eq implements the equals predicate for values contained in the IOEither monad func Eq[E, A any](eq EQ.Eq[ET.Either[E, A]]) EQ.Eq[IOEither[E, A]] { - return G.Eq[IOEither[E, A]](eq) + return IO.Eq(eq) } // FromStrictEquals constructs an [EQ.Eq] from the canonical comparison function func FromStrictEquals[E, A comparable]() EQ.Eq[IOEither[E, A]] { - return G.FromStrictEquals[IOEither[E, A]]() + return Eq(ET.FromStrictEquals[E, A]()) } diff --git a/v2/ioeither/generic/eq.go b/v2/ioeither/generic/eq.go deleted file mode 100644 index 8a92b9f..0000000 --- a/v2/ioeither/generic/eq.go +++ /dev/null @@ -1,32 +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 ( - ET "github.com/IBM/fp-go/v2/either" - EQ "github.com/IBM/fp-go/v2/eq" - G "github.com/IBM/fp-go/v2/io/generic" -) - -// Eq implements the equals predicate for values contained in the IOEither monad -func Eq[GA ~func() ET.Either[E, A], E, A any](eq EQ.Eq[ET.Either[E, A]]) EQ.Eq[GA] { - return G.Eq[GA](eq) -} - -// FromStrictEquals constructs an [EQ.Eq] from the canonical comparison function -func FromStrictEquals[GA ~func() ET.Either[E, A], E, A comparable]() EQ.Eq[GA] { - return Eq[GA](ET.FromStrictEquals[E, A]()) -} diff --git a/v2/ioeither/ioeither.go b/v2/ioeither/ioeither.go index caea425..63a8dff 100644 --- a/v2/ioeither/ioeither.go +++ b/v2/ioeither/ioeither.go @@ -19,7 +19,9 @@ import ( "time" ET "github.com/IBM/fp-go/v2/either" + "github.com/IBM/fp-go/v2/internal/eithert" I "github.com/IBM/fp-go/v2/io" + IOG "github.com/IBM/fp-go/v2/io/generic" G "github.com/IBM/fp-go/v2/ioeither/generic" IOO "github.com/IBM/fp-go/v2/iooption" L "github.com/IBM/fp-go/v2/lazy" @@ -28,26 +30,26 @@ import ( // 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]] +type IOEither[E, A any] = I.IO[ET.Either[E, A]] func MakeIO[E, A any](f IOEither[E, A]) IOEither[E, A] { - return G.MakeIO(f) + return f } func Left[A, E any](l E) IOEither[E, A] { - return G.Left[IOEither[E, A]](l) + return eithert.Left(IOG.MonadOf[IOEither[E, A], ET.Either[E, A]], l) } func Right[E, A any](r A) IOEither[E, A] { - return G.Right[IOEither[E, A]](r) + return eithert.Right(IOG.MonadOf[IOEither[E, A], ET.Either[E, A]], r) } func Of[E, A any](r A) IOEither[E, A] { - return G.Of[IOEither[E, A]](r) + return Right[E, A](r) } func MonadOf[E, A any](r A) IOEither[E, A] { - return G.MonadOf[IOEither[E, A]](r) + return Of[E, A](r) } func LeftIO[A, E any](ml I.IO[E]) IOEither[E, A] { diff --git a/v2/ioeither/ioeither_test.go b/v2/ioeither/ioeither_test.go index fc72f60..8ef54d9 100644 --- a/v2/ioeither/ioeither_test.go +++ b/v2/ioeither/ioeither_test.go @@ -70,9 +70,9 @@ func TestFromOption(t *testing.T) { func TestChainIOK(t *testing.T) { f := ChainIOK[string](func(n int) I.IO[string] { - return I.MakeIO(func() string { + return func() string { return fmt.Sprintf("%d", n) - }) + } }) assert.Equal(t, E.Right[string]("1"), f(Right[string](1))()) diff --git a/v2/iooption/eq.go b/v2/iooption/eq.go index 8cc1ed5..9d131ce 100644 --- a/v2/iooption/eq.go +++ b/v2/iooption/eq.go @@ -17,15 +17,16 @@ package iooption import ( EQ "github.com/IBM/fp-go/v2/eq" - G "github.com/IBM/fp-go/v2/iooption/generic" + IO "github.com/IBM/fp-go/v2/io" + O "github.com/IBM/fp-go/v2/option" ) // Eq implements the equals predicate for values contained in the IO monad -func Eq[A any](e EQ.Eq[A]) EQ.Eq[IOOption[A]] { - return G.Eq[IOOption[A]](e) +func Eq[A any](eq EQ.Eq[A]) EQ.Eq[IOOption[A]] { + return IO.Eq(O.Eq(eq)) } // FromStrictEquals constructs an [EQ.Eq] from the canonical comparison function func FromStrictEquals[A comparable]() EQ.Eq[IOOption[A]] { - return G.FromStrictEquals[IOOption[A]]() + return Eq(EQ.FromStrictEquals[A]()) } diff --git a/v2/iooption/generic/eq.go b/v2/iooption/generic/eq.go deleted file mode 100644 index e1f7385..0000000 --- a/v2/iooption/generic/eq.go +++ /dev/null @@ -1,32 +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 ( - EQ "github.com/IBM/fp-go/v2/eq" - G "github.com/IBM/fp-go/v2/io/generic" - O "github.com/IBM/fp-go/v2/option" -) - -// Eq implements the equals predicate for values contained in the IO monad -func Eq[GA ~func() O.Option[A], A any](e EQ.Eq[A]) EQ.Eq[GA] { - return G.Eq[GA](O.Eq(e)) -} - -// FromStrictEquals constructs an [EQ.Eq] from the canonical comparison function -func FromStrictEquals[GA ~func() O.Option[A], A comparable]() EQ.Eq[GA] { - return Eq[GA](EQ.FromStrictEquals[A]()) -} diff --git a/v2/iooption/iooption.go b/v2/iooption/iooption.go index 9fda43d..71b0791 100644 --- a/v2/iooption/iooption.go +++ b/v2/iooption/iooption.go @@ -28,7 +28,7 @@ import ( // 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]] +type IOOption[A any] = I.IO[O.Option[A]] func MakeIO[A any](f IOOption[A]) IOOption[A] { return G.MakeIO(f) diff --git a/v2/iooption/iooption_test.go b/v2/iooption/iooption_test.go index cb94e23..36c1cec 100644 --- a/v2/iooption/iooption_test.go +++ b/v2/iooption/iooption_test.go @@ -56,9 +56,9 @@ func TestFromOption(t *testing.T) { func TestChainIOK(t *testing.T) { f := ChainIOK(func(n int) I.IO[string] { - return I.MakeIO(func() string { + return func() string { return fmt.Sprintf("%d", n) - }) + } }) assert.Equal(t, O.Of("1"), f(Of(1))()) diff --git a/v2/lambda/y.go b/v2/lambda/y.go index 60cc176..7e725a4 100644 --- a/v2/lambda/y.go +++ b/v2/lambda/y.go @@ -16,11 +16,11 @@ package lambda // Y is the Y-combinator based on https://dreamsongs.com/Files/WhyOfY.pdf -func Y[Endo ~func(RecFct) RecFct, RecFct ~func(T) R, T, R any](f Endo) RecFct { +func Y[T, R any](f func(func(T) R) func(T) R) func(T) R { - type internal[RecFct ~func(T) R, T, R any] func(internal[RecFct, T, R]) RecFct + type internal[T, R any] func(internal[T, R]) func(T) R - g := func(h internal[RecFct, T, R]) RecFct { + g := func(h internal[T, R]) func(T) R { return func(t T) R { return f(h(h))(t) } diff --git a/v2/lazy/apply.go b/v2/lazy/apply.go index 5b3040d..2928aa1 100644 --- a/v2/lazy/apply.go +++ b/v2/lazy/apply.go @@ -16,15 +16,15 @@ package lazy import ( - G "github.com/IBM/fp-go/v2/io/generic" + IO "github.com/IBM/fp-go/v2/io" M "github.com/IBM/fp-go/v2/monoid" S "github.com/IBM/fp-go/v2/semigroup" ) func ApplySemigroup[A any](s S.Semigroup[A]) S.Semigroup[Lazy[A]] { - return G.ApplySemigroup[Lazy[A]](s) + return IO.ApplySemigroup(s) } func ApplicativeMonoid[A any](m M.Monoid[A]) M.Monoid[Lazy[A]] { - return G.ApplicativeMonoid[Lazy[A]](m) + return IO.ApplicativeMonoid(m) } diff --git a/v2/lazy/eq.go b/v2/lazy/eq.go index 4974e68..2cd36fc 100644 --- a/v2/lazy/eq.go +++ b/v2/lazy/eq.go @@ -17,10 +17,10 @@ package lazy import ( EQ "github.com/IBM/fp-go/v2/eq" - G "github.com/IBM/fp-go/v2/io/generic" + IO "github.com/IBM/fp-go/v2/io" ) // 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) + return IO.Eq(e) } diff --git a/v2/lazy/lazy.go b/v2/lazy/lazy.go index dab660f..3386f00 100644 --- a/v2/lazy/lazy.go +++ b/v2/lazy/lazy.go @@ -22,7 +22,7 @@ import ( ) // Lazy represents a synchronous computation without side effects -type Lazy[A any] func() A +type Lazy[A any] = func() A func MakeLazy[A any](f func() A) Lazy[A] { return G.MakeIO[Lazy[A]](f) diff --git a/v2/monoid/apply.go b/v2/monoid/apply.go index f6384c4..667762d 100644 --- a/v2/monoid/apply.go +++ b/v2/monoid/apply.go @@ -28,7 +28,7 @@ func ApplicativeMonoid[A, HKTA, HKTFA any]( ) Monoid[HKTA] { return MakeMonoid( - S.ApplySemigroup[A](fmap, fap, m).Concat, + S.ApplySemigroup(fmap, fap, m).Concat, fof(m.Empty()), ) }