From bf04dad87c92cb7bc17615496990967ce3494805 Mon Sep 17 00:00:00 2001 From: "Dr. Carsten Leue" Date: Sat, 15 Jul 2023 23:27:45 +0200 Subject: [PATCH] fix: add missing chain methods Signed-off-by: Dr. Carsten Leue --- .gitignore | 2 ++ context/readerioeither/reader.go | 24 ++++++++++++++ internal/fromeither/either.go | 18 +++++++++++ io/generic/logging.go | 13 +++----- io/logging.go | 4 +-- io/logging_test.go | 2 +- readerioeither/generic/reader.go | 54 +++++++++++++++++++++++++++---- readerioeither/reader.go | 24 ++++++++++++++ samples/readfile/file.json | 3 ++ samples/readfile/readfile_test.go | 31 ++++++++++++++++++ 10 files changed, 157 insertions(+), 18 deletions(-) create mode 100644 samples/readfile/file.json create mode 100644 samples/readfile/readfile_test.go diff --git a/.gitignore b/.gitignore index 55bf27e..4598b32 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ fp-go.exe +main.exe +build/ \ No newline at end of file diff --git a/context/readerioeither/reader.go b/context/readerioeither/reader.go index 1e3d623..bb52269 100644 --- a/context/readerioeither/reader.go +++ b/context/readerioeither/reader.go @@ -55,6 +55,14 @@ func Chain[A, B any](f func(A) ReaderIOEither[B]) func(ReaderIOEither[A]) Reader return RIE.Chain[ReaderIOEither[A]](f) } +func MonadChainFirst[A, B any](ma ReaderIOEither[A], f func(A) ReaderIOEither[B]) ReaderIOEither[A] { + return RIE.MonadChainFirst(ma, f) +} + +func ChainFirst[A, B any](f func(A) ReaderIOEither[B]) func(ReaderIOEither[A]) ReaderIOEither[A] { + return RIE.ChainFirst[ReaderIOEither[A]](f) +} + func Of[A any](a A) ReaderIOEither[A] { return RIE.Of[ReaderIOEither[A]](a) } @@ -145,6 +153,14 @@ func ChainEitherK[A, B any](f func(A) ET.Either[error, B]) func(ma ReaderIOEithe return RIE.ChainEitherK[ReaderIOEither[A], ReaderIOEither[B]](f) } +func MonadChainFirstEitherK[A, B any](ma ReaderIOEither[A], f func(A) ET.Either[error, B]) ReaderIOEither[A] { + return RIE.MonadChainFirstEitherK[ReaderIOEither[A]](ma, f) +} + +func ChainFirstEitherK[A, B any](f func(A) ET.Either[error, B]) func(ma ReaderIOEither[A]) ReaderIOEither[A] { + return RIE.ChainFirstEitherK[ReaderIOEither[A]](f) +} + func ChainOptionK[A, B any](onNone func() error) func(func(A) O.Option[B]) func(ReaderIOEither[A]) ReaderIOEither[B] { return RIE.ChainOptionK[ReaderIOEither[A], ReaderIOEither[B]](onNone) } @@ -175,6 +191,14 @@ func ChainIOK[A, B any](f func(A) IO.IO[B]) func(ma ReaderIOEither[A]) ReaderIOE return RIE.ChainIOK[ReaderIOEither[A], ReaderIOEither[B]](f) } +func MonadChainFirstIOK[A, B any](ma ReaderIOEither[A], f func(A) IO.IO[B]) ReaderIOEither[A] { + return RIE.MonadChainFirstIOK[ReaderIOEither[A]](ma, f) +} + +func ChainFirstIOK[A, B any](f func(A) IO.IO[B]) func(ma ReaderIOEither[A]) ReaderIOEither[A] { + return RIE.ChainFirstIOK[ReaderIOEither[A]](f) +} + func ChainIOEitherK[A, B any](f func(A) IOE.IOEither[error, B]) func(ma ReaderIOEither[A]) ReaderIOEither[B] { return RIE.ChainIOEitherK[ReaderIOEither[A], ReaderIOEither[B]](f) } diff --git a/internal/fromeither/either.go b/internal/fromeither/either.go index 3c2f202..1059cc2 100644 --- a/internal/fromeither/either.go +++ b/internal/fromeither/either.go @@ -3,6 +3,7 @@ package fromeither import ( ET "github.com/ibm/fp-go/either" F "github.com/ibm/fp-go/function" + C "github.com/ibm/fp-go/internal/chain" O "github.com/ibm/fp-go/option" ) @@ -51,3 +52,20 @@ func ChainOptionK[A, E, B, HKTEA, HKTEB any]( ) func(f func(A) O.Option[B]) func(ma HKTEA) HKTEB { return F.Flow2(FromOptionK[E, A](fromEither, onNone), F.Bind1st(F.Bind2nd[HKTEA, func(A) HKTEB, HKTEB], mchain)) } + +func MonadChainFirstEitherK[A, E, B, HKTEA, HKTEB any]( + mchain func(HKTEA, func(A) HKTEA) HKTEA, + mmap func(HKTEB, func(B) A) HKTEA, + fromEither func(ET.Either[E, B]) HKTEB, + ma HKTEA, + f func(A) ET.Either[E, B]) HKTEA { + return C.MonadChainFirst(mchain, mmap, ma, F.Flow2(f, fromEither)) +} + +func ChainFirstEitherK[A, E, B, HKTEA, HKTEB any]( + mchain func(HKTEA, func(A) HKTEA) HKTEA, + mmap func(HKTEB, func(B) A) HKTEA, + fromEither func(ET.Either[E, B]) HKTEB, + f func(A) ET.Either[E, B]) func(HKTEA) HKTEA { + return C.ChainFirst(mchain, mmap, F.Flow2(f, fromEither)) +} diff --git a/io/generic/logging.go b/io/generic/logging.go index 91a8dab..da45502 100644 --- a/io/generic/logging.go +++ b/io/generic/logging.go @@ -17,13 +17,10 @@ func Logger[GA ~func() any, A any](loggers ...*log.Logger) func(string) func(A) } } -func Logf[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(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) + }) } } diff --git a/io/logging.go b/io/logging.go index 83b6515..ca33f66 100644 --- a/io/logging.go +++ b/io/logging.go @@ -13,6 +13,6 @@ func Logger[A any](loggers ...*log.Logger) func(string) func(A) IO[any] { // 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](loggers ...*log.Logger) func(string) func(A) IO[any] { - return G.Logf[IO[any], A](loggers...) +func Logf[A any](prefix string) func(A) IO[any] { + return G.Logf[IO[any], A](prefix) } diff --git a/io/logging_test.go b/io/logging_test.go index bb3c564..870f2dc 100644 --- a/io/logging_test.go +++ b/io/logging_test.go @@ -17,7 +17,7 @@ func TestLogger(t *testing.T) { func TestLogf(t *testing.T) { - l := Logf[int]() + l := Logf[int] lio := l("Value is %d") diff --git a/readerioeither/generic/reader.go b/readerioeither/generic/reader.go index c6db622..a8b9b03 100644 --- a/readerioeither/generic/reader.go +++ b/readerioeither/generic/reader.go @@ -3,12 +3,12 @@ package generic import ( ET "github.com/ibm/fp-go/either" F "github.com/ibm/fp-go/function" + C "github.com/ibm/fp-go/internal/chain" "github.com/ibm/fp-go/internal/eithert" FE "github.com/ibm/fp-go/internal/fromeither" FIO "github.com/ibm/fp-go/internal/fromio" FIOE "github.com/ibm/fp-go/internal/fromioeither" FR "github.com/ibm/fp-go/internal/fromreader" - GIO "github.com/ibm/fp-go/ioeither/generic" IOE "github.com/ibm/fp-go/ioeither/generic" O "github.com/ibm/fp-go/option" RD "github.com/ibm/fp-go/reader/generic" @@ -48,6 +48,18 @@ func Chain[GEA ~func(R) GIOA, GEB ~func(R) GIOB, GIOA ~func() ET.Either[E, A], G return F.Bind2nd(MonadChain[GEA, GEB, GIOA, GIOB, R, E, A, B], f) } +func MonadChainFirst[GEA ~func(R) GIOA, GEB ~func(R) GIOB, GIOA ~func() ET.Either[E, A], GIOB ~func() ET.Either[E, B], R, E, A, B any](fa GEA, f func(A) GEB) GEA { + return C.MonadChainFirst( + MonadChain[GEA, GEA, GIOA, GIOA, R, E, A, A], + MonadMap[GEB, GEA, GIOB, GIOA, R, E, B, A], + fa, + f) +} + +func ChainFirst[GEA ~func(R) GIOA, GEB ~func(R) GIOB, GIOA ~func() ET.Either[E, A], GIOB ~func() ET.Either[E, B], R, E, A, B any](f func(A) GEB) func(fa GEA) GEA { + return F.Bind2nd(MonadChainFirst[GEA, GEB, GIOA, GIOB, R, E, A, B], f) +} + func MonadChainEitherK[GEA ~func(R) GIOA, GEB ~func(R) GIOB, GIOA ~func() ET.Either[E, A], GIOB ~func() ET.Either[E, B], R, E, A, B any](ma GEA, f func(A) ET.Either[E, B]) GEB { return FE.MonadChainEitherK( MonadChain[GEA, GEB, GIOA, GIOB, R, E, A, B], @@ -61,6 +73,34 @@ func ChainEitherK[GEA ~func(R) GIOA, GEB ~func(R) GIOB, GIOA ~func() ET.Either[E return F.Bind2nd(MonadChainEitherK[GEA, GEB, GIOA, GIOB, R, E, A, B], f) } +func MonadChainFirstEitherK[GEA ~func(R) GIOA, GIOA ~func() ET.Either[E, A], R, E, A, B any](ma GEA, f func(A) ET.Either[E, B]) GEA { + return FE.MonadChainFirstEitherK( + MonadChain[GEA, GEA, GIOA, GIOA, R, E, A, A], + MonadMap[func(R) func() ET.Either[E, B], GEA, func() ET.Either[E, B], GIOA, R, E, B, A], + FromEither[func(R) func() ET.Either[E, B], func() ET.Either[E, B], R, E, B], + ma, + f, + ) +} + +func ChainFirstEitherK[GEA ~func(R) GIOA, GIOA ~func() ET.Either[E, A], R, E, A, B any](f func(A) ET.Either[E, B]) func(ma GEA) GEA { + return F.Bind2nd(MonadChainFirstEitherK[GEA, GIOA, R, E, A, B], f) +} + +func MonadChainFirstIOK[GEA ~func(R) GIOA, GIOA ~func() ET.Either[E, A], GIO ~func() B, R, E, A, B any](ma GEA, f func(A) GIO) GEA { + return FIO.MonadChainFirstIOK( + MonadChain[GEA, GEA, GIOA, GIOA, R, E, A, A], + MonadMap[func(R) func() ET.Either[E, B], GEA, func() ET.Either[E, B], GIOA, R, E, B, A], + FromIO[func(R) func() ET.Either[E, B], func() ET.Either[E, B], GIO, R, E, B], + ma, + f, + ) +} + +func ChainFirstIOK[GEA ~func(R) GIOA, GIOA ~func() ET.Either[E, A], GIO ~func() B, R, E, A, B any](f func(A) GIO) func(GEA) GEA { + return F.Bind2nd(MonadChainFirstIOK[GEA, GIOA, GIO, R, E, A, B], f) +} + func MonadChainReaderK[GEA ~func(R) GIOA, GEB ~func(R) GIOB, GIOA ~func() ET.Either[E, A], GIOB ~func() ET.Either[E, B], GB ~func(R) B, R, E, A, B any](ma GEA, f func(A) GB) GEB { return FR.MonadChainReaderK( MonadChain[GEA, GEB, GIOA, GIOB, R, E, A, B], @@ -166,11 +206,11 @@ func FromEither[GEA ~func(R) GIOA, GIOA ~func() ET.Either[E, A], R, E, A any](t } func RightReader[GA ~func(R) A, GEA ~func(R) GIOA, GIOA ~func() ET.Either[E, A], R, E, A any](ma GA) GEA { - return F.Flow2(ma, GIO.Right[GIOA, E, A]) + return F.Flow2(ma, IOE.Right[GIOA, E, A]) } func LeftReader[GE ~func(R) E, GEA ~func(R) GIOA, GIOA ~func() ET.Either[E, A], R, E, A any](ma GE) GEA { - return F.Flow2(ma, GIO.Left[GIOA, E, A]) + return F.Flow2(ma, IOE.Left[GIOA, E, A]) } func FromReader[GA ~func(R) A, GEA ~func(R) GIOA, GIOA ~func() ET.Either[E, A], R, E, A any](ma GA) GEA { @@ -204,11 +244,11 @@ func LeftReaderIO[GEA ~func(R) GIOA, GIOA ~func() ET.Either[E, A], GRIO ~func(R) } func RightIO[GEA ~func(R) GIOA, GIOA ~func() ET.Either[E, A], GR ~func() A, R, E, A any](ma GR) GEA { - return F.Pipe2(ma, GIO.RightIO[GIOA, GR, E, A], FromIOEither[GEA, GIOA, R, E, A]) + return F.Pipe2(ma, IOE.RightIO[GIOA, GR, E, A], FromIOEither[GEA, GIOA, R, E, A]) } func LeftIO[GEA ~func(R) GIOA, GIOA ~func() ET.Either[E, A], GR ~func() E, R, E, A any](ma GR) GEA { - return F.Pipe2(ma, GIO.LeftIO[GIOA, GR, E, A], FromIOEither[GEA, GIOA, R, E, A]) + return F.Pipe2(ma, IOE.LeftIO[GIOA, GR, E, A], FromIOEither[GEA, GIOA, R, E, A]) } func FromIO[GEA ~func(R) GIOA, GIOA ~func() ET.Either[E, A], GR ~func() A, R, E, A any](ma GR) GEA { @@ -216,7 +256,7 @@ func FromIO[GEA ~func(R) GIOA, GIOA ~func() ET.Either[E, A], GR ~func() A, R, E, } func FromReaderEither[GA ~func(R) ET.Either[E, A], GEA ~func(R) GIOA, GIOA ~func() ET.Either[E, A], R, E, A any](ma GA) GEA { - return F.Flow2(ma, GIO.FromEither[GIOA, E, A]) + return F.Flow2(ma, IOE.FromEither[GIOA, E, A]) } func Ask[GER ~func(R) GIOR, GIOR ~func() ET.Either[E, R], R, E any]() GER { @@ -267,7 +307,7 @@ func BiMap[GA ~func(R) GE1A, GB ~func(R) GE2B, GE1A ~func() ET.Either[E1, A], GE // Swap changes the order of type parameters func Swap[GREA ~func(R) GEA, GRAE ~func(R) GAE, GEA ~func() ET.Either[E, A], GAE ~func() ET.Either[A, E], R, E, A any](val GREA) GRAE { - return RD.MonadMap[GREA, GRAE, R, GEA, GAE](val, GIO.Swap[GEA, GAE]) + return RD.MonadMap[GREA, GRAE, R, GEA, GAE](val, IOE.Swap[GEA, GAE]) } // Defer creates an IO by creating a brand new IO via a generator function, each time diff --git a/readerioeither/reader.go b/readerioeither/reader.go index ddec7b4..887f3bd 100644 --- a/readerioeither/reader.go +++ b/readerioeither/reader.go @@ -54,6 +54,10 @@ func MonadChain[R, E, A, B any](fa ReaderIOEither[R, E, A], f func(A) ReaderIOEi return G.MonadChain(fa, f) } +func MonadChainFirst[R, E, A, B any](fa ReaderIOEither[R, E, A], f func(A) ReaderIOEither[R, E, B]) ReaderIOEither[R, E, A] { + return G.MonadChainFirst(fa, f) +} + func MonadChainEitherK[R, E, A, B any](ma ReaderIOEither[R, E, A], f func(A) ET.Either[E, B]) ReaderIOEither[R, E, B] { return G.MonadChainEitherK[ReaderIOEither[R, E, A], ReaderIOEither[R, E, B]](ma, f) } @@ -62,6 +66,14 @@ func ChainEitherK[R, E, A, B any](f func(A) ET.Either[E, B]) func(ma ReaderIOEit return G.ChainEitherK[ReaderIOEither[R, E, A], ReaderIOEither[R, E, B]](f) } +func MonadChainFirstEitherK[R, E, A, B any](ma ReaderIOEither[R, E, A], f func(A) ET.Either[E, B]) ReaderIOEither[R, E, A] { + return G.MonadChainFirstEitherK[ReaderIOEither[R, E, A]](ma, f) +} + +func ChainFirstEitherK[R, E, A, B any](f func(A) ET.Either[E, B]) func(ma ReaderIOEither[R, E, A]) ReaderIOEither[R, E, A] { + return G.ChainFirstEitherK[ReaderIOEither[R, E, A]](f) +} + func MonadChainReaderK[R, E, A, B any](ma ReaderIOEither[R, E, A], f func(A) RD.Reader[R, B]) ReaderIOEither[R, E, B] { return G.MonadChainReaderK[ReaderIOEither[R, E, A], ReaderIOEither[R, E, B]](ma, f) } @@ -86,6 +98,14 @@ func ChainIOK[R, E, A, B any](f func(A) io.IO[B]) func(ma ReaderIOEither[R, E, A return G.ChainIOK[ReaderIOEither[R, E, A], ReaderIOEither[R, E, B]](f) } +func MonadChainFirstIOK[R, E, A, B any](ma ReaderIOEither[R, E, A], f func(A) io.IO[B]) ReaderIOEither[R, E, A] { + return G.MonadChainFirstIOK[ReaderIOEither[R, E, A]](ma, f) +} + +func ChainFirstIOK[R, E, A, B any](f func(A) io.IO[B]) func(ma ReaderIOEither[R, E, A]) ReaderIOEither[R, E, A] { + return G.ChainFirstIOK[ReaderIOEither[R, E, A]](f) +} + func ChainOptionK[R, E, A, B any](onNone func() E) func(func(A) O.Option[B]) func(ReaderIOEither[R, E, A]) ReaderIOEither[R, E, B] { return G.ChainOptionK[ReaderIOEither[R, E, A], ReaderIOEither[R, E, B]](onNone) } @@ -102,6 +122,10 @@ func Chain[R, E, A, B any](f func(A) ReaderIOEither[R, E, B]) func(fa ReaderIOEi return G.Chain[ReaderIOEither[R, E, A]](f) } +func ChainFirst[R, E, A, B any](f func(A) ReaderIOEither[R, E, B]) func(fa ReaderIOEither[R, E, A]) ReaderIOEither[R, E, A] { + return G.ChainFirst[ReaderIOEither[R, E, A]](f) +} + func Right[R, E, A any](a A) ReaderIOEither[R, E, A] { return G.Right[ReaderIOEither[R, E, A]](a) } diff --git a/samples/readfile/file.json b/samples/readfile/file.json new file mode 100644 index 0000000..6f6b333 --- /dev/null +++ b/samples/readfile/file.json @@ -0,0 +1,3 @@ +{ + "data": "Carsten" +} \ No newline at end of file diff --git a/samples/readfile/readfile_test.go b/samples/readfile/readfile_test.go new file mode 100644 index 0000000..73f1615 --- /dev/null +++ b/samples/readfile/readfile_test.go @@ -0,0 +1,31 @@ +package readfile + +import ( + "context" + "testing" + + R "github.com/ibm/fp-go/context/readerioeither" + "github.com/ibm/fp-go/context/readerioeither/file" + E "github.com/ibm/fp-go/either" + F "github.com/ibm/fp-go/function" + IO "github.com/ibm/fp-go/io" + J "github.com/ibm/fp-go/json" + "github.com/stretchr/testify/assert" +) + +type RecordType struct { + Data string `json:"data"` +} + +func TestReadSingleFile(t *testing.T) { + + data := F.Pipe2( + file.ReadFile("./file.json"), + R.ChainEitherK(J.Unmarshal[RecordType]), + R.ChainFirstIOK(IO.Logf[RecordType]("Log: %v")), + ) + + result := data(context.Background()) + + assert.Equal(t, E.Of[error](RecordType{"Carsten"}), result()) +}