1
0
mirror of https://github.com/IBM/fp-go.git synced 2025-11-25 22:21:49 +02:00

fix: slowly migrate IO

Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
This commit is contained in:
Dr. Carsten Leue
2025-03-02 10:57:52 +01:00
parent ddb9e48441
commit eb4218c575
29 changed files with 281 additions and 333 deletions

View File

@@ -67,9 +67,9 @@ type MultiInjectionToken[T any] interface {
// makeID creates a generator of unique string IDs // makeID creates a generator of unique string IDs
func makeID() IO.IO[string] { func makeID() IO.IO[string] {
var count atomic.Int64 var count atomic.Int64
return IO.MakeIO(func() string { return func() string {
return strconv.FormatInt(count.Add(1), 16) return strconv.FormatInt(count.Add(1), 16)
}) }
} }
// genID is the common generator of unique string IDs // genID is the common generator of unique string IDs

View File

@@ -13,19 +13,30 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
package generic package io
import ( 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 { func (o *ioApplicative[A, B]) Of(a A) IO[A] {
return Map[GA, GB, A, B](f) return Of(a)
} }
// Functor implements the functoric operations for [IO] func (o *ioApplicative[A, B]) Map(f func(A) B) func(IO[A]) IO[B] {
func Functor[A, B any, GA ~func() A, GB ~func() B]() functor.Functor[A, B, GA, GB] { return Map(f)
return &ioFunctor[A, B, GA, GB]{} }
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]{}
} }

View File

@@ -16,15 +16,14 @@
package io package io
import ( import (
G "github.com/IBM/fp-go/v2/io/generic"
M "github.com/IBM/fp-go/v2/monoid" M "github.com/IBM/fp-go/v2/monoid"
S "github.com/IBM/fp-go/v2/semigroup" 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]) 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]] { 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)
} }

View File

@@ -16,7 +16,7 @@
package io package io
import ( 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 // 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], use func(A) IO[B],
release func(A, B) IO[ANY], release func(A, B) IO[ANY],
) IO[B] { ) 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,
)
} }

View File

