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

Add Do notation support and Bind to Monads (#100)

* fix: implement bind, let, apS for serveral monads

Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>

* fix: implement bind for maps

Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>

* fix: implement do notation for more monads

Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>

* fix: add bind to more monads

Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>

* fix: add Do and Bind support to Monads

Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>

---------

Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
This commit is contained in:
Carsten Leue
2024-01-31 21:34:46 +01:00
committed by GitHub
parent c73467caf5
commit 7d3759619c
77 changed files with 3665 additions and 174 deletions

View File

@@ -19,26 +19,48 @@ import (
G "github.com/IBM/fp-go/ioeither/generic"
)
// Bind applies a function to an input state and merges the result into that state
func Bind[E, A, S1, S2 any](s func(A) func(S1) S2, f func(S1) IOEither[E, A]) func(IOEither[E, S1]) IOEither[E, S2] {
return G.Bind[IOEither[E, S1], IOEither[E, S2], IOEither[E, A], func(S1) IOEither[E, A]](s, f)
// Bind creates an empty context of type [S] to be used with the [Bind] operation
func Do[E, S any](
empty S,
) IOEither[E, S] {
return G.Do[IOEither[E, S], E, S](empty)
}
// BindTo initializes some state based on a value
func BindTo[
E, A, S2 any](s func(A) S2) func(IOEither[E, A]) IOEither[E, S2] {
return G.BindTo[IOEither[E, S2], IOEither[E, A]](s)
}
func ApS[
E, A, S1, S2 any,
](s func(A) func(S1) S2, fa IOEither[E, A]) func(IOEither[E, S1]) IOEither[E, S2] {
return G.ApS[IOEither[E, S1], IOEither[E, S2], IOEither[E, A], IOEither[E, func(S1) S2]](s, fa)
}
func Let[E, A, S1, S2 any](
s func(A) func(S1) S2,
f func(S1) A,
// Bind attaches the result of a computation to a context [S1] to produce a context [S2]
func Bind[E, S1, S2, T any](
setter func(T) func(S1) S2,
f func(S1) IOEither[E, T],
) func(IOEither[E, S1]) IOEither[E, S2] {
return G.Let[IOEither[E, S1], IOEither[E, S2]](s, f)
return G.Bind[IOEither[E, S1], IOEither[E, S2], IOEither[E, T], E, S1, S2, T](setter, f)
}
// Let attaches the result of a computation to a context [S1] to produce a context [S2]
func Let[E, S1, S2, T any](
setter func(T) func(S1) S2,
f func(S1) T,
) func(IOEither[E, S1]) IOEither[E, S2] {
return G.Let[IOEither[E, S1], IOEither[E, S2], E, S1, S2, T](setter, f)
}
// LetTo attaches the a value to a context [S1] to produce a context [S2]
func LetTo[E, S1, S2, T any](
setter func(T) func(S1) S2,
b T,
) func(IOEither[E, S1]) IOEither[E, S2] {
return G.LetTo[IOEither[E, S1], IOEither[E, S2], E, S1, S2, T](setter, b)
}
// BindTo initializes a new state [S1] from a value [T]
func BindTo[E, S1, T any](
setter func(T) S1,
) func(IOEither[E, T]) IOEither[E, S1] {
return G.BindTo[IOEither[E, S1], IOEither[E, T], E, S1, T](setter)
}
// ApS attaches a value to a context [S1] to produce a context [S2] by considering the context and the value concurrently
func ApS[E, S1, S2, T any](
setter func(T) func(S1) S2,
fa IOEither[E, T],
) func(IOEither[E, S1]) IOEither[E, S2] {
return G.ApS[IOEither[E, S1], IOEither[E, S2], IOEither[E, T], E, S1, S2, T](setter, fa)
}

57
ioeither/bind_test.go Normal file
View File

@@ -0,0 +1,57 @@
// 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 ioeither
import (
"testing"
E "github.com/IBM/fp-go/either"
F "github.com/IBM/fp-go/function"
"github.com/IBM/fp-go/internal/utils"
"github.com/stretchr/testify/assert"
)
func getLastName(s utils.Initial) IOEither[error, string] {
return Of[error]("Doe")
}
func getGivenName(s utils.WithLastName) IOEither[error, string] {
return Of[error]("John")
}
func TestBind(t *testing.T) {
res := F.Pipe3(
Do[error](utils.Empty),
Bind(utils.SetLastName, getLastName),
Bind(utils.SetGivenName, getGivenName),
Map[error](utils.GetFullName),
)
assert.Equal(t, res(), E.Of[error]("John Doe"))
}
func TestApS(t *testing.T) {
res := F.Pipe3(
Do[error](utils.Empty),
ApS(utils.SetLastName, Of[error]("Doe")),
ApS(utils.SetGivenName, Of[error]("John")),
Map[error](utils.GetFullName),
)
assert.Equal(t, res(), E.Of[error]("John Doe"))
}

View File

@@ -26,7 +26,7 @@ func Eq[E, A any](eq EQ.Eq[ET.Either[E, A]]) EQ.Eq[IOEither[E, A]] {
return G.Eq[IOEither[E, A]](eq)
}
// FromStrictEquals constructs an `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]] {
return G.FromStrictEquals[IOEither[E, A]]()
}

View File

@@ -17,63 +17,74 @@ package generic
import (
ET "github.com/IBM/fp-go/either"
G "github.com/IBM/fp-go/internal/bindt"
A "github.com/IBM/fp-go/internal/apply"
C "github.com/IBM/fp-go/internal/chain"
F "github.com/IBM/fp-go/internal/functor"
)
// Bind applies a function to an input state and merges the result into that state
func Bind[
GS1 ~func() ET.Either[E, S1],
GS2 ~func() ET.Either[E, S2],
GA ~func() ET.Either[E, A],
FCT ~func(S1) GA,
E any,
SET ~func(A) func(S1) S2,
A, S1, S2 any](s SET, f FCT) func(GS1) GS2 {
return G.Bind(
// Bind creates an empty context of type [S] to be used with the [Bind] operation
func Do[GS ~func() ET.Either[E, S], E, S any](
empty S,
) GS {
return Of[GS, E, S](empty)
}
// Bind attaches the result of a computation to a context [S1] to produce a context [S2]
func Bind[GS1 ~func() ET.Either[E, S1], GS2 ~func() ET.Either[E, S2], GT ~func() ET.Either[E, T], E, S1, S2, T any](
setter func(T) func(S1) S2,
f func(S1) GT,
) func(GS1) GS2 {
return C.Bind(
Chain[GS1, GS2, E, S1, S2],
Map[GA, GS2, E, A, S2],
s,
Map[GT, GS2, E, T, S2],
setter,
f,
)
}
// BindTo initializes some state based on a value
func BindTo[
GS2 ~func() ET.Either[E, S2],
GA ~func() ET.Either[E, A],
E any,
SET ~func(A) S2,
A, S2 any](s SET) func(GA) GS2 {
return G.BindTo(
Map[GA, GS2, E, A, S2],
s,
)
}
func ApS[
GS1 ~func() ET.Either[E, S1],
GS2 ~func() ET.Either[E, S2],
GB ~func() ET.Either[E, B],
GS1S2 ~func() ET.Either[E, func(S1) S2],
SET ~func(B) func(S1) S2,
E, S1, S2, B any,
](s SET, fb GB) func(GS1) GS2 {
return G.ApS[SET, S1, S2, B, GS1S2, GS1, GS2, GB](
Ap[GS2, GS1S2, GS1, E, S1, S2],
Map[GB, GS1S2, E, B, func(S1) S2],
s,
fb,
)
}
func Let[
GS1 ~func() ET.Either[E, S1],
GS2 ~func() ET.Either[E, S2],
SET ~func(B) func(S1) S2,
FCT ~func(S1) B,
E, S1, S2, B any](
s SET,
f FCT,
// Let attaches the result of a computation to a context [S1] to produce a context [S2]
func Let[GS1 ~func() ET.Either[E, S1], GS2 ~func() ET.Either[E, S2], E, S1, S2, T any](
key func(T) func(S1) S2,
f func(S1) T,
) func(GS1) GS2 {
return G.Let[SET, FCT, S1, S2, B, GS1, GS2](Map[GS1, GS2, E, S1, S2], s, f)
return F.Let(
Map[GS1, GS2, E, S1, S2],
key,
f,
)
}
// LetTo attaches the a value to a context [S1] to produce a context [S2]
func LetTo[GS1 ~func() ET.Either[E, S1], GS2 ~func() ET.Either[E, S2], E, S1, S2, B any](
key func(B) func(S1) S2,
b B,
) func(GS1) GS2 {
return F.LetTo(
Map[GS1, GS2, E, S1, S2],
key,
b,
)
}
// BindTo initializes a new state [S1] from a value [T]
func BindTo[GS1 ~func() ET.Either[E, S1], GT ~func() ET.Either[E, T], E, S1, S2, T any](
setter func(T) S1,
) func(GT) GS1 {
return C.BindTo(
Map[GT, GS1, E, T, S1],
setter,
)
}
// 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() ET.Either[E, S1], GS2 ~func() ET.Either[E, S2], GT ~func() ET.Either[E, T], E, S1, S2, T any](
setter func(T) func(S1) S2,
fa GT,
) func(GS1) GS2 {
return A.ApS(
Ap[GS2, func() ET.Either[E, func(T) S2], GT, E, T, S2],
Map[GS1, func() ET.Either[E, func(T) S2], E, S1, func(T) S2],
setter,
fa,
)
}

View File

@@ -26,7 +26,7 @@ func Eq[GA ~func() ET.Either[E, A], E, A any](eq EQ.Eq[ET.Either[E, A]]) EQ.Eq[G
return G.Eq[GA](eq)
}
// FromStrictEquals constructs an `Eq` from the canonical comparison function
// 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

@@ -134,7 +134,7 @@ func MonadChainIOK[GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B], GR ~f
func ChainIOK[GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B], GR ~func() B, E, A, B any](f func(A) GR) func(GA) GB {
return FI.ChainIOK(
MonadChain[GA, GB, E, A, B],
Chain[GA, GB, E, A, B],
FromIO[GB, GR, E, B],
f,
)