1
0
mirror of https://github.com/IBM/fp-go.git synced 2025-08-10 22:31:32 +02:00

fix: refactor Writer monad

Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
This commit is contained in:
Dr. Carsten Leue
2024-02-13 10:44:57 +01:00
parent d0e4984b60
commit 01786a054b
10 changed files with 302 additions and 149 deletions

View File

@@ -17,20 +17,22 @@ package writer
import ( import (
M "github.com/IBM/fp-go/monoid" M "github.com/IBM/fp-go/monoid"
SG "github.com/IBM/fp-go/semigroup"
G "github.com/IBM/fp-go/writer/generic" G "github.com/IBM/fp-go/writer/generic"
) )
// Bind creates an empty context of type [S] to be used with the [Bind] operation // Bind creates an empty context of type [S] to be used with the [Bind] operation
func Do[S, W any](m M.Monoid[W]) func(S) Writer[W, S] { func Do[S, W any](m M.Monoid[W], s S) Writer[W, S] {
return G.Do[Writer[W, S], W, S](m) return G.Do[Writer[W, S], W, S](m, s)
} }
// Bind attaches the result of a computation to a context [S1] to produce a context [S2] // Bind attaches the result of a computation to a context [S1] to produce a context [S2]
func Bind[S1, S2, T, W any]( func Bind[S1, S2, T, W any](
s SG.Semigroup[W],
setter func(T) func(S1) S2, setter func(T) func(S1) S2,
f func(S1) Writer[W, T], f func(S1) Writer[W, T],
) func(Writer[W, S1]) Writer[W, S2] { ) func(Writer[W, S1]) Writer[W, S2] {
return G.Bind[Writer[W, S1], Writer[W, S2], Writer[W, T], W, S1, S2, T](setter, f) return G.Bind[Writer[W, S1], Writer[W, S2], Writer[W, T], W, S1, S2, T](s, setter, f)
} }
// Let attaches the result of a computation to a context [S1] to produce a context [S2] // Let attaches the result of a computation to a context [S1] to produce a context [S2]
@@ -58,8 +60,9 @@ func BindTo[W, S1, T any](
// ApS attaches a value to a context [S1] to produce a context [S2] by considering the context and the value concurrently // 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, W any]( func ApS[S1, S2, T, W any](
s SG.Semigroup[W],
setter func(T) func(S1) S2, setter func(T) func(S1) S2,
fa Writer[W, T], fa Writer[W, T],
) func(Writer[W, S1]) Writer[W, S2] { ) func(Writer[W, S1]) Writer[W, S2] {
return G.ApS[Writer[W, S1], Writer[W, S2], Writer[W, T], W, S1, S2, T](setter, fa) return G.ApS[Writer[W, S1], Writer[W, S2], Writer[W, T], W, S1, S2, T](s, setter, fa)
} }

View File

@@ -34,33 +34,33 @@ var (
) )
func getLastName(s utils.Initial) Writer[[]string, string] { func getLastName(s utils.Initial) Writer[[]string, string] {
return Of[string](monoid)("Doe") return Of[string](monoid, "Doe")
} }
func getGivenName(s utils.WithLastName) Writer[[]string, string] { func getGivenName(s utils.WithLastName) Writer[[]string, string] {
return Of[string](monoid)("John") return Of[string](monoid, "John")
} }
func TestBind(t *testing.T) { func TestBind(t *testing.T) {
res := F.Pipe3( res := F.Pipe3(
Do[utils.Initial](monoid)(utils.Empty), Do[utils.Initial](monoid, utils.Empty),
Bind(utils.SetLastName, getLastName), Bind(sg, utils.SetLastName, getLastName),
Bind(utils.SetGivenName, getGivenName), Bind(sg, utils.SetGivenName, getGivenName),
Map[[]string](utils.GetFullName), Map[[]string](utils.GetFullName),
) )
assert.True(t, eq.Equals(res, Of[string](monoid)("John Doe"))) assert.True(t, eq.Equals(res, Of[string](monoid, "John Doe")))
} }
func TestApS(t *testing.T) { func TestApS(t *testing.T) {
res := F.Pipe3( res := F.Pipe3(
Do[utils.Initial](monoid)(utils.Empty), Do[utils.Initial](monoid, utils.Empty),
ApS(utils.SetLastName, Of[string](monoid)("Doe")), ApS(sg, utils.SetLastName, Of[string](monoid, "Doe")),
ApS(utils.SetGivenName, Of[string](monoid)("John")), ApS(sg, utils.SetGivenName, Of[string](monoid, "John")),
Map[[]string](utils.GetFullName), Map[[]string](utils.GetFullName),
) )
assert.True(t, eq.Equals(res, Of[string](monoid)("John Doe"))) assert.True(t, eq.Equals(res, Of[string](monoid, "John Doe")))
} }

View File