@@ -17,15 +17,24 @@ package io
import ( import (
EQ "github.com/IBM/fp-go/v2/eq" 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 // Eq implements the equals predicate for values contained in the IO monad
func Eq[A any](e EQ.Eq[A]) EQ.Eq[IO[A]] { 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 // FromStrictEquals constructs an [EQ.Eq] from the canonical comparison function
func FromStrictEquals[A comparable]() EQ.Eq[IO[A]] { func FromStrictEquals[A comparable]() EQ.Eq[IO[A]] {
return G.FromStrictEquals[IO[A]]() return Eq(EQ.FromStrictEquals[A]())
} }

View File

@@ -24,16 +24,16 @@ import (
// Close closes a closeable resource and ignores a potential error // Close closes a closeable resource and ignores a potential error
func Close[R io.Closer](r R) IO.IO[R] { func Close[R io.Closer](r R) IO.IO[R] {
return IO.MakeIO[R](func() R { return func() R {
r.Close() // #nosec: G104 r.Close() // #nosec: G104
return r return r
}) }
} }
// Remove removes a resource and ignores a potential error // Remove removes a resource and ignores a potential error
func Remove(name string) IO.IO[string] { func Remove(name string) IO.IO[string] {
return IO.MakeIO[string](func() string { return func() string {
os.Remove(name) // #nosec: G104 os.Remove(name) // #nosec: G104
return name return name
}) }
} }

View File

@@ -17,10 +17,19 @@ package io
import ( import (
"github.com/IBM/fp-go/v2/internal/functor" "github.com/IBM/fp-go/v2/internal/functor"
G "github.com/IBM/fp-go/v2/io/generic"
) )
// Functor returns the monadic operations for [IO] type (
func Functor[A, B any]() functor.Functor[A, B, IO[A], IO[B]] { ioFunctor[A, B any] struct{}
return G.Functor[A, B, IO[A], IO[B]]()
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]{}
} }

View File

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

View File

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

View File

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

View File

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

View File

@@ -18,152 +18,253 @@ package io
import ( import (
"time" "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" 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 // IO represents a synchronous computation that cannot fail
// refer to [https://andywhite.xyz/posts/2021-01-27-rte-foundations/#ioltagt] for more details // refer to [https://andywhite.xyz/posts/2021-01-27-rte-foundations/#ioltagt] for more details
type IO[A any] func() A type IO[A any] = func() A
func MakeIO[A any](f func() A) IO[A] {
return G.MakeIO[IO[A]](f)
}
func Of[A any](a A) IO[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] { 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 // FromImpure converts a side effect without a return value into a side effect that returns any
func FromImpure(f func()) IO[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] { 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] { 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] { 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] { 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] { 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. // 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] { 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. // 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]) 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] { // MonadApSeq implements the applicative on a single thread by first executing mab and the ma
return G.MonadAp[IO[A], IO[B]](mab, 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] { 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] { 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 // Memoize computes the value of the provided [IO] monad lazily but exactly once
func Memoize[A any](ma IO[A]) IO[A] { 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 // 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. // keeping only the result of the first.
func MonadChainFirst[A, B any](fa IO[A], f func(A) IO[B]) IO[A] { 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 // 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. // 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]) 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. // 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] { 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. // 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]) 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. // 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] { 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. // 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]) 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 // 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] { 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 // 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]) func(IO[A]) IO[B] {
return G.ChainTo[IO[A]](fb) return Chain(F.Constant1[A](fb))
} }
// Now returns the current timestamp // 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 // 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] { 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] { 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] { 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 // 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) 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 // 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) 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 // 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]] { 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] // WithDuration returns an operation that measures the [time.Duration]
func WithDuration[A any](a IO[A]) IO[T.Tuple2[A, 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))
}
} }

View File

@@ -44,7 +44,7 @@ func TestFlatten(t *testing.T) {
} }
func TestMemoize(t *testing.T) { func TestMemoize(t *testing.T) {
data := Memoize(MakeIO(rand.Int)) data := Memoize(rand.Int)
value1 := data() value1 := data()
value2 := data() value2 := data()

View File

@@ -17,10 +17,30 @@ package io
import ( import (
"github.com/IBM/fp-go/v2/internal/monad" "github.com/IBM/fp-go/v2/internal/monad"
G "github.com/IBM/fp-go/v2/io/generic"
) )
// Monad returns the monadic operations for [IO] type (
func Monad[A, B any]() monad.Monad[A, B, IO[A], IO[B], IO[func(A) B]] { ioMonad[A, B any] struct{}
return G.Monad[A, B, IO[A], IO[B], IO[func(A) B]]() 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]{}
} }

View File

@@ -17,10 +17,18 @@ package io
import ( import (
"github.com/IBM/fp-go/v2/internal/pointed" "github.com/IBM/fp-go/v2/internal/pointed"
G "github.com/IBM/fp-go/v2/io/generic"
) )
// Pointed returns the monadic operations for [IO] type (
func Pointed[A any]() pointed.Pointed[A, IO[A]] { ioPointed[A any] struct{}
return G.Pointed[A, IO[A]]() 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]{}
} }

View File

@@ -33,39 +33,25 @@ func AssertLaws[A, B, C any](t *testing.T,
bc func(B) C, bc func(B) C,
) func(a A) bool { ) func(a A) bool {
return L.AssertLaws(t, return L.MonadAssertLaws(t,
io.Eq(eqa), io.Eq(eqa),
io.Eq(eqb), io.Eq(eqb),
io.Eq(eqc), io.Eq(eqc),
io.Of[A], io.Pointed[C](),
io.Of[B], io.Pointed[func(A) A](),
io.Of[C], io.Pointed[func(B) C](),
io.Pointed[func(func(A) B) B](),
io.Of[func(A) A], io.Functor[func(B) C, func(func(A) B) func(A) C](),
io.Of[func(A) B],
io.Of[func(B) C],
io.Of[func(func(A) B) B],
io.MonadMap[A, A], io.Applicative[func(A) B, B](),
io.MonadMap[A, B], io.Applicative[func(A) B, func(A) C](),
io.MonadMap[A, C],
io.MonadMap[B, C],
io.MonadMap[func(B) C, func(func(A) B) func(A) C], io.Monad[A, A](),
io.Monad[A, B](),
io.MonadChain[A, A], io.Monad[A, C](),
io.MonadChain[A, B], io.Monad[B, C](),
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],
ab, ab,
bc, bc,

View File

@@ -18,15 +18,15 @@ package ioeither
import ( import (
ET "github.com/IBM/fp-go/v2/either" ET "github.com/IBM/fp-go/v2/either"
EQ "github.com/IBM/fp-go/v2/eq" 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 // 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]] { 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 // FromStrictEquals constructs an [EQ.Eq] from the canonical comparison function
func FromStrictEquals[E, A comparable]() EQ.Eq[IOEither[E, A]] { func FromStrictEquals[E, A comparable]() EQ.Eq[IOEither[E, A]] {
return G.FromStrictEquals[IOEither[E, A]]() return Eq(ET.FromStrictEquals[E, A]())
} }

View File

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

View File

@@ -19,7 +19,9 @@ import (
"time" "time"
ET "github.com/IBM/fp-go/v2/either" ET "github.com/IBM/fp-go/v2/either"
"github.com/IBM/fp-go/v2/internal/eithert"
I "github.com/IBM/fp-go/v2/io" 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" G "github.com/IBM/fp-go/v2/ioeither/generic"
IOO "github.com/IBM/fp-go/v2/iooption" IOO "github.com/IBM/fp-go/v2/iooption"
L "github.com/IBM/fp-go/v2/lazy" L "github.com/IBM/fp-go/v2/lazy"
@@ -28,26 +30,26 @@ import (
// IOEither represents a synchronous computation that may fail // IOEither represents a synchronous computation that may fail
// refer to [https://andywhite.xyz/posts/2021-01-27-rte-foundations/#ioeitherlte-agt] for more details // refer to [https://andywhite.xyz/posts/2021-01-27-rte-foundations/#ioeitherlte-agt] for more details
type IOEither[E, A any] I.IO[ET.Either[E, A]] type IOEither[E, A any] = I.IO[ET.Either[E, A]]
func MakeIO[E, A any](f IOEither[E, A]) IOEither[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] { 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] { 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] { 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] { 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] { func LeftIO[A, E any](ml I.IO[E]) IOEither[E, A] {

View File

@@ -70,9 +70,9 @@ func TestFromOption(t *testing.T) {
func TestChainIOK(t *testing.T) { func TestChainIOK(t *testing.T) {
f := ChainIOK[string](func(n int) I.IO[string] { f := ChainIOK[string](func(n int) I.IO[string] {
return I.MakeIO(func() string { return func() string {
return fmt.Sprintf("%d", n) return fmt.Sprintf("%d", n)
}) }
}) })
assert.Equal(t, E.Right[string]("1"), f(Right[string](1))()) assert.Equal(t, E.Right[string]("1"), f(Right[string](1))())

View File

@@ -17,15 +17,16 @@ package iooption
import ( import (
EQ "github.com/IBM/fp-go/v2/eq" 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 // Eq implements the equals predicate for values contained in the IO monad
func Eq[A any](e EQ.Eq[A]) EQ.Eq[IOOption[A]] { func Eq[A any](eq EQ.Eq[A]) EQ.Eq[IOOption[A]] {
return G.Eq[IOOption[A]](e) return IO.Eq(O.Eq(eq))
} }
// FromStrictEquals constructs an [EQ.Eq] from the canonical comparison function // FromStrictEquals constructs an [EQ.Eq] from the canonical comparison function
func FromStrictEquals[A comparable]() EQ.Eq[IOOption[A]] { func FromStrictEquals[A comparable]() EQ.Eq[IOOption[A]] {
return G.FromStrictEquals[IOOption[A]]() return Eq(EQ.FromStrictEquals[A]())
} }

View File

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

View File

@@ -28,7 +28,7 @@ import (
// IO represents a synchronous computation that may fail // IO represents a synchronous computation that may fail
// refer to [https://andywhite.xyz/posts/2021-01-27-rte-foundations/#ioeitherlte-agt] for more details // refer to [https://andywhite.xyz/posts/2021-01-27-rte-foundations/#ioeitherlte-agt] for more details
type 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] { func MakeIO[A any](f IOOption[A]) IOOption[A] {
return G.MakeIO(f) return G.MakeIO(f)

View File

@@ -56,9 +56,9 @@ func TestFromOption(t *testing.T) {
func TestChainIOK(t *testing.T) { func TestChainIOK(t *testing.T) {
f := ChainIOK(func(n int) I.IO[string] { f := ChainIOK(func(n int) I.IO[string] {
return I.MakeIO(func() string { return func() string {
return fmt.Sprintf("%d", n) return fmt.Sprintf("%d", n)
}) }
}) })
assert.Equal(t, O.Of("1"), f(Of(1))()) assert.Equal(t, O.Of("1"), f(Of(1))())

View File

@@ -16,11 +16,11 @@
package lambda package lambda
// Y is the Y-combinator based on https://dreamsongs.com/Files/WhyOfY.pdf // 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 func(t T) R {
return f(h(h))(t) return f(h(h))(t)
} }

View File

@@ -16,15 +16,15 @@
package lazy package lazy
import ( 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" M "github.com/IBM/fp-go/v2/monoid"
S "github.com/IBM/fp-go/v2/semigroup" S "github.com/IBM/fp-go/v2/semigroup"
) )
func ApplySemigroup[A any](s S.Semigroup[A]) S.Semigroup[Lazy[A]] { 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]] { func ApplicativeMonoid[A any](m M.Monoid[A]) M.Monoid[Lazy[A]] {
return G.ApplicativeMonoid[Lazy[A]](m) return IO.ApplicativeMonoid(m)
} }

View File

@@ -17,10 +17,10 @@ package lazy
import ( import (
EQ "github.com/IBM/fp-go/v2/eq" 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 // Eq implements the equals predicate for values contained in the IO monad
func Eq[A any](e EQ.Eq[A]) EQ.Eq[Lazy[A]] { func Eq[A any](e EQ.Eq[A]) EQ.Eq[Lazy[A]] {
return G.Eq[Lazy[A]](e) return IO.Eq(e)
} }

View File

@@ -22,7 +22,7 @@ import (
) )
// Lazy represents a synchronous computation without side effects // 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] { func MakeLazy[A any](f func() A) Lazy[A] {
return G.MakeIO[Lazy[A]](f) return G.MakeIO[Lazy[A]](f)

View File

@@ -28,7 +28,7 @@ func ApplicativeMonoid[A, HKTA, HKTFA any](
) Monoid[HKTA] { ) Monoid[HKTA] {
return MakeMonoid( return MakeMonoid(
S.ApplySemigroup[A](fmap, fap, m).Concat, S.ApplySemigroup(fmap, fap, m).Concat,
fof(m.Empty()), fof(m.Empty()),
) )
} }