diff --git a/context/readerio/reader.go b/context/readerio/reader.go index b7ac0af..d57c958 100644 --- a/context/readerio/reader.go +++ b/context/readerio/reader.go @@ -18,6 +18,7 @@ package readerio import ( "context" + L "github.com/IBM/fp-go/lazy" R "github.com/IBM/fp-go/readerio/generic" ) @@ -54,6 +55,13 @@ func Ask() ReaderIO[context.Context] { } // Defer creates an IO by creating a brand new IO via a generator function, each time -func Defer[A any](gen func() ReaderIO[A]) ReaderIO[A] { +func Defer[A any](gen L.Lazy[ReaderIO[A]]) ReaderIO[A] { return R.Defer[ReaderIO[A]](gen) } + +// Memoize computes the value of the provided [ReaderIO] monad lazily but exactly once +// The context used to compute the value is the context of the first call, so do not use this +// method if the value has a functional dependency on the content of the context +func Memoize[A any](rdr ReaderIO[A]) ReaderIO[A] { + return R.Memoize[ReaderIO[A]](rdr) +} diff --git a/context/readerioeither/generic/reader.go b/context/readerioeither/generic/reader.go index 415cfd8..2d18ace 100644 --- a/context/readerioeither/generic/reader.go +++ b/context/readerioeither/generic/reader.go @@ -573,3 +573,13 @@ func MonadAlt[LAZY ~func() GEA, GEA ~func(context.Context) GIOA, GIOA ~func() E. func Alt[LAZY ~func() GEA, GEA ~func(context.Context) GIOA, GIOA ~func() E.Either[error, A], A any](second LAZY) func(GEA) GEA { return RIE.Alt(second) } + +// Memoize computes the value of the provided monad lazily but exactly once +// The context used to compute the value is the context of the first call, so do not use this +// method if the value has a functional dependency on the content of the context +func Memoize[ + GRA ~func(context.Context) GIOA, + GIOA ~func() E.Either[error, A], + A any](rdr GRA) GRA { + return RIE.Memoize[GRA](rdr) +} diff --git a/context/readerioeither/reader.go b/context/readerioeither/reader.go index 904c623..0d41094 100644 --- a/context/readerioeither/reader.go +++ b/context/readerioeither/reader.go @@ -213,3 +213,10 @@ func MonadAlt[A any](first ReaderIOEither[A], second L.Lazy[ReaderIOEither[A]]) func Alt[A any](second L.Lazy[ReaderIOEither[A]]) func(ReaderIOEither[A]) ReaderIOEither[A] { return G.Alt(second) } + +// Memoize computes the value of the provided [ReaderIOEither] monad lazily but exactly once +// The context used to compute the value is the context of the first call, so do not use this +// method if the value has a functional dependency on the content of the context +func Memoize[A any](rdr ReaderIOEither[A]) ReaderIOEither[A] { + return G.Memoize[ReaderIOEither[A]](rdr) +} diff --git a/io/io.go b/io/io.go index dd92c72..0e0b71e 100644 --- a/io/io.go +++ b/io/io.go @@ -84,7 +84,7 @@ func Flatten[A any](mma IO[IO[A]]) IO[A] { return G.Flatten(mma) } -// 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] { return G.Memoize(ma) } diff --git a/readerio/generic/reader.go b/readerio/generic/reader.go index 455a6c4..8321dc2 100644 --- a/readerio/generic/reader.go +++ b/readerio/generic/reader.go @@ -16,6 +16,8 @@ package generic import ( + "sync" + F "github.com/IBM/fp-go/function" FR "github.com/IBM/fp-go/internal/fromreader" "github.com/IBM/fp-go/internal/readert" @@ -99,3 +101,26 @@ func Defer[GEA ~func(E) GA, GA ~func() A, E, A any](gen func() GEA) GEA { } } } + +// Memoize computes the value of the provided reader monad lazily but exactly once +// The context used to compute the value is the context of the first call, so do not use this +// method if the value has a functional dependency on the content of the context +func Memoize[GEA ~func(E) GA, GA ~func() A, E, A any](rdr GEA) GEA { + // synchronization primitives + var once sync.Once + var result A + // callback + gen := func(e E) func() { + return func() { + result = rdr(e)() + } + } + // returns our memoized wrapper + return func(e E) GA { + io := gen(e) + return func() A { + once.Do(io) + return result + } + } +} diff --git a/readerio/reader.go b/readerio/reader.go index 4b00fbc..10641da 100644 --- a/readerio/reader.go +++ b/readerio/reader.go @@ -75,3 +75,10 @@ func ChainIOK[E, A, B any](f func(A) IO.IO[B]) func(ReaderIO[E, A]) ReaderIO[E, func Defer[E, A any](gen func() ReaderIO[E, A]) ReaderIO[E, A] { return G.Defer[ReaderIO[E, A]](gen) } + +// Memoize computes the value of the provided [ReaderIO] monad lazily but exactly once +// The context used to compute the value is the context of the first call, so do not use this +// method if the value has a functional dependency on the content of the context +func Memoize[E, A any](rdr ReaderIO[E, A]) ReaderIO[E, A] { + return G.Memoize[ReaderIO[E, A]](rdr) +} diff --git a/readerioeither/generic/reader.go b/readerioeither/generic/reader.go index 2adc700..a08a947 100644 --- a/readerioeither/generic/reader.go +++ b/readerioeither/generic/reader.go @@ -406,3 +406,11 @@ func TryCatch[GEA ~func(R) GA, GA ~func() ET.Either[E, A], R, E, A any](f func(R return IOE.TryCatch[GA](f(r), onThrow) } } + +// Memoize computes the value of the provided monad lazily but exactly once +// The context used to compute the value is the context of the first call, so do not use this +// method if the value has a functional dependency on the content of the context +func Memoize[ + GEA ~func(R) GIOA, GIOA ~func() ET.Either[E, A], R, E, A any](rdr GEA) GEA { + return G.Memoize[GEA](rdr) +} diff --git a/readerioeither/reader.go b/readerioeither/reader.go index 7cdec44..79ba7c0 100644 --- a/readerioeither/reader.go +++ b/readerioeither/reader.go @@ -264,3 +264,11 @@ func MonadAlt[R, E, A any](first ReaderIOEither[R, E, A], second L.Lazy[ReaderIO func Alt[R, E, A any](second L.Lazy[ReaderIOEither[R, E, A]]) func(ReaderIOEither[R, E, A]) ReaderIOEither[R, E, A] { return G.Alt(second) } + +// Memoize computes the value of the provided [ReaderIOEither] monad lazily but exactly once +// The context used to compute the value is the context of the first call, so do not use this +// method if the value has a functional dependency on the content of the context +func Memoize[ + R, E, A any](rdr ReaderIOEither[R, E, A]) ReaderIOEither[R, E, A] { + return G.Memoize[ReaderIOEither[R, E, A]](rdr) +}