@@ -16,26 +16,28 @@
package generic package generic
import ( import (
FCT "github.com/IBM/fp-go/function"
"github.com/IBM/fp-go/internal/apply" "github.com/IBM/fp-go/internal/apply"
C "github.com/IBM/fp-go/internal/chain" C "github.com/IBM/fp-go/internal/chain"
F "github.com/IBM/fp-go/internal/functor" F "github.com/IBM/fp-go/internal/functor"
M "github.com/IBM/fp-go/monoid" M "github.com/IBM/fp-go/monoid"
P "github.com/IBM/fp-go/pair"
SG "github.com/IBM/fp-go/semigroup" SG "github.com/IBM/fp-go/semigroup"
T "github.com/IBM/fp-go/tuple"
) )
// Bind creates an empty context of type [S] to be used with the [Bind] operation // Bind creates an empty context of type [S] to be used with the [Bind] operation
func Do[GS ~func() T.Tuple3[S, W, SG.Semigroup[W]], W, S any](m M.Monoid[W]) func(S) GS { func Do[GS ~func() P.Pair[S, W], W, S any](m M.Monoid[W], s S) GS {
return Of[GS, W, S](m) return Of[GS, W, S](m, s)
} }
// Bind attaches the result of a computation to a context [S1] to produce a context [S2] // Bind attaches the result of a computation to a context [S1] to produce a context [S2]
func Bind[GS1 ~func() T.Tuple3[S1, W, SG.Semigroup[W]], GS2 ~func() T.Tuple3[S2, W, SG.Semigroup[W]], GT ~func() T.Tuple3[A, W, SG.Semigroup[W]], W, S1, S2, A any]( func Bind[GS1 ~func() P.Pair[S1, W], GS2 ~func() P.Pair[S2, W], GT ~func() P.Pair[A, W], W, S1, S2, A any](
s SG.Semigroup[W],
setter func(A) func(S1) S2, setter func(A) func(S1) S2,
f func(S1) GT, f func(S1) GT,
) func(GS1) GS2 { ) func(GS1) GS2 {
return C.Bind( return C.Bind(
Chain[GS2, GS1, func(S1) GS2, W, S1, S2], FCT.Bind1st(Chain[GS2, GS1, func(S1) GS2, W, S1, S2], s),
Map[GS2, GT, func(A) S2, W, A, S2], Map[GS2, GT, func(A) S2, W, A, S2],
setter, setter,
f, f,
@@ -43,7 +45,7 @@ func Bind[GS1 ~func() T.Tuple3[S1, W, SG.Semigroup[W]], GS2 ~func() T.Tuple3[S2,
} }
// Let attaches the result of a computation to a context [S1] to produce a context [S2] // Let attaches the result of a computation to a context [S1] to produce a context [S2]
func Let[GS1 ~func() T.Tuple3[S1, W, SG.Semigroup[W]], GS2 ~func() T.Tuple3[S2, W, SG.Semigroup[W]], W, S1, S2, A any]( func Let[GS1 ~func() P.Pair[S1, W], GS2 ~func() P.Pair[S2, W], W, S1, S2, A any](
key func(A) func(S1) S2, key func(A) func(S1) S2,
f func(S1) A, f func(S1) A,
) func(GS1) GS2 { ) func(GS1) GS2 {
@@ -55,7 +57,7 @@ func Let[GS1 ~func() T.Tuple3[S1, W, SG.Semigroup[W]], GS2 ~func() T.Tuple3[S2,
} }
// LetTo attaches the a value to a context [S1] to produce a context [S2] // LetTo attaches the a value to a context [S1] to produce a context [S2]
func LetTo[GS1 ~func() T.Tuple3[S1, W, SG.Semigroup[W]], GS2 ~func() T.Tuple3[S2, W, SG.Semigroup[W]], W, S1, S2, B any]( func LetTo[GS1 ~func() P.Pair[S1, W], GS2 ~func() P.Pair[S2, W], W, S1, S2, B any](
key func(B) func(S1) S2, key func(B) func(S1) S2,
b B, b B,
) func(GS1) GS2 { ) func(GS1) GS2 {
@@ -67,7 +69,7 @@ func LetTo[GS1 ~func() T.Tuple3[S1, W, SG.Semigroup[W]], GS2 ~func() T.Tuple3[S2
} }
// BindTo initializes a new state [S1] from a value [T] // BindTo initializes a new state [S1] from a value [T]
func BindTo[GS1 ~func() T.Tuple3[S1, W, SG.Semigroup[W]], GT ~func() T.Tuple3[A, W, SG.Semigroup[W]], W, S1, A any]( func BindTo[GS1 ~func() P.Pair[S1, W], GT ~func() P.Pair[A, W], W, S1, A any](
setter func(A) S1, setter func(A) S1,
) func(GT) GS1 { ) func(GT) GS1 {
return C.BindTo( return C.BindTo(
@@ -77,13 +79,14 @@ func BindTo[GS1 ~func() T.Tuple3[S1, W, SG.Semigroup[W]], GT ~func() T.Tuple3[A,
} }
// ApS attaches a value to a context [S1] to produce a context [S2] by considering the context and the value concurrently // ApS attaches a value to a context [S1] to produce a context [S2] by considering the context and the value concurrently
func ApS[GS1 ~func() T.Tuple3[S1, W, SG.Semigroup[W]], GS2 ~func() T.Tuple3[S2, W, SG.Semigroup[W]], GT ~func() T.Tuple3[A, W, SG.Semigroup[W]], W, S1, S2, A any]( func ApS[GS1 ~func() P.Pair[S1, W], GS2 ~func() P.Pair[S2, W], GT ~func() P.Pair[A, W], W, S1, S2, A any](
s SG.Semigroup[W],
setter func(A) func(S1) S2, setter func(A) func(S1) S2,
fa GT, fa GT,
) func(GS1) GS2 { ) func(GS1) GS2 {
return apply.ApS( return apply.ApS(
Ap[GS2, func() T.Tuple3[func(A) S2, W, SG.Semigroup[W]], GT, W, A, S2], FCT.Bind1st(Ap[GS2, func() P.Pair[func(A) S2, W], GT, W, A, S2], s),
Map[func() T.Tuple3[func(A) S2, W, SG.Semigroup[W]], GS1, func(S1) func(A) S2], Map[func() P.Pair[func(A) S2, W], GS1, func(S1) func(A) S2],
setter, setter,
fa, fa,
) )

View File

@@ -17,21 +17,18 @@ package generic
import ( import (
EQ "github.com/IBM/fp-go/eq" EQ "github.com/IBM/fp-go/eq"
SG "github.com/IBM/fp-go/semigroup" P "github.com/IBM/fp-go/pair"
T "github.com/IBM/fp-go/tuple"
) )
// Constructs an equal predicate for a [Writer] // Constructs an equal predicate for a [Writer]
func Eq[GA ~func() T.Tuple3[A, W, SG.Semigroup[W]], W, A any](w EQ.Eq[W], a EQ.Eq[A]) EQ.Eq[GA] { func Eq[GA ~func() P.Pair[A, W], W, A any](w EQ.Eq[W], a EQ.Eq[A]) EQ.Eq[GA] {
eqp := P.Eq(a, w)
return EQ.FromEquals(func(l, r GA) bool { return EQ.FromEquals(func(l, r GA) bool {
ll := l() return eqp.Equals(l(), r())
rr := r()
return a.Equals(ll.F1, rr.F1) && w.Equals(ll.F2, rr.F2)
}) })
} }
// FromStrictEquals constructs an [EQ.Eq] from the canonical comparison function // FromStrictEquals constructs an [EQ.Eq] from the canonical comparison function
func FromStrictEquals[GA ~func() T.Tuple3[A, W, SG.Semigroup[W]], W, A comparable]() EQ.Eq[GA] { func FromStrictEquals[GA ~func() P.Pair[A, W], W, A comparable]() EQ.Eq[GA] {
return Eq[GA](EQ.FromStrictEquals[W](), EQ.FromStrictEquals[A]()) return Eq[GA](EQ.FromStrictEquals[W](), EQ.FromStrictEquals[A]())
} }

106
writer/generic/monad.go Normal file
View File

@@ -0,0 +1,106 @@
// 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/internal/applicative"
"github.com/IBM/fp-go/internal/functor"
"github.com/IBM/fp-go/internal/monad"
"github.com/IBM/fp-go/internal/pointed"
M "github.com/IBM/fp-go/monoid"
P "github.com/IBM/fp-go/pair"
SG "github.com/IBM/fp-go/semigroup"
)
type writerPointed[GA ~func() P.Pair[A, W], W, A any] struct {
m M.Monoid[W]
}
type writerFunctor[GB ~func() P.Pair[B, W], GA ~func() P.Pair[A, W], W, A, B any] struct{}
type writerApplicative[GB ~func() P.Pair[B, W], GAB ~func() P.Pair[func(A) B, W], GA ~func() P.Pair[A, W], W, A, B any] struct {
s SG.Semigroup[W]
m M.Monoid[W]
}
type writerMonad[GB ~func() P.Pair[B, W], GAB ~func() P.Pair[func(A) B, W], GA ~func() P.Pair[A, W], W, A, B any] struct {
s SG.Semigroup[W]
m M.Monoid[W]
}
func (o *writerPointed[GA, W, A]) Of(a A) GA {
return Of[GA](o.m, a)
}
func (o *writerApplicative[GB, GAB, GA, W, A, B]) Of(a A) GA {
return Of[GA](o.m, a)
}
func (o *writerMonad[GB, GAB, GA, W, A, B]) Of(a A) GA {
return Of[GA](o.m, a)
}
func (o *writerFunctor[GB, GA, W, A, B]) Map(f func(A) B) func(GA) GB {
return Map[GB, GA](f)
}
func (o *writerApplicative[GB, GAB, GA, W, A, B]) Map(f func(A) B) func(GA) GB {
return Map[GB, GA](f)
}
func (o *writerMonad[GB, GAB, GA, W, A, B]) Map(f func(A) B) func(GA) GB {
return Map[GB, GA](f)
}
func (o *writerMonad[GB, GAB, GA, W, A, B]) Chain(f func(A) GB) func(GA) GB {
return Chain[GB, GA](o.s, f)
}
func (o *writerApplicative[GB, GAB, GA, W, A, B]) Ap(fa GA) func(GAB) GB {
return Ap[GB, GAB, GA](o.s, fa)
}
func (o *writerMonad[GB, GAB, GA, W, A, B]) Ap(fa GA) func(GAB) GB {
return Ap[GB, GAB, GA](o.s, fa)
}
// Pointed implements the pointed operations for [Writer]
func Pointed[GA ~func() P.Pair[A, W], W, A any](m M.Monoid[W]) pointed.Pointed[A, GA] {
return &writerPointed[GA, W, A]{
m: m,
}
}
// Functor implements the functor operations for [Writer]
func Functor[GB ~func() P.Pair[B, W], GA ~func() P.Pair[A, W], W, A, B any]() functor.Functor[A, B, GA, GB] {
return &writerFunctor[GB, GA, W, A, B]{}
}
// Applicative implements the applicative operations for [Writer]
func Applicative[GB ~func() P.Pair[B, W], GAB ~func() P.Pair[func(A) B, W], GA ~func() P.Pair[A, W], W, A, B any](m M.Monoid[W]) applicative.Applicative[A, B, GA, GB, GAB] {
return &writerApplicative[GB, GAB, GA, W, A, B]{
s: M.ToSemigroup(m),
m: m,
}
}
// Monad implements the monadic operations for [Writer]
func Monad[GB ~func() P.Pair[B, W], GAB ~func() P.Pair[func(A) B, W], GA ~func() P.Pair[A, W], W, A, B any](m M.Monoid[W]) monad.Monad[A, B, GA, GB, GAB] {
return &writerMonad[GB, GAB, GA, W, A, B]{
s: M.ToSemigroup(m),
m: m,
}
}

View File

@@ -21,133 +21,133 @@ import (
FC "github.com/IBM/fp-go/internal/functor" FC "github.com/IBM/fp-go/internal/functor"
IO "github.com/IBM/fp-go/io/generic" IO "github.com/IBM/fp-go/io/generic"
M "github.com/IBM/fp-go/monoid" M "github.com/IBM/fp-go/monoid"
P "github.com/IBM/fp-go/pair"
SG "github.com/IBM/fp-go/semigroup" SG "github.com/IBM/fp-go/semigroup"
T "github.com/IBM/fp-go/tuple"
) )
func Tell[GA ~func() T.Tuple3[any, W, SG.Semigroup[W]], W any](s SG.Semigroup[W]) func(W) GA { func Tell[GA ~func() P.Pair[any, W], W any](w W) GA {
return F.Flow2( return IO.Of[GA](P.MakePair[any](w, w))
F.Bind13of3(T.MakeTuple3[any, W, SG.Semigroup[W]])(nil, s),
IO.Of[GA],
)
} }
func Of[GA ~func() T.Tuple3[A, W, SG.Semigroup[W]], W, A any](m M.Monoid[W]) func(A) GA { func Of[GA ~func() P.Pair[A, W], W, A any](m M.Monoid[W], a A) GA {
return F.Flow2( return IO.Of[GA](P.MakePair(a, m.Empty()))
F.Bind23of3(T.MakeTuple3[A, W, SG.Semigroup[W]])(m.Empty(), M.ToSemigroup(m)),
IO.Of[GA],
)
} }
// Listen modifies the result to include the changes to the accumulator // Listen modifies the result to include the changes to the accumulator
func Listen[GA ~func() T.Tuple3[A, W, SG.Semigroup[W]], GTA ~func() T.Tuple3[T.Tuple2[A, W], W, SG.Semigroup[W]], W, A any](fa GA) GTA { func Listen[GA ~func() P.Pair[A, W], GTA ~func() P.Pair[P.Pair[A, W], W], W, A any](fa GA) GTA {
return func() T.Tuple3[T.Tuple2[A, W], W, SG.Semigroup[W]] { return func() P.Pair[P.Pair[A, W], W] {
t := fa() t := fa()
return T.MakeTuple3(T.MakeTuple2(t.F1, t.F2), t.F2, t.F3) return P.MakePair(t, P.Tail(t))
} }
} }
// Pass applies the returned function to the accumulator // Pass applies the returned function to the accumulator
func Pass[GFA ~func() T.Tuple3[T.Tuple2[A, FCT], W, SG.Semigroup[W]], GA ~func() T.Tuple3[A, W, SG.Semigroup[W]], FCT ~func(W) W, W, A any](fa GFA) GA { func Pass[GFA ~func() P.Pair[P.Pair[A, FCT], W], GA ~func() P.Pair[A, W], FCT ~func(W) W, W, A any](fa GFA) GA {
return func() T.Tuple3[A, W, SG.Semigroup[W]] { return func() P.Pair[A, W] {
t := fa() t := fa()
return T.MakeTuple3(t.F1.F1, t.F1.F2(t.F2), t.F3) a := P.Head(t)
return P.MakePair(P.Head(a), P.Tail(a)(P.Tail(t)))
} }
} }
func MonadMap[GB ~func() T.Tuple3[B, W, SG.Semigroup[W]], GA ~func() T.Tuple3[A, W, SG.Semigroup[W]], FCT ~func(A) B, W, A, B any](fa GA, f FCT) GB { func MonadMap[GB ~func() P.Pair[B, W], GA ~func() P.Pair[A, W], FCT ~func(A) B, W, A, B any](fa GA, f FCT) GB {
return IO.MonadMap[GA, GB](fa, T.Map3(f, F.Identity[W], F.Identity[SG.Semigroup[W]])) return IO.MonadMap[GA, GB](fa, P.Map[W](f))
} }
func Map[GB ~func() T.Tuple3[B, W, SG.Semigroup[W]], GA ~func() T.Tuple3[A, W, SG.Semigroup[W]], FCT ~func(A) B, W, A, B any](f FCT) func(GA) GB { func Map[GB ~func() P.Pair[B, W], GA ~func() P.Pair[A, W], FCT ~func(A) B, W, A, B any](f FCT) func(GA) GB {
return IO.Map[GA, GB](T.Map3(f, F.Identity[W], F.Identity[SG.Semigroup[W]])) return IO.Map[GA, GB](P.Map[W](f))
} }
func MonadChain[GB ~func() T.Tuple3[B, W, SG.Semigroup[W]], GA ~func() T.Tuple3[A, W, SG.Semigroup[W]], FCT ~func(A) GB, W, A, B any](fa GA, f FCT) GB { func MonadChain[GB ~func() P.Pair[B, W], GA ~func() P.Pair[A, W], FCT ~func(A) GB, W, A, B any](s SG.Semigroup[W], fa GA, f FCT) GB {
return func() T.Tuple3[B, W, SG.Semigroup[W]] { return func() P.Pair[B, W] {
a := fa() a := fa()
b := f(a.F1)() b := f(P.Head(a))()
return T.MakeTuple3(b.F1, b.F3.Concat(a.F2, b.F2), b.F3) return P.MakePair(P.Head(b), s.Concat(P.Tail(a), P.Tail(b)))
} }
} }
func Chain[GB ~func() T.Tuple3[B, W, SG.Semigroup[W]], GA ~func() T.Tuple3[A, W, SG.Semigroup[W]], FCT ~func(A) GB, W, A, B any](f FCT) func(GA) GB { func Chain[GB ~func() P.Pair[B, W], GA ~func() P.Pair[A, W], FCT ~func(A) GB, W, A, B any](s SG.Semigroup[W], f FCT) func(GA) GB {
return F.Bind2nd(MonadChain[GB, GA, FCT, W, A, B], f) return func(fa GA) GB {
return MonadChain(s, fa, f)
}
} }
func MonadAp[GB ~func() T.Tuple3[B, W, SG.Semigroup[W]], GAB ~func() T.Tuple3[func(A) B, W, SG.Semigroup[W]], GA ~func() T.Tuple3[A, W, SG.Semigroup[W]], W, A, B any](fab GAB, fa GA) GB { func MonadAp[GB ~func() P.Pair[B, W], GAB ~func() P.Pair[func(A) B, W], GA ~func() P.Pair[A, W], W, A, B any](s SG.Semigroup[W], fab GAB, fa GA) GB {
return func() T.Tuple3[B, W, SG.Semigroup[W]] { return func() P.Pair[B, W] {
f := fab() f := fab()
a := fa() a := fa()
return T.MakeTuple3(f.F1(a.F1), f.F3.Concat(f.F2, a.F2), f.F3) return P.MakePair(P.Head(f)(P.Head(a)), s.Concat(P.Tail(f), P.Tail(a)))
} }
} }
func Ap[GB ~func() T.Tuple3[B, W, SG.Semigroup[W]], GAB ~func() T.Tuple3[func(A) B, W, SG.Semigroup[W]], GA ~func() T.Tuple3[A, W, SG.Semigroup[W]], W, A, B any](ga GA) func(GAB) GB { func Ap[GB ~func() P.Pair[B, W], GAB ~func() P.Pair[func(A) B, W], GA ~func() P.Pair[A, W], W, A, B any](s SG.Semigroup[W], ga GA) func(GAB) GB {
return F.Bind2nd(MonadAp[GB, GAB, GA], ga) return func(fab GAB) GB {
return MonadAp[GB](s, fab, ga)
}
} }
func MonadChainFirst[GB ~func() T.Tuple3[B, W, SG.Semigroup[W]], GA ~func() T.Tuple3[A, W, SG.Semigroup[W]], FCT ~func(A) GB, W, A, B any](ma GA, f FCT) GA { func MonadChainFirst[GB ~func() P.Pair[B, W], GA ~func() P.Pair[A, W], FCT ~func(A) GB, W, A, B any](s SG.Semigroup[W], ma GA, f FCT) GA {
return C.MonadChainFirst( return C.MonadChainFirst(
MonadChain[GA, GA, func(A) GA], F.Bind1of3(MonadChain[GA, GA, func(A) GA])(s),
MonadMap[GA, GB, func(B) A], MonadMap[GA, GB, func(B) A],
ma, ma,
f, f,
) )
} }
func ChainFirst[GB ~func() T.Tuple3[B, W, SG.Semigroup[W]], GA ~func() T.Tuple3[A, W, SG.Semigroup[W]], FCT ~func(A) GB, W, A, B any](f FCT) func(GA) GA { func ChainFirst[GB ~func() P.Pair[B, W], GA ~func() P.Pair[A, W], FCT ~func(A) GB, W, A, B any](s SG.Semigroup[W], f FCT) func(GA) GA {
return C.ChainFirst( return C.ChainFirst(
Chain[GA, GA, func(A) GA], F.Bind1st(Chain[GA, GA, func(A) GA], s),
Map[GA, GB, func(B) A], Map[GA, GB, func(B) A],
f, f,
) )
} }
func Flatten[GAA ~func() T.Tuple3[GA, W, SG.Semigroup[W]], GA ~func() T.Tuple3[A, W, SG.Semigroup[W]], W, A any](mma GAA) GA { func Flatten[GAA ~func() P.Pair[GA, W], GA ~func() P.Pair[A, W], W, A any](s SG.Semigroup[W], mma GAA) GA {
return MonadChain[GA, GAA, func(GA) GA](mma, F.Identity[GA]) return MonadChain[GA, GAA, func(GA) GA](s, mma, F.Identity[GA])
} }
func Execute[GA ~func() T.Tuple3[A, W, SG.Semigroup[W]], W, A any](fa GA) W { func Execute[GA ~func() P.Pair[A, W], W, A any](fa GA) W {
return fa().F2 return P.Tail(fa())
} }
func Evaluate[GA ~func() T.Tuple3[A, W, SG.Semigroup[W]], W, A any](fa GA) A { func Evaluate[GA ~func() P.Pair[A, W], W, A any](fa GA) A {
return fa().F1 return P.Head(fa())
} }
// MonadCensor modifies the final accumulator value by applying a function // MonadCensor modifies the final accumulator value by applying a function
func MonadCensor[GA ~func() T.Tuple3[A, W, SG.Semigroup[W]], FCT ~func(W) W, W, A any](fa GA, f FCT) GA { func MonadCensor[GA ~func() P.Pair[A, W], FCT ~func(W) W, W, A any](fa GA, f FCT) GA {
return IO.MonadMap[GA, GA](fa, T.Map3(F.Identity[A], f, F.Identity[SG.Semigroup[W]])) return IO.MonadMap[GA, GA](fa, P.MapTail[A](f))
} }
// Censor modifies the final accumulator value by applying a function // Censor modifies the final accumulator value by applying a function
func Censor[GA ~func() T.Tuple3[A, W, SG.Semigroup[W]], FCT ~func(W) W, W, A any](f FCT) func(GA) GA { func Censor[GA ~func() P.Pair[A, W], FCT ~func(W) W, W, A any](f FCT) func(GA) GA {
return IO.Map[GA, GA](T.Map3(F.Identity[A], f, F.Identity[SG.Semigroup[W]])) return IO.Map[GA, GA](P.MapTail[A](f))
} }
// MonadListens projects a value from modifications made to the accumulator during an action // MonadListens projects a value from modifications made to the accumulator during an action
func MonadListens[GA ~func() T.Tuple3[A, W, SG.Semigroup[W]], GAB ~func() T.Tuple3[T.Tuple2[A, B], W, SG.Semigroup[W]], FCT ~func(W) B, W, A, B any](fa GA, f FCT) GAB { func MonadListens[GA ~func() P.Pair[A, W], GAB ~func() P.Pair[P.Pair[A, B], W], FCT ~func(W) B, W, A, B any](fa GA, f FCT) GAB {
return func() T.Tuple3[T.Tuple2[A, B], W, SG.Semigroup[W]] { return func() P.Pair[P.Pair[A, B], W] {
a := fa() a := fa()
return T.MakeTuple3(T.MakeTuple2(a.F1, f(a.F2)), a.F2, a.F3) t := P.Tail(a)
return P.MakePair(P.MakePair(P.Head(a), f(t)), t)
} }
} }
// Listens projects a value from modifications made to the accumulator during an action // Listens projects a value from modifications made to the accumulator during an action
func Listens[GA ~func() T.Tuple3[A, W, SG.Semigroup[W]], GAB ~func() T.Tuple3[T.Tuple2[A, B], W, SG.Semigroup[W]], FCT ~func(W) B, W, A, B any](f FCT) func(GA) GAB { func Listens[GA ~func() P.Pair[A, W], GAB ~func() P.Pair[P.Pair[A, B], W], FCT ~func(W) B, W, A, B any](f FCT) func(GA) GAB {
return F.Bind2nd(MonadListens[GA, GAB, FCT], f) return F.Bind2nd(MonadListens[GA, GAB, FCT], f)
} }
func MonadFlap[FAB ~func(A) B, GFAB ~func() T.Tuple3[FAB, W, SG.Semigroup[W]], GB ~func() T.Tuple3[B, W, SG.Semigroup[W]], W, A, B any](fab GFAB, a A) GB { func MonadFlap[FAB ~func(A) B, GFAB ~func() P.Pair[FAB, W], GB ~func() P.Pair[B, W], W, A, B any](fab GFAB, a A) GB {
return FC.MonadFlap( return FC.MonadFlap(
MonadMap[GB, GFAB, func(FAB) B], MonadMap[GB, GFAB, func(FAB) B],
fab, fab,
a) a)
} }
func Flap[FAB ~func(A) B, GFAB ~func() T.Tuple3[FAB, W, SG.Semigroup[W]], GB ~func() T.Tuple3[B, W, SG.Semigroup[W]], W, A, B any](a A) func(GFAB) GB { func Flap[FAB ~func(A) B, GFAB ~func() P.Pair[FAB, W], GB ~func() P.Pair[B, W], W, A, B any](a A) func(GFAB) GB {
return FC.Flap(Map[GB, GFAB, func(FAB) B], a) return FC.Flap(Map[GB, GFAB, func(FAB) B], a)
} }

45
writer/monad.go Normal file
View File

@@ -0,0 +1,45 @@
// 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 writer
import (
"github.com/IBM/fp-go/internal/applicative"
"github.com/IBM/fp-go/internal/functor"
"github.com/IBM/fp-go/internal/monad"
"github.com/IBM/fp-go/internal/pointed"
M "github.com/IBM/fp-go/monoid"
G "github.com/IBM/fp-go/writer/generic"
)
// Pointed implements the pointed operations for [Writer]
func Pointed[W, A any](m M.Monoid[W]) pointed.Pointed[A, Writer[W, A]] {
return G.Pointed[Writer[W, A], W, A](m)
}
// Functor implements the pointed operations for [Writer]
func Functor[W, A, B any]() functor.Functor[A, B, Writer[W, A], Writer[W, B]] {
return G.Functor[Writer[W, B], Writer[W, A], W, A, B]()
}
// Applicative implements the applicative operations for [Writer]
func Applicative[W, A, B any](m M.Monoid[W]) applicative.Applicative[A, B, Writer[W, A], Writer[W, B], Writer[W, func(A) B]] {
return G.Applicative[Writer[W, B], Writer[W, func(A) B], Writer[W, A]](m)
}
// Monad implements the monadic operations for [Writer]
func Monad[W, A, B any](m M.Monoid[W]) monad.Monad[A, B, Writer[W, A], Writer[W, B], Writer[W, func(A) B]] {
return G.Monad[Writer[W, B], Writer[W, func(A) B], Writer[W, A]](m)
}

View File

@@ -37,39 +37,40 @@ func AssertLaws[W, 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, fofc := WRT.Pointed[W, C](m)
fofaa := WRT.Pointed[W, func(A) A](m)
fofbc := WRT.Pointed[W, func(B) C](m)
fofabb := WRT.Pointed[W, func(func(A) B) B](m)
fmap := WRT.Functor[W, func(B) C, func(func(A) B) func(A) C]()
fapabb := WRT.Applicative[W, func(A) B, B](m)
fapabac := WRT.Applicative[W, func(A) B, func(A) C](m)
maa := WRT.Monad[W, A, A](m)
mab := WRT.Monad[W, A, B](m)
mac := WRT.Monad[W, A, C](m)
mbc := WRT.Monad[W, B, C](m)
return L.MonadAssertLaws(t,
WRT.Eq(eqw, eqa), WRT.Eq(eqw, eqa),
WRT.Eq(eqw, eqb), WRT.Eq(eqw, eqb),
WRT.Eq(eqw, eqc), WRT.Eq(eqw, eqc),
WRT.Of[A](m), fofc,
WRT.Of[B](m), fofaa,
WRT.Of[C](m), fofbc,
fofabb,
WRT.Of[func(A) A](m), fmap,
WRT.Of[func(A) B](m),
WRT.Of[func(B) C](m),
WRT.Of[func(func(A) B) B](m),
WRT.MonadMap[func(A) A, W, A, A], fapabb,
WRT.MonadMap[func(A) B, W, A, B], fapabac,
WRT.MonadMap[func(A) C, W, A, C],
WRT.MonadMap[func(B) C, W, B, C],
WRT.MonadMap[func(func(B) C) func(func(A) B) func(A) C, W, func(B) C, func(func(A) B) func(A) C], maa,
mab,
WRT.MonadChain[func(A) WRT.Writer[W, A], W, A, A], mac,
WRT.MonadChain[func(A) WRT.Writer[W, B], W, A, B], mbc,
WRT.MonadChain[func(A) WRT.Writer[W, C], W, A, C],
WRT.MonadChain[func(B) WRT.Writer[W, C], W, B, C],
WRT.MonadAp[A, A, W],
WRT.MonadAp[B, A, W],
WRT.MonadAp[C, B, W],
WRT.MonadAp[C, A, W],
WRT.MonadAp[B, func(A) B, W],
WRT.MonadAp[func(A) C, func(A) B, W],
ab, ab,
bc, bc,

View File

@@ -19,30 +19,30 @@ import (
EM "github.com/IBM/fp-go/endomorphism" EM "github.com/IBM/fp-go/endomorphism"
IO "github.com/IBM/fp-go/io" IO "github.com/IBM/fp-go/io"
M "github.com/IBM/fp-go/monoid" M "github.com/IBM/fp-go/monoid"
S "github.com/IBM/fp-go/semigroup" P "github.com/IBM/fp-go/pair"
T "github.com/IBM/fp-go/tuple" SG "github.com/IBM/fp-go/semigroup"
G "github.com/IBM/fp-go/writer/generic" G "github.com/IBM/fp-go/writer/generic"
) )
type Writer[W, A any] IO.IO[T.Tuple3[A, W, S.Semigroup[W]]] type Writer[W, A any] IO.IO[P.Pair[A, W]]
// Tell appends a value to the accumulator // Tell appends a value to the accumulator
func Tell[W any](s S.Semigroup[W]) func(W) Writer[W, any] { func Tell[W any](w W) Writer[W, any] {
return G.Tell[Writer[W, any]](s) return G.Tell[Writer[W, any]](w)
} }
func Of[A, W any](m M.Monoid[W]) func(A) Writer[W, A] { func Of[A, W any](m M.Monoid[W], a A) Writer[W, A] {
return G.Of[Writer[W, A]](m) return G.Of[Writer[W, A]](m, a)
} }
// Listen modifies the result to include the changes to the accumulator // Listen modifies the result to include the changes to the accumulator
func Listen[W, A any](fa Writer[W, A]) Writer[W, T.Tuple2[A, W]] { func Listen[W, A any](fa Writer[W, A]) Writer[W, P.Pair[A, W]] {
return G.Listen[Writer[W, A], Writer[W, T.Tuple2[A, W]], W, A](fa) return G.Listen[Writer[W, A], Writer[W, P.Pair[A, W]], W, A](fa)
} }
// Pass applies the returned function to the accumulator // Pass applies the returned function to the accumulator
func Pass[W, A any](fa Writer[W, T.Tuple2[A, EM.Endomorphism[W]]]) Writer[W, A] { func Pass[W, A any](fa Writer[W, P.Pair[A, EM.Endomorphism[W]]]) Writer[W, A] {
return G.Pass[Writer[W, T.Tuple2[A, EM.Endomorphism[W]]], Writer[W, A]](fa) return G.Pass[Writer[W, P.Pair[A, EM.Endomorphism[W]]], Writer[W, A]](fa)
} }
func MonadMap[FCT ~func(A) B, W, A, B any](fa Writer[W, A], f FCT) Writer[W, B] { func MonadMap[FCT ~func(A) B, W, A, B any](fa Writer[W, A], f FCT) Writer[W, B] {
@@ -53,32 +53,32 @@ func Map[W any, FCT ~func(A) B, A, B any](f FCT) func(Writer[W, A]) Writer[W, B]
return G.Map[Writer[W, B], Writer[W, A]](f) return G.Map[Writer[W, B], Writer[W, A]](f)
} }
func MonadChain[FCT ~func(A) Writer[W, B], W, A, B any](fa Writer[W, A], fct FCT) Writer[W, B] { func MonadChain[FCT ~func(A) Writer[W, B], W, A, B any](s SG.Semigroup[W], fa Writer[W, A], fct FCT) Writer[W, B] {
return G.MonadChain[Writer[W, B], Writer[W, A], FCT](fa, fct) return G.MonadChain[Writer[W, B], Writer[W, A], FCT](s, fa, fct)
} }
func Chain[A, B, W any](fa func(A) Writer[W, B]) func(Writer[W, A]) Writer[W, B] { func Chain[A, B, W any](s SG.Semigroup[W], fa func(A) Writer[W, B]) func(Writer[W, A]) Writer[W, B] {
return G.Chain[Writer[W, B], Writer[W, A], func(A) Writer[W, B]](fa) return G.Chain[Writer[W, B], Writer[W, A], func(A) Writer[W, B]](s, fa)
} }
func MonadAp[B, A, W any](fab Writer[W, func(A) B], fa Writer[W, A]) Writer[W, B] { func MonadAp[B, A, W any](s SG.Semigroup[W], fab Writer[W, func(A) B], fa Writer[W, A]) Writer[W, B] {
return G.MonadAp[Writer[W, B], Writer[W, func(A) B], Writer[W, A]](fab, fa) return G.MonadAp[Writer[W, B], Writer[W, func(A) B], Writer[W, A]](s, fab, fa)
} }
func Ap[B, A, W any](fa Writer[W, A]) func(Writer[W, func(A) B]) Writer[W, B] { func Ap[B, A, W any](s SG.Semigroup[W], fa Writer[W, A]) func(Writer[W, func(A) B]) Writer[W, B] {
return G.Ap[Writer[W, B], Writer[W, func(A) B], Writer[W, A]](fa) return G.Ap[Writer[W, B], Writer[W, func(A) B], Writer[W, A]](s, fa)
} }
func MonadChainFirst[FCT ~func(A) Writer[W, B], W, A, B any](fa Writer[W, A], fct FCT) Writer[W, A] { func MonadChainFirst[FCT ~func(A) Writer[W, B], W, A, B any](s SG.Semigroup[W], fa Writer[W, A], fct FCT) Writer[W, A] {
return G.MonadChainFirst[Writer[W, B], Writer[W, A], FCT](fa, fct) return G.MonadChainFirst[Writer[W, B], Writer[W, A], FCT](s, fa, fct)
} }
func ChainFirst[FCT ~func(A) Writer[W, B], W, A, B any](fct FCT) func(Writer[W, A]) Writer[W, A] { func ChainFirst[FCT ~func(A) Writer[W, B], W, A, B any](s SG.Semigroup[W], fct FCT) func(Writer[W, A]) Writer[W, A] {
return G.ChainFirst[Writer[W, B], Writer[W, A], FCT](fct) return G.ChainFirst[Writer[W, B], Writer[W, A], FCT](s, fct)
} }
func Flatten[W, A any](mma Writer[W, Writer[W, A]]) Writer[W, A] { func Flatten[W, A any](s SG.Semigroup[W], mma Writer[W, Writer[W, A]]) Writer[W, A] {
return G.Flatten[Writer[W, Writer[W, A]], Writer[W, A]](mma) return G.Flatten[Writer[W, Writer[W, A]], Writer[W, A]](s, mma)
} }
// Execute extracts the accumulator // Execute extracts the accumulator
@@ -102,13 +102,13 @@ func Censor[A any, FCT ~func(W) W, W any](f FCT) func(Writer[W, A]) Writer[W, A]
} }
// MonadListens projects a value from modifications made to the accumulator during an action // MonadListens projects a value from modifications made to the accumulator during an action
func MonadListens[A any, FCT ~func(W) B, W, B any](fa Writer[W, A], f FCT) Writer[W, T.Tuple2[A, B]] { func MonadListens[A any, FCT ~func(W) B, W, B any](fa Writer[W, A], f FCT) Writer[W, P.Pair[A, B]] {
return G.MonadListens[Writer[W, A], Writer[W, T.Tuple2[A, B]]](fa, f) return G.MonadListens[Writer[W, A], Writer[W, P.Pair[A, B]]](fa, f)
} }
// Listens projects a value from modifications made to the accumulator during an action // Listens projects a value from modifications made to the accumulator during an action
func Listens[A any, FCT ~func(W) B, W, B any](f FCT) func(Writer[W, A]) Writer[W, T.Tuple2[A, B]] { func Listens[A any, FCT ~func(W) B, W, B any](f FCT) func(Writer[W, A]) Writer[W, P.Pair[A, B]] {
return G.Listens[Writer[W, A], Writer[W, T.Tuple2[A, B]]](f) return G.Listens[Writer[W, A], Writer[W, P.Pair[A, B]]](f)
} }
func MonadFlap[W, B, A any](fab Writer[W, func(A) B], a A) Writer[W, B] { func MonadFlap[W, B, A any](fab Writer[W, func(A) B], a A) Writer[W, B] {

View File

@@ -20,24 +20,22 @@ import (
A "github.com/IBM/fp-go/array" A "github.com/IBM/fp-go/array"
F "github.com/IBM/fp-go/function" F "github.com/IBM/fp-go/function"
S "github.com/IBM/fp-go/semigroup" P "github.com/IBM/fp-go/pair"
T "github.com/IBM/fp-go/tuple"
) )
func doubleAndLog(data int) Writer[[]string, int] { func doubleAndLog(data int) Writer[[]string, int] {
return func() T.Tuple3[int, []string, S.Semigroup[[]string]] { return func() P.Pair[int, []string] {
result := data * 2 result := data * 2
return T.MakeTuple3(result, A.Of(fmt.Sprintf("Doubled %d -> %d", data, result)), sg) return P.MakePair(result, A.Of(fmt.Sprintf("Doubled %d -> %d", data, result)))
} }
} }
func ExampleWriter_logging() { func ExampleWriter_logging() {
res := F.Pipe4( res := F.Pipe3(
10, Of[int](monoid, 10),
Of[int](monoid), Chain(sg, doubleAndLog),
Chain(doubleAndLog), Chain(sg, doubleAndLog),
Chain(doubleAndLog),
Execute[[]string, int], Execute[[]string, int],
) )