1
0
mirror of https://github.com/IBM/fp-go.git synced 2025-06-21 00:19:34 +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

66
array/bind.go Normal file
View File

@ -0,0 +1,66 @@
// 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 array
import (
G "github.com/IBM/fp-go/array/generic"
)
// Bind creates an empty context of type [S] to be used with the [Bind] operation
func Do[S any](
empty S,
) []S {
return G.Do[[]S, S](empty)
}
// Bind attaches the result of a computation to a context [S1] to produce a context [S2]
func Bind[S1, S2, T any](
setter func(T) func(S1) S2,
f func(S1) []T,
) func([]S1) []S2 {
return G.Bind[[]S1, []S2, []T, S1, S2, T](setter, f)
}
// Let attaches the result of a computation to a context [S1] to produce a context [S2]
func Let[S1, S2, T any](
setter func(T) func(S1) S2,
f func(S1) T,
) func([]S1) []S2 {
return G.Let[[]S1, []S2, S1, S2, T](setter, f)
}
// LetTo attaches the a value to a context [S1] to produce a context [S2]
func LetTo[S1, S2, T any](
setter func(T) func(S1) S2,
b T,
) func([]S1) []S2 {
return G.LetTo[[]S1, []S2, S1, S2, T](setter, b)
}
// BindTo initializes a new state [S1] from a value [T]
func BindTo[S1, T any](
setter func(T) S1,
) func([]T) []S1 {
return G.BindTo[[]S1, []T, 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[S1, S2, T any](
setter func(T) func(S1) S2,
fa []T,
) func([]S1) []S2 {
return G.ApS[[]S1, []S2, []T, S1, S2, T](setter, fa)
}

56
array/bind_test.go Normal file
View File

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

89
array/generic/bind.go Normal file
View File

@ -0,0 +1,89 @@
// 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 (
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 creates an empty context of type [S] to be used with the [Bind] operation
func Do[GS ~[]S, S any](
empty S,
) GS {
return Of[GS](empty)
}
// Bind attaches the result of a computation to a context [S1] to produce a context [S2]
func Bind[GS1 ~[]S1, GS2 ~[]S2, GT ~[]T, S1, S2, T any](
setter func(T) func(S1) S2,
f func(S1) GT,
) func(GS1) GS2 {
return C.Bind(
Chain[GS1, GS2, S1, S2],
Map[GT, GS2, T, S2],
setter,
f,
)
}
// Let attaches the result of a computation to a context [S1] to produce a context [S2]
func Let[GS1 ~[]S1, GS2 ~[]S2, S1, S2, T any](
key func(T) func(S1) S2,
f func(S1) T,
) func(GS1) GS2 {
return F.Let(
Map[GS1, GS2, S1, S2],
key,
f,
)
}
// LetTo attaches the a value to a context [S1] to produce a context [S2]
func LetTo[GS1 ~[]S1, GS2 ~[]S2, S1, S2, B any](
key func(B) func(S1) S2,
b B,
) func(GS1) GS2 {
return F.LetTo(
Map[GS1, GS2, S1, S2],
key,
b,
)
}
// BindTo initializes a new state [S1] from a value [T]
func BindTo[GS1 ~[]S1, GT ~[]T, S1, T any](
setter func(T) S1,
) func(GT) GS1 {
return C.BindTo(
Map[GT, GS1, 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 ~[]S1, GS2 ~[]S2, GT ~[]T, S1, S2, T any](
setter func(T) func(S1) S2,
fa GT,
) func(GS1) GS2 {
return A.ApS(
Ap[GS2, []func(T) S2, GT, S2, T],
Map[GS1, []func(T) S2, S1, func(T) S2],
setter,
fa,
)
}

74
array/testing/laws.go Normal file
View File

@ -0,0 +1,74 @@
// 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 testing
import (
"testing"
RA "github.com/IBM/fp-go/array"
EQ "github.com/IBM/fp-go/eq"
L "github.com/IBM/fp-go/internal/monad/testing"
)
// AssertLaws asserts the apply monad laws for the array monad
func AssertLaws[A, B, C any](t *testing.T,
eqa EQ.Eq[A],
eqb EQ.Eq[B],
eqc EQ.Eq[C],
ab func(A) B,
bc func(B) C,
) func(a A) bool {
return L.AssertLaws(t,
RA.Eq(eqa),
RA.Eq(eqb),
RA.Eq(eqc),
RA.Of[A],
RA.Of[B],
RA.Of[C],
RA.Of[func(A) A],
RA.Of[func(A) B],
RA.Of[func(B) C],
RA.Of[func(func(A) B) B],
RA.MonadMap[A, A],
RA.MonadMap[A, B],
RA.MonadMap[A, C],
RA.MonadMap[B, C],
RA.MonadMap[func(B) C, func(func(A) B) func(A) C],
RA.MonadChain[A, A],
RA.MonadChain[A, B],
RA.MonadChain[A, C],
RA.MonadChain[B, C],
RA.MonadAp[A, A],
RA.MonadAp[B, A],
RA.MonadAp[C, B],
RA.MonadAp[C, A],
RA.MonadAp[B, func(A) B],
RA.MonadAp[func(A) C, func(A) B],
ab,
bc,
)
}

View File

@ -0,0 +1,47 @@
// 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 testing
import (
"fmt"
"testing"
EQ "github.com/IBM/fp-go/eq"
"github.com/stretchr/testify/assert"
)
func TestMonadLaws(t *testing.T) {
// some comparison
eqa := EQ.FromStrictEquals[bool]()
eqb := EQ.FromStrictEquals[int]()
eqc := EQ.FromStrictEquals[string]()
ab := func(a bool) int {
if a {
return 1
}
return 0
}
bc := func(b int) string {
return fmt.Sprintf("value %d", b)
}
laws := AssertLaws(t, eqa, eqb, eqc, ab, bc)
assert.True(t, laws(true))
assert.True(t, laws(false))
}

View File

@ -0,0 +1,68 @@
// 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 readereither
import (
"context"
G "github.com/IBM/fp-go/readereither/generic"
)
// Bind creates an empty context of type [S] to be used with the [Bind] operation
func Do[S any](
empty S,
) ReaderEither[S] {
return G.Do[ReaderEither[S], context.Context, error, S](empty)
}
// Bind attaches the result of a computation to a context [S1] to produce a context [S2]
func Bind[S1, S2, T any](
setter func(T) func(S1) S2,
f func(S1) ReaderEither[T],
) func(ReaderEither[S1]) ReaderEither[S2] {
return G.Bind[ReaderEither[S1], ReaderEither[S2], ReaderEither[T], context.Context, error, S1, S2, T](setter, f)
}
// Let attaches the result of a computation to a context [S1] to produce a context [S2]
func Let[S1, S2, T any](
setter func(T) func(S1) S2,
f func(S1) T,
) func(ReaderEither[S1]) ReaderEither[S2] {
return G.Let[ReaderEither[S1], ReaderEither[S2], context.Context, error, S1, S2, T](setter, f)
}
// LetTo attaches the a value to a context [S1] to produce a context [S2]
func LetTo[S1, S2, T any](
setter func(T) func(S1) S2,
b T,
) func(ReaderEither[S1]) ReaderEither[S2] {
return G.LetTo[ReaderEither[S1], ReaderEither[S2], context.Context, error, S1, S2, T](setter, b)
}
// BindTo initializes a new state [S1] from a value [T]
func BindTo[S1, T any](
setter func(T) S1,
) func(ReaderEither[T]) ReaderEither[S1] {
return G.BindTo[ReaderEither[S1], ReaderEither[T], context.Context, error, 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[S1, S2, T any](
setter func(T) func(S1) S2,
fa ReaderEither[T],
) func(ReaderEither[S1]) ReaderEither[S2] {
return G.ApS[ReaderEither[S1], ReaderEither[S2], ReaderEither[T], context.Context, error, S1, S2, T](setter, fa)
}

View File

@ -0,0 +1,58 @@
// 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 readereither
import (
"context"
"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) ReaderEither[string] {
return Of("Doe")
}
func getGivenName(s utils.WithLastName) ReaderEither[string] {
return Of("John")
}
func TestBind(t *testing.T) {
res := F.Pipe3(
Do(utils.Empty),
Bind(utils.SetLastName, getLastName),
Bind(utils.SetGivenName, getGivenName),
Map(utils.GetFullName),
)
assert.Equal(t, res(context.Background()), E.Of[error]("John Doe"))
}
func TestApS(t *testing.T) {
res := F.Pipe3(
Do(utils.Empty),
ApS(utils.SetLastName, Of("Doe")),
ApS(utils.SetGivenName, Of("John")),
Map(utils.GetFullName),
)
assert.Equal(t, res(context.Background()), E.Of[error]("John Doe"))
}

View File

@ -0,0 +1,67 @@
// 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 readerioeither
import (
G "github.com/IBM/fp-go/context/readerioeither/generic"
IOE "github.com/IBM/fp-go/ioeither"
)
// Bind creates an empty context of type [S] to be used with the [Bind] operation
func Do[S any](
empty S,
) ReaderIOEither[S] {
return G.Do[ReaderIOEither[S], IOE.IOEither[error, S], S](empty)
}
// Bind attaches the result of a computation to a context [S1] to produce a context [S2]
func Bind[S1, S2, T any](
setter func(T) func(S1) S2,
f func(S1) ReaderIOEither[T],
) func(ReaderIOEither[S1]) ReaderIOEither[S2] {
return G.Bind[ReaderIOEither[S1], ReaderIOEither[S2], ReaderIOEither[T], IOE.IOEither[error, S1], IOE.IOEither[error, S2], IOE.IOEither[error, T], S1, S2, T](setter, f)
}
// Let attaches the result of a computation to a context [S1] to produce a context [S2]
func Let[S1, S2, T any](
setter func(T) func(S1) S2,
f func(S1) T,
) func(ReaderIOEither[S1]) ReaderIOEither[S2] {
return G.Let[ReaderIOEither[S1], ReaderIOEither[S2], IOE.IOEither[error, S1], IOE.IOEither[error, S2], S1, S2, T](setter, f)
}
// LetTo attaches the a value to a context [S1] to produce a context [S2]
func LetTo[S1, S2, T any](
setter func(T) func(S1) S2,
b T,
) func(ReaderIOEither[S1]) ReaderIOEither[S2] {
return G.LetTo[ReaderIOEither[S1], ReaderIOEither[S2], IOE.IOEither[error, S1], IOE.IOEither[error, S2], S1, S2, T](setter, b)
}
// BindTo initializes a new state [S1] from a value [T]
func BindTo[S1, T any](
setter func(T) S1,
) func(ReaderIOEither[T]) ReaderIOEither[S1] {
return G.BindTo[ReaderIOEither[S1], ReaderIOEither[T], IOE.IOEither[error, S1], IOE.IOEither[error, T], 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[S1, S2, T any](
setter func(T) func(S1) S2,
fa ReaderIOEither[T],
) func(ReaderIOEither[S1]) ReaderIOEither[S2] {
return G.ApS[ReaderIOEither[func(T) S2], ReaderIOEither[S1], ReaderIOEither[S2], ReaderIOEither[T], IOE.IOEither[error, func(T) S2], IOE.IOEither[error, S1], IOE.IOEither[error, S2], IOE.IOEither[error, T], S1, S2, T](setter, fa)
}

View File

@ -0,0 +1,58 @@
// 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 readerioeither
import (
"context"
"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) ReaderIOEither[string] {
return Of("Doe")
}
func getGivenName(s utils.WithLastName) ReaderIOEither[string] {
return Of("John")
}
func TestBind(t *testing.T) {
res := F.Pipe3(
Do(utils.Empty),
Bind(utils.SetLastName, getLastName),
Bind(utils.SetGivenName, getGivenName),
Map(utils.GetFullName),
)
assert.Equal(t, res(context.Background())(), E.Of[error]("John Doe"))
}
func TestApS(t *testing.T) {
res := F.Pipe3(
Do(utils.Empty),
ApS(utils.SetLastName, Of("Doe")),
ApS(utils.SetGivenName, Of("John")),
Map(utils.GetFullName),
)
assert.Equal(t, res(context.Background())(), E.Of[error]("John Doe"))
}

View File

@ -0,0 +1,92 @@
// 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 (
"context"
ET "github.com/IBM/fp-go/either"
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 creates an empty context of type [S] to be used with the [Bind] operation
func Do[GRS ~func(context.Context) GS, GS ~func() ET.Either[error, S], S any](
empty S,
) GRS {
return Of[GRS, GS, S](empty)
}
// Bind attaches the result of a computation to a context [S1] to produce a context [S2]
func Bind[GRS1 ~func(context.Context) GS1, GRS2 ~func(context.Context) GS2, GRT ~func(context.Context) GT, GS1 ~func() ET.Either[error, S1], GS2 ~func() ET.Either[error, S2], GT ~func() ET.Either[error, T], S1, S2, T any](
setter func(T) func(S1) S2,
f func(S1) GRT,
) func(GRS1) GRS2 {
return C.Bind(
Chain[GRS1, GRS2, GS1, GS2, S1, S2],
Map[GRT, GRS2, GT, GS2, T, S2],
setter,
f,
)
}
// Let attaches the result of a computation to a context [S1] to produce a context [S2]
func Let[GRS1 ~func(context.Context) GS1, GRS2 ~func(context.Context) GS2, GS1 ~func() ET.Either[error, S1], GS2 ~func() ET.Either[error, S2], S1, S2, T any](
key func(T) func(S1) S2,
f func(S1) T,
) func(GRS1) GRS2 {
return F.Let(
Map[GRS1, GRS2, GS1, GS2, S1, S2],
key,
f,
)
}
// LetTo attaches the a value to a context [S1] to produce a context [S2]
func LetTo[GRS1 ~func(context.Context) GS1, GRS2 ~func(context.Context) GS2, GS1 ~func() ET.Either[error, S1], GS2 ~func() ET.Either[error, S2], S1, S2, B any](
key func(B) func(S1) S2,
b B,
) func(GRS1) GRS2 {
return F.LetTo(
Map[GRS1, GRS2, GS1, GS2, S1, S2],
key,
b,
)
}
// BindTo initializes a new state [S1] from a value [T]
func BindTo[GRS1 ~func(context.Context) GS1, GRT ~func(context.Context) GT, GS1 ~func() ET.Either[error, S1], GT ~func() ET.Either[error, T], S1, T any](
setter func(T) S1,
) func(GRT) GRS1 {
return C.BindTo(
Map[GRT, GRS1, GT, GS1, 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[GRTS1 ~func(context.Context) GTS1, GRS1 ~func(context.Context) GS1, GRS2 ~func(context.Context) GS2, GRT ~func(context.Context) GT, GTS1 ~func() ET.Either[error, func(T) S2], GS1 ~func() ET.Either[error, S1], GS2 ~func() ET.Either[error, S2], GT ~func() ET.Either[error, T], S1, S2, T any](
setter func(T) func(S1) S2,
fa GRT,
) func(GRS1) GRS2 {
return A.ApS(
Ap[GRS2, GRTS1, GRT, GS2, GTS1, GT],
Map[GRS1, GRTS1, GS1, GTS1, S1, func(T) S2],
setter,
fa,
)
}

89
either/bind.go Normal file
View File

@ -0,0 +1,89 @@
// 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 either
import (
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 creates an empty context of type [S] to be used with the [Bind] operation
func Do[E, S any](
empty S,
) Either[E, S] {
return Of[E](empty)
}
// 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) Either[E, T],
) func(Either[E, S1]) Either[E, S2] {
return C.Bind(
Chain[E, S1, S2],
Map[E, T, S2],
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](
key func(T) func(S1) S2,
f func(S1) T,
) func(Either[E, S1]) Either[E, S2] {
return F.Let(
Map[E, S1, S2],
key,
f,
)
}
// LetTo attaches the a value to a context [S1] to produce a context [S2]
func LetTo[E, S1, S2, T any](
key func(T) func(S1) S2,
b T,
) func(Either[E, S1]) Either[E, S2] {
return F.LetTo(
Map[E, S1, S2],
key,
b,
)
}
// BindTo initializes a new state [S1] from a value [T]
func BindTo[E, S1, T any](
setter func(T) S1,
) func(Either[E, T]) Either[E, S1] {
return C.BindTo(
Map[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[E, S1, S2, T any](
setter func(T) func(S1) S2,
fa Either[E, T],
) func(Either[E, S1]) Either[E, S2] {
return A.ApS(
Ap[S2, E, T],
Map[E, S1, func(T) S2],
setter,
fa,
)
}

56
either/bind_test.go Normal file
View File

@ -0,0 +1,56 @@
// 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 either
import (
"testing"
F "github.com/IBM/fp-go/function"
"github.com/IBM/fp-go/internal/utils"
"github.com/stretchr/testify/assert"
)
func getLastName(s utils.Initial) Either[error, string] {
return Of[error]("Doe")
}
func getGivenName(s utils.WithLastName) Either[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, 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, Of[error]("John Doe"))
}

View File

@ -34,7 +34,7 @@ func Eq[E, A any](e EQ.Eq[E], a EQ.Eq[A]) EQ.Eq[Either[E, A]] {
return EQ.FromEquals(F.Uncurry2(fld)) return EQ.FromEquals(F.Uncurry2(fld))
} }
// 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[Either[E, A]] { func FromStrictEquals[E, A comparable]() EQ.Eq[Either[E, A]] {
return Eq(EQ.FromStrictEquals[E](), EQ.FromStrictEquals[A]()) return Eq(EQ.FromStrictEquals[E](), EQ.FromStrictEquals[A]())
} }

View File

@ -35,12 +35,12 @@ func strictEq[A comparable](a, b A) bool {
return a == b return a == b
} }
// FromStrictEquals constructs an `Eq` from the canonical comparison function // FromStrictEquals constructs an [EQ.Eq] from the canonical comparison function
func FromStrictEquals[T comparable]() Eq[T] { func FromStrictEquals[T comparable]() Eq[T] {
return FromEquals(strictEq[T]) return FromEquals(strictEq[T])
} }
// FromEquals constructs an `Eq` from the comparison function // FromEquals constructs an [EQ.Eq] from the comparison function
func FromEquals[T any](c func(x, y T) bool) Eq[T] { func FromEquals[T any](c func(x, y T) bool) Eq[T] {
return eq[T]{c: c} return eq[T]{c: c}
} }

89
identity/bind.go Normal file
View File

@ -0,0 +1,89 @@
// 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 identity
import (
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 creates an empty context of type [S] to be used with the [Bind] operation
func Do[S any](
empty S,
) S {
return empty
}
// Bind attaches the result of a computation to a context [S1] to produce a context [S2]
func Bind[S1, S2, T any](
setter func(T) func(S1) S2,
f func(S1) T,
) func(S1) S2 {
return C.Bind(
Chain[S1, S2],
Map[T, S2],
setter,
f,
)
}
// Let attaches the result of a computation to a context [S1] to produce a context [S2]
func Let[S1, S2, T any](
key func(T) func(S1) S2,
f func(S1) T,
) func(S1) S2 {
return F.Let(
Map[S1, S2],
key,
f,
)
}
// LetTo attaches the a value to a context [S1] to produce a context [S2]
func LetTo[S1, S2, B any](
key func(B) func(S1) S2,
b B,
) func(S1) S2 {
return F.LetTo(
Map[S1, S2],
key,
b,
)
}
// BindTo initializes a new state [S1] from a value [T]
func BindTo[S1, T any](
setter func(T) S1,
) func(T) S1 {
return C.BindTo(
Map[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[S1, S2, T any](
setter func(T) func(S1) S2,
fa T,
) func(S1) S2 {
return A.ApS(
Ap[S2, T],
Map[S1, func(T) S2],
setter,
fa,
)
}

View File

@ -37,6 +37,16 @@ func MonadAp[HKTGA, HKTGB, HKTGAB, HKTFGAB, HKTFGGAB, HKTFGA, HKTFGB any](
return fap(fmap(fab, F.Bind1st(F.Bind1st[HKTGAB, HKTGA, HKTGB], gap)), fa) return fap(fmap(fab, F.Bind1st(F.Bind1st[HKTGAB, HKTGA, HKTGB], gap)), fa)
} }
// func Ap[HKTGA, HKTGB, HKTGAB, HKTFGAB, HKTFGGAB, HKTFGA, HKTFGB any](
// fap func(HKTFGA) func(HKTFGGAB) HKTFGB,
// fmap func(func(HKTGAB) func(HKTGA) HKTGB) func(HKTFGAB) HKTFGGAB,
// gap func(HKTGA) func(HKTGAB) HKTGB,
// fa HKTFGA) func(HKTFGAB) HKTFGB {
// return fap(fmap(F.Bind1st(F.Bind1st[HKTGAB, HKTGA, HKTGB], gap)), fa)
// }
// export function ap<F, G>( // export function ap<F, G>(
// F: Apply<F>, // F: Apply<F>,
// G: Apply<G> // G: Apply<G>
@ -107,3 +117,25 @@ func ApSecond[HKTGA, HKTGB, HKTGBB, A, B any](
return MonadApSecond(fap, fmap, first, second) return MonadApSecond(fap, fmap, first, second)
} }
} }
func MonadApS[S1, S2, B, HKTBGBS2, HKTS1, HKTS2, HKTB any](
fap func(HKTBGBS2, HKTB) HKTS2,
fmap func(HKTS1, func(S1) func(B) S2) HKTBGBS2,
fa HKTS1,
key func(B) func(S1) S2,
fb HKTB,
) HKTS2 {
return fap(fmap(fa, F.Flip(key)), fb)
}
func ApS[S1, S2, B, HKTBGBS2, HKTS1, HKTS2, HKTB any](
fap func(HKTB) func(HKTBGBS2) HKTS2,
fmap func(func(S1) func(B) S2) func(HKTS1) HKTBGBS2,
key func(B) func(S1) S2,
fb HKTB,
) func(HKTS1) HKTS2 {
return F.Flow2(
fmap(F.Flip(key)),
fap(fb),
)
}

View File

@ -1,75 +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 bindt
import (
F "github.com/IBM/fp-go/function"
I "github.com/IBM/fp-go/identity"
T "github.com/IBM/fp-go/tuple"
)
func Bind[SET ~func(B) func(S1) S2, FCT ~func(S1) HKTB, S1, S2, B, HKTS1, HKTS2, HKTB any](
mchain func(func(S1) HKTS2) func(HKTS1) HKTS2,
mmap func(func(B) S2) func(HKTB) HKTS2,
s SET,
f FCT,
) func(HKTS1) HKTS2 {
return mchain(F.Flow3(
T.Replicate2[S1],
T.Map2(F.Flow2(
I.Ap[S2, S1],
F.Flow2(
F.Bind1st(F.Flow2[SET, func(func(S1) S2) S2], s),
mmap,
)), f),
T.Tupled2(I.MonadAp[HKTS2, HKTB]),
))
}
func BindTo[SET ~func(B) S2, S2, B, HKTS2, HKTB any](
mmap func(func(B) S2) func(HKTB) HKTS2,
s SET,
) func(HKTB) HKTS2 {
return mmap(s)
}
func ApS[
SET ~func(B) func(S1) S2,
S1, S2, B, HKTS1S2, HKTS1, HKTS2, HKTB any,
](
ap func(HKTS1) func(HKTS1S2) HKTS2,
mmap func(func(B) func(S1) S2) func(HKTB) HKTS1S2,
s SET, fb HKTB) func(HKTS1) HKTS2 {
return F.Flow2(
ap,
I.Ap[HKTS2, HKTS1S2](mmap(s)(fb)),
)
}
func Let[SET ~func(B) func(S1) S2, FCT ~func(S1) B, S1, S2, B, HKTS1, HKTS2 any](
mmap func(func(S1) S2) func(HKTS1) HKTS2,
s SET,
f FCT,
) func(HKTS1) HKTS2 {
return mmap(F.Flow3(
T.Replicate2[S1],
T.Map2(F.Flow2(
I.Ap[S2, S1],
F.Bind1st(F.Flow2[SET, func(func(S1) S2) S2], s)), f),
T.Tupled2(I.MonadAp[S2, B]),
))
}

View File

@ -52,10 +52,59 @@ func ChainFirst[A, B, HKTA, HKTB any](
} }
func Chain[A, B, HKTA, HKTB any]( func Chain[A, B, HKTA, HKTB any](
mchain func(HKTA, func(A) HKTB) HKTB, mchain func(func(A) HKTB) func(HKTA) HKTB,
f func(A) HKTB, f func(A) HKTB,
) func(HKTA) HKTB { ) func(HKTA) HKTB {
return func(first HKTA) HKTB { return mchain(f)
return MonadChain[A, B](mchain, first, f)
} }
func MonadBind[S1, S2, B, HKTS1, HKTS2, HKTB any](
mchain func(HKTS1, func(S1) HKTS2) HKTS2,
mmap func(HKTB, func(B) S2) HKTS2,
first HKTS1,
key func(B) func(S1) S2,
f func(S1) HKTB,
) HKTS2 {
return mchain(first, func(s1 S1) HKTS2 {
return mmap(f(s1), func(b B) S2 {
return key(b)(s1)
})
})
}
func Bind[S1, S2, B, HKTS1, HKTS2, HKTB any](
mchain func(func(S1) HKTS2) func(HKTS1) HKTS2,
mmap func(func(B) S2) func(HKTB) HKTS2,
key func(B) func(S1) S2,
f func(S1) HKTB,
) func(HKTS1) HKTS2 {
mapb := F.Flow2(
F.Flip(key),
mmap,
)
return mchain(func(s1 S1) HKTS2 {
return F.Pipe2(
s1,
f,
F.Pipe1(
s1,
mapb,
),
)
})
}
func BindTo[S1, B, HKTS1, HKTB any](
mmap func(func(B) S1) func(HKTB) HKTS1,
key func(B) S1,
) func(fa HKTB) HKTS1 {
return mmap(key)
}
func MonadBindTo[S1, B, HKTS1, HKTB any](
mmap func(HKTB, func(B) S1) HKTS1,
first HKTB,
key func(B) S1,
) HKTS1 {
return mmap(first, key)
} }

View File

@ -47,7 +47,7 @@ func MonadChainIOK[GR ~func() B, A, B, HKTA, HKTB any](
} }
func ChainIOK[GR ~func() B, A, B, HKTA, HKTB any]( func ChainIOK[GR ~func() B, A, B, HKTA, HKTB any](
mchain func(HKTA, func(A) HKTB) HKTB, mchain func(func(A) HKTB) func(HKTA) HKTB,
fromio func(GR) HKTB, fromio func(GR) HKTB,
f func(A) GR) func(HKTA) HKTB { f func(A) GR) func(HKTA) HKTB {
// chain // chain

View File

@ -48,7 +48,7 @@ func MonadChainIOEitherK[GIOB ~func() ET.Either[E, B], E, A, B, HKTA, HKTB any](
} }
func ChainIOEitherK[GIOB ~func() ET.Either[E, B], E, A, B, HKTA, HKTB any]( func ChainIOEitherK[GIOB ~func() ET.Either[E, B], E, A, B, HKTA, HKTB any](
mchain func(HKTA, func(A) HKTB) HKTB, mchain func(func(A) HKTB) func(HKTA) HKTB,
fromio func(GIOB) HKTB, fromio func(GIOB) HKTB,
f func(A) GIOB) func(HKTA) HKTB { f func(A) GIOB) func(HKTA) HKTB {
// chain // chain

View File

@ -21,6 +21,54 @@ import (
// HKTFGA = HKT[F, HKT[G, A]] // HKTFGA = HKT[F, HKT[G, A]]
// HKTFGB = HKT[F, HKT[G, B]] // HKTFGB = HKT[F, HKT[G, B]]
func MonadMap[A, B, HKTGA, HKTGB, HKTFGA, HKTFGB any](fmap func(HKTFGA, func(HKTGA) HKTGB) HKTFGB, gmap func(HKTGA, func(A) B) HKTGB, fa HKTFGA, f func(A) B) HKTFGB { func MonadMap[A, B, HKTGA, HKTGB, HKTFGA, HKTFGB any](
fmap func(HKTFGA, func(HKTGA) HKTGB) HKTFGB,
gmap func(HKTGA, func(A) B) HKTGB,
fa HKTFGA,
f func(A) B) HKTFGB {
return fmap(fa, F.Bind2nd(gmap, f)) return fmap(fa, F.Bind2nd(gmap, f))
} }
func Map[A, B, HKTGA, HKTGB, HKTFGA, HKTFGB any](
fmap func(func(HKTGA) HKTGB) func(HKTFGA) HKTFGB,
gmap func(func(A) B) func(HKTGA) HKTGB,
f func(A) B) func(HKTFGA) HKTFGB {
return F.Pipe2(
f,
gmap,
fmap,
)
}
func MonadLet[S1, S2, B, HKTS1, HKTS2 any](
mmap func(HKTS1, func(S1) S2) HKTS2,
first HKTS1,
key func(B) func(S1) S2,
f func(S1) B,
) HKTS2 {
return mmap(first, func(s1 S1) S2 {
return key(f(s1))(s1)
})
}
func Let[S1, S2, B, HKTS1, HKTS2 any](
mmap func(func(S1) S2) func(HKTS1) HKTS2,
key func(B) func(S1) S2,
f func(S1) B,
) func(HKTS1) HKTS2 {
return mmap(func(s1 S1) S2 {
return key(f(s1))(s1)
})
}
func LetTo[S1, S2, B, HKTS1, HKTS2 any](
mmap func(func(S1) S2) func(HKTS1) HKTS2,
key func(B) func(S1) S2,
b B,
) func(HKTS1) HKTS2 {
return F.Pipe2(
b,
key,
mmap,
)
}

View File

@ -64,9 +64,6 @@ func MonadAp[A, B, HKTFAB, HKTFGAB, HKTFA, HKTFB any](
fmap func(HKTFAB, func(O.Option[func(A) B]) func(O.Option[A]) O.Option[B]) HKTFGAB, fmap func(HKTFAB, func(O.Option[func(A) B]) func(O.Option[A]) O.Option[B]) HKTFGAB,
fab HKTFAB, fab HKTFAB,
fa HKTFA) HKTFB { fa HKTFA) HKTFB {
// HKTGA = O.Option[A]
// HKTGB = O.Option[B]
// HKTGAB = O.Option[func(a A) B]
return apply.MonadAp(fap, fmap, O.MonadAp[B, A], fab, fa) return apply.MonadAp(fap, fmap, O.MonadAp[B, A], fab, fa)
} }

View File

@ -23,10 +23,25 @@ import (
// here we implement the monadic operations using callbacks from // here we implement the monadic operations using callbacks from
// higher kinded types, as good a golang allows use to do this // higher kinded types, as good a golang allows use to do this
func MonadMap[GEA ~func(E) HKTA, GEB ~func(E) HKTB, E, A, B, HKTA, HKTB any](fmap func(HKTA, func(A) B) HKTB, fa GEA, f func(A) B) GEB { func MonadMap[GEA ~func(E) HKTA, GEB ~func(E) HKTB, E, A, B, HKTA, HKTB any](
fmap func(HKTA, func(A) B) HKTB,
fa GEA,
f func(A) B,
) GEB {
return R.MonadMap[GEA, GEB](fa, F.Bind2nd(fmap, f)) return R.MonadMap[GEA, GEB](fa, F.Bind2nd(fmap, f))
} }
func Map[GEA ~func(E) HKTA, GEB ~func(E) HKTB, E, A, B, HKTA, HKTB any](
fmap func(func(A) B) func(HKTA) HKTB,
f func(A) B,
) func(GEA) GEB {
return F.Pipe2(
f,
fmap,
R.Map[GEA, GEB, E, HKTA, HKTB],
)
}
func MonadChain[GEA ~func(E) HKTA, GEB ~func(E) HKTB, A, E, HKTA, HKTB any](fchain func(HKTA, func(A) HKTB) HKTB, ma GEA, f func(A) GEB) GEB { func MonadChain[GEA ~func(E) HKTA, GEB ~func(E) HKTB, A, E, HKTA, HKTB any](fchain func(HKTA, func(A) HKTB) HKTB, ma GEA, f func(A) GEB) GEB {
return R.MakeReader(func(r E) HKTB { return R.MakeReader(func(r E) HKTB {
return fchain(ma(r), func(a A) HKTB { return fchain(ma(r), func(a A) HKTB {

59
internal/utils/do.go Normal file
View File

@ -0,0 +1,59 @@
// 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 utils
import (
"fmt"
F "github.com/IBM/fp-go/function"
)
type (
Initial struct {
}
WithLastName struct {
Initial
LastName string
}
WithGivenName struct {
WithLastName
GivenName string
}
)
var (
Empty = Initial{}
SetLastName = F.Curry2(func(name string, s1 Initial) WithLastName {
return WithLastName{
Initial: s1,
LastName: name,
}
})
SetGivenName = F.Curry2(func(name string, s1 WithLastName) WithGivenName {
return WithGivenName{
WithLastName: s1,
GivenName: name,
}
})
)
func GetFullName(s WithGivenName) string {
return fmt.Sprintf("%s %s", s.GivenName, s.LastName)
}

66
io/bind.go Normal file
View File

@ -0,0 +1,66 @@
// 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 io
import (
G "github.com/IBM/fp-go/io/generic"
)
// Bind creates an empty context of type [S] to be used with the [Bind] operation
func Do[S any](
empty S,
) IO[S] {
return G.Do[IO[S], S](empty)
}
// Bind attaches the result of a computation to a context [S1] to produce a context [S2]
func Bind[S1, S2, T any](
setter func(T) func(S1) S2,
f func(S1) IO[T],
) func(IO[S1]) IO[S2] {
return G.Bind[IO[S1], IO[S2], IO[T], S1, S2, T](setter, f)
}
// Let attaches the result of a computation to a context [S1] to produce a context [S2]
func Let[S1, S2, T any](
setter func(T) func(S1) S2,
f func(S1) T,
) func(IO[S1]) IO[S2] {
return G.Let[IO[S1], IO[S2], S1, S2, T](setter, f)
}
// LetTo attaches the a value to a context [S1] to produce a context [S2]
func LetTo[S1, S2, T any](
setter func(T) func(S1) S2,
b T,
) func(IO[S1]) IO[S2] {
return G.LetTo[IO[S1], IO[S2], S1, S2, T](setter, b)
}
// BindTo initializes a new state [S1] from a value [T]
func BindTo[S1, T any](
setter func(T) S1,
) func(IO[T]) IO[S1] {
return G.BindTo[IO[S1], IO[T], 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[S1, S2, T any](
setter func(T) func(S1) S2,
fa IO[T],
) func(IO[S1]) IO[S2] {
return G.ApS[IO[S1], IO[S2], IO[T], S1, S2, T](setter, fa)
}

56
io/bind_test.go Normal file
View File

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

89
io/generic/bind.go Normal file
View File

@ -0,0 +1,89 @@
// 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 (
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 creates an empty context of type [S] to be used with the [Bind] operation
func Do[GS ~func() S, S any](
empty S,
) GS {
return Of[GS](empty)
}
// Bind attaches the result of a computation to a context [S1] to produce a context [S2]
func Bind[GS1 ~func() S1, GS2 ~func() S2, GT ~func() T, S1, S2, T any](
setter func(T) func(S1) S2,
f func(S1) GT,
) func(GS1) GS2 {
return C.Bind(
Chain[GS1, GS2, S1, S2],
Map[GT, GS2, T, S2],
setter,
f,
)
}
// Let attaches the result of a computation to a context [S1] to produce a context [S2]
func Let[GS1 ~func() S1, GS2 ~func() S2, S1, S2, T any](
key func(T) func(S1) S2,
f func(S1) T,
) func(GS1) GS2 {
return F.Let(
Map[GS1, GS2, S1, S2],
key,
f,
)
}
// LetTo attaches the a value to a context [S1] to produce a context [S2]
func LetTo[GS1 ~func() S1, GS2 ~func() S2, S1, S2, B any](
key func(B) func(S1) S2,
b B,
) func(GS1) GS2 {
return F.LetTo(
Map[GS1, GS2, S1, S2],
key,
b,
)
}
// BindTo initializes a new state [S1] from a value [T]
func BindTo[GS1 ~func() S1, GT ~func() T, S1, T any](
setter func(T) S1,
) func(GT) GS1 {
return C.BindTo(
Map[GT, GS1, 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() S1, GS2 ~func() S2, GT ~func() T, S1, S2, T any](
setter func(T) func(S1) S2,
fa GT,
) func(GS1) GS2 {
return A.ApS(
Ap[GS2, func() func(T) S2, GT, S2, T],
Map[GS1, func() func(T) S2, S1, func(T) S2],
setter,
fa,
)
}

View File

@ -19,26 +19,48 @@ import (
G "github.com/IBM/fp-go/ioeither/generic" G "github.com/IBM/fp-go/ioeither/generic"
) )
// Bind applies a function to an input state and merges the result into that state // Bind creates an empty context of type [S] to be used with the [Bind] operation
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] { func Do[E, S any](
return G.Bind[IOEither[E, S1], IOEither[E, S2], IOEither[E, A], func(S1) IOEither[E, A]](s, f) empty S,
) IOEither[E, S] {
return G.Do[IOEither[E, S], E, S](empty)
} }
// BindTo initializes some state based on a value // Bind attaches the result of a computation to a context [S1] to produce a context [S2]
func BindTo[ func Bind[E, S1, S2, T any](
E, A, S2 any](s func(A) S2) func(IOEither[E, A]) IOEither[E, S2] { setter func(T) func(S1) S2,
return G.BindTo[IOEither[E, S2], IOEither[E, A]](s) f func(S1) IOEither[E, T],
}
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,
) func(IOEither[E, S1]) IOEither[E, S2] { ) 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) 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]] { func FromStrictEquals[E, A comparable]() EQ.Eq[IOEither[E, A]] {
return G.FromStrictEquals[IOEither[E, A]]() return G.FromStrictEquals[IOEither[E, A]]()
} }

View File

@ -17,63 +17,74 @@ package generic
import ( import (
ET "github.com/IBM/fp-go/either" 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 // Bind creates an empty context of type [S] to be used with the [Bind] operation
func Bind[ func Do[GS ~func() ET.Either[E, S], E, S any](
GS1 ~func() ET.Either[E, S1], empty S,
GS2 ~func() ET.Either[E, S2], ) GS {
GA ~func() ET.Either[E, A], return Of[GS, E, S](empty)
FCT ~func(S1) GA, }
E any,
SET ~func(A) func(S1) S2, // Bind attaches the result of a computation to a context [S1] to produce a context [S2]
A, S1, S2 any](s SET, f FCT) func(GS1) GS2 { 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](
return G.Bind( setter func(T) func(S1) S2,
f func(S1) GT,
) func(GS1) GS2 {
return C.Bind(
Chain[GS1, GS2, E, S1, S2], Chain[GS1, GS2, E, S1, S2],
Map[GA, GS2, E, A, S2], Map[GT, GS2, E, T, S2],
s, setter,
f, f,
) )
} }
// BindTo initializes some state based on a value // Let attaches the result of a computation to a context [S1] to produce a context [S2]
func BindTo[ func Let[GS1 ~func() ET.Either[E, S1], GS2 ~func() ET.Either[E, S2], E, S1, S2, T any](
GS2 ~func() ET.Either[E, S2], key func(T) func(S1) S2,
GA ~func() ET.Either[E, A], f func(S1) T,
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,
) func(GS1) GS2 { ) 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) 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] { func FromStrictEquals[GA ~func() ET.Either[E, A], E, A comparable]() EQ.Eq[GA] {
return Eq[GA](ET.FromStrictEquals[E, A]()) 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 { 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( return FI.ChainIOK(
MonadChain[GA, GB, E, A, B], Chain[GA, GB, E, A, B],
FromIO[GB, GR, E, B], FromIO[GB, GR, E, B],
f, f,
) )

66
iooption/bind.go Normal file
View File

@ -0,0 +1,66 @@
// 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 iooption
import (
G "github.com/IBM/fp-go/iooption/generic"
)
// Bind creates an empty context of type [S] to be used with the [Bind] operation
func Do[S any](
empty S,
) IOOption[S] {
return G.Do[IOOption[S], S](empty)
}
// Bind attaches the result of a computation to a context [S1] to produce a context [S2]
func Bind[S1, S2, T any](
setter func(T) func(S1) S2,
f func(S1) IOOption[T],
) func(IOOption[S1]) IOOption[S2] {
return G.Bind[IOOption[S1], IOOption[S2], IOOption[T], S1, S2, T](setter, f)
}
// Let attaches the result of a computation to a context [S1] to produce a context [S2]
func Let[S1, S2, T any](
setter func(T) func(S1) S2,
f func(S1) T,
) func(IOOption[S1]) IOOption[S2] {
return G.Let[IOOption[S1], IOOption[S2], S1, S2, T](setter, f)
}
// LetTo attaches the a value to a context [S1] to produce a context [S2]
func LetTo[S1, S2, T any](
setter func(T) func(S1) S2,
b T,
) func(IOOption[S1]) IOOption[S2] {
return G.LetTo[IOOption[S1], IOOption[S2], S1, S2, T](setter, b)
}
// BindTo initializes a new state [S1] from a value [T]
func BindTo[S1, T any](
setter func(T) S1,
) func(IOOption[T]) IOOption[S1] {
return G.BindTo[IOOption[S1], IOOption[T], 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[S1, S2, T any](
setter func(T) func(S1) S2,
fa IOOption[T],
) func(IOOption[S1]) IOOption[S2] {
return G.ApS[IOOption[S1], IOOption[S2], IOOption[T], S1, S2, T](setter, fa)
}

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

31
iooption/eq.go Normal file
View File

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

90
iooption/generic/bind.go Normal file
View File

@ -0,0 +1,90 @@
// 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 (
A "github.com/IBM/fp-go/internal/apply"
C "github.com/IBM/fp-go/internal/chain"
F "github.com/IBM/fp-go/internal/functor"
O "github.com/IBM/fp-go/option"
)
// Bind creates an empty context of type [S] to be used with the [Bind] operation
func Do[GS ~func() O.Option[S], S any](
empty S,
) GS {
return Of[GS](empty)
}
// Bind attaches the result of a computation to a context [S1] to produce a context [S2]
func Bind[GS1 ~func() O.Option[S1], GS2 ~func() O.Option[S2], GT ~func() O.Option[T], S1, S2, T any](
setter func(T) func(S1) S2,
f func(S1) GT,
) func(GS1) GS2 {
return C.Bind(
Chain[GS1, GS2, S1, S2],
Map[GT, GS2, T, S2],
setter,
f,
)
}
// Let attaches the result of a computation to a context [S1] to produce a context [S2]
func Let[GS1 ~func() O.Option[S1], GS2 ~func() O.Option[S2], S1, S2, T any](
key func(T) func(S1) S2,
f func(S1) T,
) func(GS1) GS2 {
return F.Let(
Map[GS1, GS2, S1, S2],
key,
f,
)
}
// LetTo attaches the a value to a context [S1] to produce a context [S2]
func LetTo[GS1 ~func() O.Option[S1], GS2 ~func() O.Option[S2], S1, S2, B any](
key func(B) func(S1) S2,
b B,
) func(GS1) GS2 {
return F.LetTo(
Map[GS1, GS2, S1, S2],
key,
b,
)
}
// BindTo initializes a new state [S1] from a value [T]
func BindTo[GS1 ~func() O.Option[S1], GT ~func() O.Option[T], S1, T any](
setter func(T) S1,
) func(GT) GS1 {
return C.BindTo(
Map[GT, GS1, 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() O.Option[S1], GS2 ~func() O.Option[S2], GT ~func() O.Option[T], S1, S2, T any](
setter func(T) func(S1) S2,
fa GT,
) func(GS1) GS2 {
return A.ApS(
Ap[GS2, func() O.Option[func(T) S2], GT, T, S2],
Map[GS1, func() O.Option[func(T) S2], S1, func(T) S2],
setter,
fa,
)
}

32
iooption/generic/eq.go Normal file
View File

@ -0,0 +1,32 @@
// 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/eq"
G "github.com/IBM/fp-go/io/generic"
O "github.com/IBM/fp-go/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

@ -145,7 +145,7 @@ func MonadChainIOK[GA ~func() O.Option[A], GB ~func() O.Option[B], GR ~func() B,
func ChainIOK[GA ~func() O.Option[A], GB ~func() O.Option[B], GR ~func() B, A, B any](f func(A) GR) func(GA) GB { func ChainIOK[GA ~func() O.Option[A], GB ~func() O.Option[B], GR ~func() B, A, B any](f func(A) GR) func(GA) GB {
return FI.ChainIOK( return FI.ChainIOK(
MonadChain[GA, GB, A, B], Chain[GA, GB, A, B],
FromIO[GB, GR, B], FromIO[GB, GR, B],
f, f,
) )

74
iooption/testing/laws.go Normal file
View File

@ -0,0 +1,74 @@
// 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 testing
import (
"testing"
EQ "github.com/IBM/fp-go/eq"
L "github.com/IBM/fp-go/internal/monad/testing"
IOO "github.com/IBM/fp-go/iooption"
)
// AssertLaws asserts the apply monad laws for the `Either` monad
func AssertLaws[A, B, C any](t *testing.T,
eqa EQ.Eq[A],
eqb EQ.Eq[B],
eqc EQ.Eq[C],
ab func(A) B,
bc func(B) C,
) func(a A) bool {
return L.AssertLaws(t,
IOO.Eq(eqa),
IOO.Eq(eqb),
IOO.Eq(eqc),
IOO.Of[A],
IOO.Of[B],
IOO.Of[C],
IOO.Of[func(A) A],
IOO.Of[func(A) B],
IOO.Of[func(B) C],
IOO.Of[func(func(A) B) B],
IOO.MonadMap[A, A],
IOO.MonadMap[A, B],
IOO.MonadMap[A, C],
IOO.MonadMap[B, C],
IOO.MonadMap[func(B) C, func(func(A) B) func(A) C],
IOO.MonadChain[A, A],
IOO.MonadChain[A, B],
IOO.MonadChain[A, C],
IOO.MonadChain[B, C],
IOO.MonadAp[A, A],
IOO.MonadAp[B, A],
IOO.MonadAp[C, B],
IOO.MonadAp[C, A],
IOO.MonadAp[B, func(A) B],
IOO.MonadAp[func(A) C, func(A) B],
ab,
bc,
)
}

View File

@ -0,0 +1,47 @@
// 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 testing
import (
"fmt"
"testing"
EQ "github.com/IBM/fp-go/eq"
"github.com/stretchr/testify/assert"
)
func TestMonadLaws(t *testing.T) {
// some comparison
eqa := EQ.FromStrictEquals[bool]()
eqb := EQ.FromStrictEquals[int]()
eqc := EQ.FromStrictEquals[string]()
ab := func(a bool) int {
if a {
return 1
}
return 0
}
bc := func(b int) string {
return fmt.Sprintf("value %d", b)
}
laws := AssertLaws(t, eqa, eqb, eqc, ab, bc)
assert.True(t, laws(true))
assert.True(t, laws(false))
}

View File

@ -0,0 +1,66 @@
// 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 stateless
import (
G "github.com/IBM/fp-go/iterator/stateless/generic"
)
// Bind creates an empty context of type [S] to be used with the [Bind] operation
func Do[S any](
empty S,
) Iterator[S] {
return G.Do[Iterator[S], S](empty)
}
// Bind attaches the result of a computation to a context [S1] to produce a context [S2]
func Bind[S1, S2, T any](
setter func(T) func(S1) S2,
f func(S1) Iterator[T],
) func(Iterator[S1]) Iterator[S2] {
return G.Bind[Iterator[S1], Iterator[S2], Iterator[T], S1, S2, T](setter, f)
}
// Let attaches the result of a computation to a context [S1] to produce a context [S2]
func Let[S1, S2, T any](
setter func(T) func(S1) S2,
f func(S1) T,
) func(Iterator[S1]) Iterator[S2] {
return G.Let[Iterator[S1], Iterator[S2], S1, S2, T](setter, f)
}
// LetTo attaches the a value to a context [S1] to produce a context [S2]
func LetTo[S1, S2, T any](
setter func(T) func(S1) S2,
b T,
) func(Iterator[S1]) Iterator[S2] {
return G.LetTo[Iterator[S1], Iterator[S2], S1, S2, T](setter, b)
}
// BindTo initializes a new state [S1] from a value [T]
func BindTo[S1, T any](
setter func(T) S1,
) func(Iterator[T]) Iterator[S1] {
return G.BindTo[Iterator[S1], Iterator[T], 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[S1, S2, T any](
setter func(T) func(S1) S2,
fa Iterator[T],
) func(Iterator[S1]) Iterator[S2] {
return G.ApS[Iterator[func(T) S2], Iterator[S1], Iterator[S2], Iterator[T], S1, S2, T](setter, fa)
}

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

View File

@ -0,0 +1,92 @@
// 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 (
"github.com/IBM/fp-go/internal/apply"
C "github.com/IBM/fp-go/internal/chain"
F "github.com/IBM/fp-go/internal/functor"
O "github.com/IBM/fp-go/option"
T "github.com/IBM/fp-go/tuple"
)
// Bind creates an empty context of type [S] to be used with the [Bind] operation
func Do[GS ~func() O.Option[T.Tuple2[GS, S]], S any](
empty S,
) GS {
return Of[GS](empty)
}
// Bind attaches the result of a computation to a context [S1] to produce a context [S2]
func Bind[GS1 ~func() O.Option[T.Tuple2[GS1, S1]], GS2 ~func() O.Option[T.Tuple2[GS2, S2]], GA ~func() O.Option[T.Tuple2[GA, A]], S1, S2, A any](
setter func(A) func(S1) S2,
f func(S1) GA,
) func(GS1) GS2 {
return C.Bind(
Chain[GS2, GS1, S1, S2],
Map[GS2, GA, func(A) S2, A, S2],
setter,
f,
)
}
// Let attaches the result of a computation to a context [S1] to produce a context [S2]
func Let[GS1 ~func() O.Option[T.Tuple2[GS1, S1]], GS2 ~func() O.Option[T.Tuple2[GS2, S2]], S1, S2, A any](
key func(A) func(S1) S2,
f func(S1) A,
) func(GS1) GS2 {
return F.Let(
Map[GS2, GS1, func(S1) S2, S1, S2],
key,
f,
)
}
// LetTo attaches the a value to a context [S1] to produce a context [S2]
func LetTo[GS1 ~func() O.Option[T.Tuple2[GS1, S1]], GS2 ~func() O.Option[T.Tuple2[GS2, S2]], S1, S2, B any](
key func(B) func(S1) S2,
b B,
) func(GS1) GS2 {
return F.LetTo(
Map[GS2, GS1, func(S1) S2, S1, S2],
key,
b,
)
}
// BindTo initializes a new state [S1] from a value [T]
func BindTo[GS1 ~func() O.Option[T.Tuple2[GS1, S1]], GA ~func() O.Option[T.Tuple2[GA, A]], S1, A any](
setter func(A) S1,
) func(GA) GS1 {
return C.BindTo(
Map[GS1, GA, func(A) S1, A, 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[GAS2 ~func() O.Option[T.Tuple2[GAS2, func(A) S2]], GS1 ~func() O.Option[T.Tuple2[GS1, S1]], GS2 ~func() O.Option[T.Tuple2[GS2, S2]], GA ~func() O.Option[T.Tuple2[GA, A]], S1, S2, A any](
setter func(A) func(S1) S2,
fa GA,
) func(GS1) GS2 {
return apply.ApS(
Ap[GAS2, GS2, GA, A, S2],
Map[GAS2, GS1, func(S1) func(A) S2, S1, func(A) S2],
setter,
fa,
)
}

66
lazy/bind.go Normal file
View File

@ -0,0 +1,66 @@
// 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 lazy
import (
G "github.com/IBM/fp-go/io/generic"
)
// Bind creates an empty context of type [S] to be used with the [Bind] operation
func Do[S any](
empty S,
) Lazy[S] {
return G.Do[Lazy[S], S](empty)
}
// Bind attaches the result of a computation to a context [S1] to produce a context [S2]
func Bind[S1, S2, T any](
setter func(T) func(S1) S2,
f func(S1) Lazy[T],
) func(Lazy[S1]) Lazy[S2] {
return G.Bind[Lazy[S1], Lazy[S2], Lazy[T], S1, S2, T](setter, f)
}
// Let attaches the result of a computation to a context [S1] to produce a context [S2]
func Let[S1, S2, T any](
setter func(T) func(S1) S2,
f func(S1) T,
) func(Lazy[S1]) Lazy[S2] {
return G.Let[Lazy[S1], Lazy[S2], S1, S2, T](setter, f)
}
// LetTo attaches the a value to a context [S1] to produce a context [S2]
func LetTo[S1, S2, T any](
setter func(T) func(S1) S2,
b T,
) func(Lazy[S1]) Lazy[S2] {
return G.LetTo[Lazy[S1], Lazy[S2], S1, S2, T](setter, b)
}
// BindTo initializes a new state [S1] from a value [T]
func BindTo[S1, T any](
setter func(T) S1,
) func(Lazy[T]) Lazy[S1] {
return G.BindTo[Lazy[S1], Lazy[T], 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[S1, S2, T any](
setter func(T) func(S1) S2,
fa Lazy[T],
) func(Lazy[S1]) Lazy[S2] {
return G.ApS[Lazy[S1], Lazy[S2], Lazy[T], S1, S2, T](setter, fa)
}

56
lazy/bind_test.go Normal file
View File

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

89
option/bind.go Normal file
View File

@ -0,0 +1,89 @@
// 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 option
import (
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 creates an empty context of type [S] to be used with the [Bind] operation
func Do[S any](
empty S,
) Option[S] {
return Of(empty)
}
// Bind attaches the result of a computation to a context [S1] to produce a context [S2]
func Bind[S1, S2, A any](
setter func(A) func(S1) S2,
f func(S1) Option[A],
) func(Option[S1]) Option[S2] {
return C.Bind(
Chain[S1, S2],
Map[A, S2],
setter,
f,
)
}
// Let attaches the result of a computation to a context [S1] to produce a context [S2]
func Let[S1, S2, B any](
key func(B) func(S1) S2,
f func(S1) B,
) func(Option[S1]) Option[S2] {
return F.Let(
Map[S1, S2],
key,
f,
)
}
// LetTo attaches the a value to a context [S1] to produce a context [S2]
func LetTo[S1, S2, B any](
key func(B) func(S1) S2,
b B,
) func(Option[S1]) Option[S2] {
return F.LetTo(
Map[S1, S2],
key,
b,
)
}
// BindTo initializes a new state [S1] from a value [T]
func BindTo[S1, T any](
setter func(T) S1,
) func(Option[T]) Option[S1] {
return C.BindTo(
Map[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[S1, S2, T any](
setter func(T) func(S1) S2,
fa Option[T],
) func(Option[S1]) Option[S2] {
return A.ApS(
Ap[S2, T],
Map[S1, func(T) S2],
setter,
fa,
)
}

56
option/bind_test.go Normal file
View File

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

View File

@ -31,7 +31,7 @@ func Eq[A any](a EQ.Eq[A]) EQ.Eq[Option[A]] {
return EQ.FromEquals(F.Uncurry2(fld)) return EQ.FromEquals(F.Uncurry2(fld))
} }
// FromStrictEquals constructs an `Eq` from the canonical comparison function // FromStrictEquals constructs an [EQ.Eq] from the canonical comparison function
func FromStrictEquals[A comparable]() EQ.Eq[Option[A]] { func FromStrictEquals[A comparable]() EQ.Eq[Option[A]] {
return Eq(EQ.FromStrictEquals[A]()) return Eq(EQ.FromStrictEquals[A]())
} }

66
reader/bind.go Normal file
View File

@ -0,0 +1,66 @@
// 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 reader
import (
G "github.com/IBM/fp-go/reader/generic"
)
// Bind creates an empty context of type [S] to be used with the [Bind] operation
func Do[R, S any](
empty S,
) Reader[R, S] {
return G.Do[Reader[R, S], R, S](empty)
}
// Bind attaches the result of a computation to a context [S1] to produce a context [S2]
func Bind[R, S1, S2, T any](
setter func(T) func(S1) S2,
f func(S1) Reader[R, T],
) func(Reader[R, S1]) Reader[R, S2] {
return G.Bind[Reader[R, S1], Reader[R, S2], Reader[R, T], R, S1, S2, T](setter, f)
}
// Let attaches the result of a computation to a context [S1] to produce a context [S2]
func Let[R, S1, S2, T any](
setter func(T) func(S1) S2,
f func(S1) T,
) func(Reader[R, S1]) Reader[R, S2] {
return G.Let[Reader[R, S1], Reader[R, S2], R, S1, S2, T](setter, f)
}
// LetTo attaches the a value to a context [S1] to produce a context [S2]
func LetTo[R, S1, S2, T any](
setter func(T) func(S1) S2,
b T,
) func(Reader[R, S1]) Reader[R, S2] {
return G.LetTo[Reader[R, S1], Reader[R, S2], R, S1, S2, T](setter, b)
}
// BindTo initializes a new state [S1] from a value [T]
func BindTo[R, S1, T any](
setter func(T) S1,
) func(Reader[R, T]) Reader[R, S1] {
return G.BindTo[Reader[R, S1], Reader[R, T], R, 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[R, S1, S2, T any](
setter func(T) func(S1) S2,
fa Reader[R, T],
) func(Reader[R, S1]) Reader[R, S2] {
return G.ApS[Reader[R, S1], Reader[R, S2], Reader[R, T], R, S1, S2, T](setter, fa)
}

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

89
reader/generic/bind.go Normal file
View File

@ -0,0 +1,89 @@
// 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 (
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 creates an empty context of type [S] to be used with the [Bind] operation
func Do[GS ~func(R) S, R, S any](
empty S,
) GS {
return Of[GS, R, S](empty)
}
// Bind attaches the result of a computation to a context [S1] to produce a context [S2]
func Bind[GS1 ~func(R) S1, GS2 ~func(R) S2, GT ~func(R) T, R, S1, S2, T any](
setter func(T) func(S1) S2,
f func(S1) GT,
) func(GS1) GS2 {
return C.Bind(
Chain[GS1, GS2, R, S1, S2],
Map[GT, GS2, R, T, S2],
setter,
f,
)
}
// Let attaches the result of a computation to a context [S1] to produce a context [S2]
func Let[GS1 ~func(R) S1, GS2 ~func(R) S2, R, S1, S2, T any](
key func(T) func(S1) S2,
f func(S1) T,
) func(GS1) GS2 {
return F.Let(
Map[GS1, GS2, R, S1, S2],
key,
f,
)
}
// LetTo attaches the a value to a context [S1] to produce a context [S2]
func LetTo[GS1 ~func(R) S1, GS2 ~func(R) S2, R, S1, S2, B any](
key func(B) func(S1) S2,
b B,
) func(GS1) GS2 {
return F.LetTo(
Map[GS1, GS2, R, S1, S2],
key,
b,
)
}
// BindTo initializes a new state [S1] from a value [T]
func BindTo[GS1 ~func(R) S1, GT ~func(R) T, R, S1, T any](
setter func(T) S1,
) func(GT) GS1 {
return C.BindTo(
Map[GT, GS1, R, 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(R) S1, GS2 ~func(R) S2, GT ~func(R) T, R, S1, S2, T any](
setter func(T) func(S1) S2,
fa GT,
) func(GS1) GS2 {
return A.ApS(
Ap[GT, GS2, func(R) func(T) S2, R, T, S2],
Map[GS1, func(R) func(T) S2, R, S1, func(T) S2],
setter,
fa,
)
}

66
readereither/bind.go Normal file
View File

@ -0,0 +1,66 @@
// 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 readereither
import (
G "github.com/IBM/fp-go/readereither/generic"
)
// Bind creates an empty context of type [S] to be used with the [Bind] operation
func Do[R, E, S any](
empty S,
) ReaderEither[R, E, S] {
return G.Do[ReaderEither[R, E, S], R, E, S](empty)
}
// Bind attaches the result of a computation to a context [S1] to produce a context [S2]
func Bind[R, E, S1, S2, T any](
setter func(T) func(S1) S2,
f func(S1) ReaderEither[R, E, T],
) func(ReaderEither[R, E, S1]) ReaderEither[R, E, S2] {
return G.Bind[ReaderEither[R, E, S1], ReaderEither[R, E, S2], ReaderEither[R, E, T], R, E, S1, S2, T](setter, f)
}
// Let attaches the result of a computation to a context [S1] to produce a context [S2]
func Let[R, E, S1, S2, T any](
setter func(T) func(S1) S2,
f func(S1) T,
) func(ReaderEither[R, E, S1]) ReaderEither[R, E, S2] {
return G.Let[ReaderEither[R, E, S1], ReaderEither[R, E, S2], R, E, S1, S2, T](setter, f)
}
// LetTo attaches the a value to a context [S1] to produce a context [S2]
func LetTo[R, E, S1, S2, T any](
setter func(T) func(S1) S2,
b T,
) func(ReaderEither[R, E, S1]) ReaderEither[R, E, S2] {
return G.LetTo[ReaderEither[R, E, S1], ReaderEither[R, E, S2], R, E, S1, S2, T](setter, b)
}
// BindTo initializes a new state [S1] from a value [T]
func BindTo[R, E, S1, T any](
setter func(T) S1,
) func(ReaderEither[R, E, T]) ReaderEither[R, E, S1] {
return G.BindTo[ReaderEither[R, E, S1], ReaderEither[R, E, T], R, 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[R, E, S1, S2, T any](
setter func(T) func(S1) S2,
fa ReaderEither[R, E, T],
) func(ReaderEither[R, E, S1]) ReaderEither[R, E, S2] {
return G.ApS[ReaderEither[R, E, S1], ReaderEither[R, E, S2], ReaderEither[R, E, T], R, E, S1, S2, T](setter, fa)
}

58
readereither/bind_test.go Normal file
View File

@ -0,0 +1,58 @@
// 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 readereither
import (
"context"
"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) ReaderEither[context.Context, error, string] {
return Of[context.Context, error]("Doe")
}
func getGivenName(s utils.WithLastName) ReaderEither[context.Context, error, string] {
return Of[context.Context, error]("John")
}
func TestBind(t *testing.T) {
res := F.Pipe3(
Do[context.Context, error](utils.Empty),
Bind(utils.SetLastName, getLastName),
Bind(utils.SetGivenName, getGivenName),
Map[context.Context, error](utils.GetFullName),
)
assert.Equal(t, res(context.Background()), E.Of[error]("John Doe"))
}
func TestApS(t *testing.T) {
res := F.Pipe3(
Do[context.Context, error](utils.Empty),
ApS(utils.SetLastName, Of[context.Context, error]("Doe")),
ApS(utils.SetGivenName, Of[context.Context, error]("John")),
Map[context.Context, error](utils.GetFullName),
)
assert.Equal(t, res(context.Background()), E.Of[error]("John Doe"))
}

View File

@ -0,0 +1,90 @@
// 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/either"
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 creates an empty context of type [S] to be used with the [Bind] operation
func Do[GS ~func(R) ET.Either[E, S], R, E, S any](
empty S,
) GS {
return Of[GS, E, R, S](empty)
}
// Bind attaches the result of a computation to a context [S1] to produce a context [S2]
func Bind[GS1 ~func(R) ET.Either[E, S1], GS2 ~func(R) ET.Either[E, S2], GT ~func(R) ET.Either[E, T], R, 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, R, S1, S2],
Map[GT, GS2, E, R, T, S2],
setter,
f,
)
}
// Let attaches the result of a computation to a context [S1] to produce a context [S2]
func Let[GS1 ~func(R) ET.Either[E, S1], GS2 ~func(R) ET.Either[E, S2], R, E, S1, S2, T any](
key func(T) func(S1) S2,
f func(S1) T,
) func(GS1) GS2 {
return F.Let(
Map[GS1, GS2, E, R, S1, S2],
key,
f,
)
}
// LetTo attaches the a value to a context [S1] to produce a context [S2]
func LetTo[GS1 ~func(R) ET.Either[E, S1], GS2 ~func(R) ET.Either[E, S2], R, E, S1, S2, B any](
key func(B) func(S1) S2,
b B,
) func(GS1) GS2 {
return F.LetTo(
Map[GS1, GS2, E, R, S1, S2],
key,
b,
)
}
// BindTo initializes a new state [S1] from a value [T]
func BindTo[GS1 ~func(R) ET.Either[E, S1], GT ~func(R) ET.Either[E, T], R, E, S1, T any](
setter func(T) S1,
) func(GT) GS1 {
return C.BindTo(
Map[GT, GS1, E, R, 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(R) ET.Either[E, S1], GS2 ~func(R) ET.Either[E, S2], GT ~func(R) ET.Either[E, T], R, E, S1, S2, T any](
setter func(T) func(S1) S2,
fa GT,
) func(GS1) GS2 {
return A.ApS(
Ap[GT, GS2, func(R) ET.Either[E, func(T) S2], E, R, T, S2],
Map[GS1, func(R) ET.Either[E, func(T) S2], E, R, S1, func(T) S2],
setter,
fa,
)
}

View File

@ -60,7 +60,7 @@ func MonadMap[GEA ~func(E) ET.Either[L, A], GEB ~func(E) ET.Either[L, B], L, E,
} }
func Map[GEA ~func(E) ET.Either[L, A], GEB ~func(E) ET.Either[L, B], L, E, A, B any](f func(A) B) func(GEA) GEB { func Map[GEA ~func(E) ET.Either[L, A], GEB ~func(E) ET.Either[L, B], L, E, A, B any](f func(A) B) func(GEA) GEB {
return F.Bind2nd(MonadMap[GEA, GEB, L, E, A, B], f) return readert.Map[GEA, GEB](ET.Map[L, A, B], f)
} }
func MonadChain[GEA ~func(E) ET.Either[L, A], GEB ~func(E) ET.Either[L, B], L, E, A, B any](ma GEA, f func(A) GEB) GEB { func MonadChain[GEA ~func(E) ET.Either[L, A], GEB ~func(E) ET.Either[L, B], L, E, A, B any](ma GEA, f func(A) GEB) GEB {

67
readerio/bind.go Normal file
View File

@ -0,0 +1,67 @@
// 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 readerio
import (
IO "github.com/IBM/fp-go/io"
G "github.com/IBM/fp-go/readerio/generic"
)
// Bind creates an empty context of type [S] to be used with the [Bind] operation
func Do[R, S any](
empty S,
) ReaderIO[R, S] {
return G.Do[ReaderIO[R, S], IO.IO[S], R, S](empty)
}
// Bind attaches the result of a computation to a context [S1] to produce a context [S2]
func Bind[R, S1, S2, T any](
setter func(T) func(S1) S2,
f func(S1) ReaderIO[R, T],
) func(ReaderIO[R, S1]) ReaderIO[R, S2] {
return G.Bind[ReaderIO[R, S1], ReaderIO[R, S2], ReaderIO[R, T], IO.IO[S1], IO.IO[S2], IO.IO[T], R, S1, S2, T](setter, f)
}
// Let attaches the result of a computation to a context [S1] to produce a context [S2]
func Let[R, S1, S2, T any](
setter func(T) func(S1) S2,
f func(S1) T,
) func(ReaderIO[R, S1]) ReaderIO[R, S2] {
return G.Let[ReaderIO[R, S1], ReaderIO[R, S2], IO.IO[S1], IO.IO[S2], R, S1, S2, T](setter, f)
}
// LetTo attaches the a value to a context [S1] to produce a context [S2]
func LetTo[R, S1, S2, T any](
setter func(T) func(S1) S2,
b T,
) func(ReaderIO[R, S1]) ReaderIO[R, S2] {
return G.LetTo[ReaderIO[R, S1], ReaderIO[R, S2], IO.IO[S1], IO.IO[S2], R, S1, S2, T](setter, b)
}
// BindTo initializes a new state [S1] from a value [T]
func BindTo[R, S1, T any](
setter func(T) S1,
) func(ReaderIO[R, T]) ReaderIO[R, S1] {
return G.BindTo[ReaderIO[R, S1], ReaderIO[R, T], IO.IO[S1], IO.IO[T], R, 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[R, S1, S2, T any](
setter func(T) func(S1) S2,
fa ReaderIO[R, T],
) func(ReaderIO[R, S1]) ReaderIO[R, S2] {
return G.ApS[ReaderIO[R, func(T) S2], ReaderIO[R, S1], ReaderIO[R, S2], ReaderIO[R, T], IO.IO[func(T) S2], IO.IO[S1], IO.IO[S2], IO.IO[T], R, S1, S2, T](setter, fa)
}

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

89
readerio/generic/bind.go Normal file
View File

@ -0,0 +1,89 @@
// 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 (
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 creates an empty context of type [S] to be used with the [Bind] operation
func Do[GRS ~func(R) GS, GS ~func() S, R, S any](
empty S,
) GRS {
return Of[GRS, GS, R, S](empty)
}
// Bind attaches the result of a computation to a context [S1] to produce a context [S2]
func Bind[GRS1 ~func(R) GS1, GRS2 ~func(R) GS2, GRT ~func(R) GT, GS1 ~func() S1, GS2 ~func() S2, GT ~func() T, R, S1, S2, T any](
setter func(T) func(S1) S2,
f func(S1) GRT,
) func(GRS1) GRS2 {
return C.Bind(
Chain[GRS1, GRS2, GS1, GS2, R, S1, S2],
Map[GRT, GRS2, GT, GS2, R, T, S2],
setter,
f,
)
}
// Let attaches the result of a computation to a context [S1] to produce a context [S2]
func Let[GRS1 ~func(R) GS1, GRS2 ~func(R) GS2, GS1 ~func() S1, GS2 ~func() S2, R, S1, S2, T any](
key func(T) func(S1) S2,
f func(S1) T,
) func(GRS1) GRS2 {
return F.Let(
Map[GRS1, GRS2, GS1, GS2, R, S1, S2],
key,
f,
)
}
// LetTo attaches the a value to a context [S1] to produce a context [S2]
func LetTo[GRS1 ~func(R) GS1, GRS2 ~func(R) GS2, GS1 ~func() S1, GS2 ~func() S2, R, S1, S2, B any](
key func(B) func(S1) S2,
b B,
) func(GRS1) GRS2 {
return F.LetTo(
Map[GRS1, GRS2, GS1, GS2, R, S1, S2],
key,
b,
)
}
// BindTo initializes a new state [S1] from a value [T]
func BindTo[GRS1 ~func(R) GS1, GRT ~func(R) GT, GS1 ~func() S1, GT ~func() T, R, S1, T any](
setter func(T) S1,
) func(GRT) GRS1 {
return C.BindTo(
Map[GRT, GRS1, GT, GS1, R, 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[GRTS1 ~func(R) GTS1, GRS1 ~func(R) GS1, GRS2 ~func(R) GS2, GRT ~func(R) GT, GTS1 ~func() func(T) S2, GS1 ~func() S1, GS2 ~func() S2, GT ~func() T, R, S1, S2, T any](
setter func(T) func(S1) S2,
fa GRT,
) func(GRS1) GRS2 {
return A.ApS(
Ap[GRT, GRS2, GRTS1, GT, GS2, GTS1, R, T, S2],
Map[GRS1, GRTS1, GS1, GTS1, R, S1, func(T) S2],
setter,
fa,
)
}

View File

@ -40,7 +40,7 @@ func MonadMap[GEA ~func(E) GIOA, GEB ~func(E) GIOB, GIOA ~func() A, GIOB ~func()
} }
func Map[GEA ~func(E) GIOA, GEB ~func(E) GIOB, GIOA ~func() A, GIOB ~func() B, E, A, B any](f func(A) B) func(GEA) GEB { func Map[GEA ~func(E) GIOA, GEB ~func(E) GIOB, GIOA ~func() A, GIOB ~func() B, E, A, B any](f func(A) B) func(GEA) GEB {
return F.Bind2nd(MonadMap[GEA, GEB, GIOA, GIOB, E, A, B], f) return readert.Map[GEA, GEB](IO.Map[GIOA, GIOB, A, B], f)
} }
func MonadChain[GEA ~func(E) GIOA, GEB ~func(E) GIOB, GIOA ~func() A, GIOB ~func() B, E, A, B any](ma GEA, f func(A) GEB) GEB { func MonadChain[GEA ~func(E) GIOA, GEB ~func(E) GIOB, GIOA ~func() A, GIOB ~func() B, E, A, B any](ma GEA, f func(A) GEB) GEB {
@ -97,7 +97,7 @@ func MonadChainIOK[GEA ~func(E) GIOA, GEB ~func(E) GIOB, GIOA ~func() A, GIOB ~f
func ChainIOK[GEA ~func(E) GIOA, GEB ~func(E) GIOB, GIOA ~func() A, GIOB ~func() B, E, A, B any](f func(A) GIOB) func(GEA) GEB { func ChainIOK[GEA ~func(E) GIOA, GEB ~func(E) GIOB, GIOA ~func() A, GIOB ~func() B, E, A, B any](f func(A) GIOB) func(GEA) GEB {
return FIO.ChainIOK( return FIO.ChainIOK(
MonadChain[GEA, GEB], Chain[GEA, GEB],
FromIO[GEB], FromIO[GEB],
f, f,
) )

67
readerioeither/bind.go Normal file
View File

@ -0,0 +1,67 @@
// 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 readerioeither
import (
IOE "github.com/IBM/fp-go/ioeither"
G "github.com/IBM/fp-go/readerioeither/generic"
)
// Bind creates an empty context of type [S] to be used with the [Bind] operation
func Do[R, E, S any](
empty S,
) ReaderIOEither[R, E, S] {
return G.Do[ReaderIOEither[R, E, S], IOE.IOEither[E, S], R, E, S](empty)
}
// Bind attaches the result of a computation to a context [S1] to produce a context [S2]
func Bind[R, E, S1, S2, T any](
setter func(T) func(S1) S2,
f func(S1) ReaderIOEither[R, E, T],
) func(ReaderIOEither[R, E, S1]) ReaderIOEither[R, E, S2] {
return G.Bind[ReaderIOEither[R, E, S1], ReaderIOEither[R, E, S2], ReaderIOEither[R, E, T], IOE.IOEither[E, S1], IOE.IOEither[E, S2], IOE.IOEither[E, T], R, E, S1, S2, T](setter, f)
}
// Let attaches the result of a computation to a context [S1] to produce a context [S2]
func Let[R, E, S1, S2, T any](
setter func(T) func(S1) S2,
f func(S1) T,
) func(ReaderIOEither[R, E, S1]) ReaderIOEither[R, E, S2] {
return G.Let[ReaderIOEither[R, E, S1], ReaderIOEither[R, E, S2], IOE.IOEither[E, S1], IOE.IOEither[E, S2], R, E, S1, S2, T](setter, f)
}
// LetTo attaches the a value to a context [S1] to produce a context [S2]
func LetTo[R, E, S1, S2, T any](
setter func(T) func(S1) S2,
b T,
) func(ReaderIOEither[R, E, S1]) ReaderIOEither[R, E, S2] {
return G.LetTo[ReaderIOEither[R, E, S1], ReaderIOEither[R, E, S2], IOE.IOEither[E, S1], IOE.IOEither[E, S2], R, E, S1, S2, T](setter, b)
}
// BindTo initializes a new state [S1] from a value [T]
func BindTo[R, E, S1, T any](
setter func(T) S1,
) func(ReaderIOEither[R, E, T]) ReaderIOEither[R, E, S1] {
return G.BindTo[ReaderIOEither[R, E, S1], ReaderIOEither[R, E, T], IOE.IOEither[E, S1], IOE.IOEither[E, T], R, 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[R, E, S1, S2, T any](
setter func(T) func(S1) S2,
fa ReaderIOEither[R, E, T],
) func(ReaderIOEither[R, E, S1]) ReaderIOEither[R, E, S2] {
return G.ApS[ReaderIOEither[R, E, func(T) S2], ReaderIOEither[R, E, S1], ReaderIOEither[R, E, S2], ReaderIOEither[R, E, T], IOE.IOEither[E, func(T) S2], IOE.IOEither[E, S1], IOE.IOEither[E, S2], IOE.IOEither[E, T], R, E, S1, S2, T](setter, fa)
}

View File

@ -0,0 +1,58 @@
// 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 readerioeither
import (
"context"
"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) ReaderIOEither[context.Context, error, string] {
return Of[context.Context, error]("Doe")
}
func getGivenName(s utils.WithLastName) ReaderIOEither[context.Context, error, string] {
return Of[context.Context, error]("John")
}
func TestBind(t *testing.T) {
res := F.Pipe3(
Do[context.Context, error](utils.Empty),
Bind(utils.SetLastName, getLastName),
Bind(utils.SetGivenName, getGivenName),
Map[context.Context, error](utils.GetFullName),
)
assert.Equal(t, res(context.Background())(), E.Of[error]("John Doe"))
}
func TestApS(t *testing.T) {
res := F.Pipe3(
Do[context.Context, error](utils.Empty),
ApS(utils.SetLastName, Of[context.Context, error]("Doe")),
ApS(utils.SetGivenName, Of[context.Context, error]("John")),
Map[context.Context, error](utils.GetFullName),
)
assert.Equal(t, res(context.Background())(), E.Of[error]("John Doe"))
}

View File

@ -26,7 +26,7 @@ func Eq[R, E, A any](eq EQ.Eq[ET.Either[E, A]]) func(R) EQ.Eq[ReaderIOEither[R,
return G.Eq[ReaderIOEither[R, E, A]](eq) return G.Eq[ReaderIOEither[R, E, A]](eq)
} }
// FromStrictEquals constructs an `Eq` from the canonical comparison function // FromStrictEquals constructs an [EQ.Eq] from the canonical comparison function
func FromStrictEquals[R, E, A comparable]() func(R) EQ.Eq[ReaderIOEither[R, E, A]] { func FromStrictEquals[R, E, A comparable]() func(R) EQ.Eq[ReaderIOEither[R, E, A]] {
return G.FromStrictEquals[ReaderIOEither[R, E, A]]() return G.FromStrictEquals[ReaderIOEither[R, E, A]]()
} }

View File

@ -0,0 +1,90 @@
// 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/either"
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 creates an empty context of type [S] to be used with the [Bind] operation
func Do[GRS ~func(R) GS, GS ~func() ET.Either[E, S], R, E, S any](
empty S,
) GRS {
return Of[GRS, GS, R, E, S](empty)
}
// Bind attaches the result of a computation to a context [S1] to produce a context [S2]
func Bind[GRS1 ~func(R) GS1, GRS2 ~func(R) GS2, GRT ~func(R) GT, GS1 ~func() ET.Either[E, S1], GS2 ~func() ET.Either[E, S2], GT ~func() ET.Either[E, T], R, E, S1, S2, T any](
setter func(T) func(S1) S2,
f func(S1) GRT,
) func(GRS1) GRS2 {
return C.Bind(
Chain[GRS1, GRS2, GS1, GS2, R, E, S1, S2],
Map[GRT, GRS2, GT, GS2, R, E, T, S2],
setter,
f,
)
}
// Let attaches the result of a computation to a context [S1] to produce a context [S2]
func Let[GRS1 ~func(R) GS1, GRS2 ~func(R) GS2, GS1 ~func() ET.Either[E, S1], GS2 ~func() ET.Either[E, S2], R, E, S1, S2, T any](
key func(T) func(S1) S2,
f func(S1) T,
) func(GRS1) GRS2 {
return F.Let(
Map[GRS1, GRS2, GS1, GS2, R, E, S1, S2],
key,
f,
)
}
// LetTo attaches the a value to a context [S1] to produce a context [S2]
func LetTo[GRS1 ~func(R) GS1, GRS2 ~func(R) GS2, GS1 ~func() ET.Either[E, S1], GS2 ~func() ET.Either[E, S2], R, E, S1, S2, B any](
key func(B) func(S1) S2,
b B,
) func(GRS1) GRS2 {
return F.LetTo(
Map[GRS1, GRS2, GS1, GS2, R, E, S1, S2],
key,
b,
)
}
// BindTo initializes a new state [S1] from a value [T]
func BindTo[GRS1 ~func(R) GS1, GRT ~func(R) GT, GS1 ~func() ET.Either[E, S1], GT ~func() ET.Either[E, T], R, E, S1, T any](
setter func(T) S1,
) func(GRT) GRS1 {
return C.BindTo(
Map[GRT, GRS1, GT, GS1, R, 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[GRTS1 ~func(R) GTS1, GRS1 ~func(R) GS1, GRS2 ~func(R) GS2, GRT ~func(R) GT, GTS1 ~func() ET.Either[E, func(T) S2], GS1 ~func() ET.Either[E, S1], GS2 ~func() ET.Either[E, S2], GT ~func() ET.Either[E, T], R, E, S1, S2, T any](
setter func(T) func(S1) S2,
fa GRT,
) func(GRS1) GRS2 {
return A.ApS(
Ap[GRT, GRS2, GRTS1, GT, GS2, GTS1, R, E, T, S2],
Map[GRS1, GRTS1, GS1, GTS1, R, E, S1, func(T) S2],
setter,
fa,
)
}

View File

@ -26,7 +26,7 @@ func Eq[GEA ~func(R) GIOA, GIOA ~func() ET.Either[E, A], R, E, A any](eq EQ.Eq[E
return G.Eq[GEA](eq) return G.Eq[GEA](eq)
} }
// FromStrictEquals constructs an `Eq` from the canonical comparison function // FromStrictEquals constructs an [EQ.Eq] from the canonical comparison function
func FromStrictEquals[GEA ~func(R) GIOA, GIOA ~func() ET.Either[E, A], R any, E, A comparable]() func(R) EQ.Eq[GEA] { func FromStrictEquals[GEA ~func(R) GIOA, GIOA ~func() ET.Either[E, A], R any, E, A comparable]() func(R) EQ.Eq[GEA] {
return Eq[GEA](ET.FromStrictEquals[E, A]()) return Eq[GEA](ET.FromStrictEquals[E, A]())
} }

57
record/bind.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 record
import (
Mo "github.com/IBM/fp-go/monoid"
G "github.com/IBM/fp-go/record/generic"
)
// Bind creates an empty context of type [S] to be used with the [Bind] operation
func Do[K comparable, S any]() map[K]S {
return G.Do[map[K]S, K, S]()
}
// Bind attaches the result of a computation to a context [S1] to produce a context [S2]
func Bind[S1, T any, K comparable, S2 any](m Mo.Monoid[map[K]S2]) func(setter func(T) func(S1) S2, f func(S1) map[K]T) func(map[K]S1) map[K]S2 {
return G.Bind[map[K]S1, map[K]S2, map[K]T, K, S1, S2, T](m)
}
// Let attaches the result of a computation to a context [S1] to produce a context [S2]
func Let[S1, T any, K comparable, S2 any](
setter func(T) func(S1) S2,
f func(S1) T,
) func(map[K]S1) map[K]S2 {
return G.Let[map[K]S1, map[K]S2, K, S1, S2, T](setter, f)
}
// LetTo attaches the a value to a context [S1] to produce a context [S2]
func LetTo[S1, T any, K comparable, S2 any](
setter func(T) func(S1) S2,
b T,
) func(map[K]S1) map[K]S2 {
return G.LetTo[map[K]S1, map[K]S2, K, S1, S2, T](setter, b)
}
// BindTo initializes a new state [S1] from a value [T]
func BindTo[S1, T any, K comparable](setter func(T) S1) func(map[K]T) map[K]S1 {
return G.BindTo[map[K]S1, map[K]T, K, 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[S1, T any, K comparable, S2 any](m Mo.Monoid[map[K]S2]) func(setter func(T) func(S1) S2, fa map[K]T) func(map[K]S1) map[K]S2 {
return G.ApS[map[K]S1, map[K]S2, map[K]T, K, S1, S2, T](m)
}

86
record/generic/bind.go Normal file
View File

@ -0,0 +1,86 @@
// 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 (
A "github.com/IBM/fp-go/internal/apply"
C "github.com/IBM/fp-go/internal/chain"
F "github.com/IBM/fp-go/internal/functor"
Mo "github.com/IBM/fp-go/monoid"
)
// Bind creates an empty context of type [S] to be used with the [Bind] operation
func Do[GS ~map[K]S, K comparable, S any]() GS {
return Empty[GS, K, S]()
}
// Bind attaches the result of a computation to a context [S1] to produce a context [S2]
func Bind[GS1 ~map[K]S1, GS2 ~map[K]S2, GT ~map[K]T, K comparable, S1, S2, T any](m Mo.Monoid[GS2]) func(setter func(T) func(S1) S2, f func(S1) GT) func(GS1) GS2 {
c := Chain[GS1, GS2, K, S1, S2](m)
return func(setter func(T) func(S1) S2, f func(S1) GT) func(GS1) GS2 {
return C.Bind(
c,
Map[GT, GS2, K, T, S2],
setter,
f,
)
}
}
// Let attaches the result of a computation to a context [S1] to produce a context [S2]
func Let[GS1 ~map[K]S1, GS2 ~map[K]S2, K comparable, S1, S2, T any](
key func(T) func(S1) S2,
f func(S1) T,
) func(GS1) GS2 {
return F.Let(
Map[GS1, GS2, K, S1, S2],
key,
f,
)
}
// LetTo attaches the a value to a context [S1] to produce a context [S2]
func LetTo[GS1 ~map[K]S1, GS2 ~map[K]S2, K comparable, S1, S2, B any](
key func(B) func(S1) S2,
b B,
) func(GS1) GS2 {
return F.LetTo(
Map[GS1, GS2, K, S1, S2],
key,
b,
)
}
// BindTo initializes a new state [S1] from a value [T]
func BindTo[GS1 ~map[K]S1, GT ~map[K]T, K comparable, S1, T any](setter func(T) S1) func(GT) GS1 {
return C.BindTo(
Map[GT, GS1, K, 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 ~map[K]S1, GS2 ~map[K]S2, GT ~map[K]T, K comparable, S1, S2, T any](m Mo.Monoid[GS2]) func(setter func(T) func(S1) S2, fa GT) func(GS1) GS2 {
a := Ap[GS2, map[K]func(T) S2, GT, K, S2, T](m)
return func(setter func(T) func(S1) S2, fa GT) func(GS1) GS2 {
return A.ApS(
a,
Map[GS1, map[K]func(T) S2, K, S1, func(T) S2],
setter,
fa,
)
}
}

View File

@ -132,6 +132,18 @@ func ReduceRefWithIndex[M ~map[K]V, K comparable, V, R any](f func(K, R, *V) R,
} }
} }
func MonadAp[BS ~map[K]B, ABS ~map[K]func(A) B, AS ~map[K]A, K comparable, B, A any](m Mo.Monoid[BS], fab ABS, fa AS) BS {
return MonadChain(m, fab, F.Bind1st(MonadMap[AS, BS, K, A, B], fa))
}
func Ap[BS ~map[K]B, ABS ~map[K]func(A) B, AS ~map[K]A, K comparable, B, A any](m Mo.Monoid[BS]) func(fa AS) func(ABS) BS {
return func(ma AS) func(ABS) BS {
return func(abs ABS) BS {
return MonadAp(m, abs, ma)
}
}
}
func MonadMap[M ~map[K]V, N ~map[K]R, K comparable, V, R any](r M, f func(V) R) N { func MonadMap[M ~map[K]V, N ~map[K]R, K comparable, V, R any](r M, f func(V) R) N {
return MonadMapWithIndex[M, N](r, F.Ignore1of2[K](f)) return MonadMapWithIndex[M, N](r, F.Ignore1of2[K](f))
} }

View File

@ -333,3 +333,11 @@ func FromArray[
V any](m Mg.Magma[V]) func(fa []T.Tuple2[K, V]) map[K]V { V any](m Mg.Magma[V]) func(fa []T.Tuple2[K, V]) map[K]V {
return G.FromArray[[]T.Tuple2[K, V], map[K]V](m) return G.FromArray[[]T.Tuple2[K, V], map[K]V](m)
} }
func MonadAp[A any, K comparable, B any](m Mo.Monoid[map[K]B], fab map[K]func(A) B, fa map[K]A) map[K]B {
return G.MonadAp[map[K]B, map[K]func(A) B, map[K]A](m, fab, fa)
}
func Ap[A any, K comparable, B any](m Mo.Monoid[map[K]B]) func(fa map[K]A) func(map[K]func(A) B) map[K]B {
return G.Ap[map[K]B, map[K]func(A) B, map[K]A](m)
}

66
writer/bind.go Normal file
View File

@ -0,0 +1,66 @@
// 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 writer
import (
M "github.com/IBM/fp-go/monoid"
S "github.com/IBM/fp-go/semigroup"
G "github.com/IBM/fp-go/writer/generic"
)
// 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] {
return G.Do[Writer[W, S], W, S](m)
}
// Bind attaches the result of a computation to a context [S1] to produce a context [S2]
func Bind[S1, S2, T, W any](s S.Semigroup[W]) func(
setter func(T) func(S1) S2,
f func(S1) Writer[W, T],
) func(Writer[W, S1]) Writer[W, S2] {
return G.Bind[Writer[W, S1], Writer[W, S2], Writer[W, T], W, S1, S2, T](s)
}
// Let attaches the result of a computation to a context [S1] to produce a context [S2]
func Let[W, S1, S2, T any](
setter func(T) func(S1) S2,
f func(S1) T,
) func(Writer[W, S1]) Writer[W, S2] {
return G.Let[Writer[W, S1], Writer[W, S2], W, S1, S2, T](setter, f)
}
// LetTo attaches the a value to a context [S1] to produce a context [S2]
func LetTo[W, S1, S2, T any](
setter func(T) func(S1) S2,
b T,
) func(Writer[W, S1]) Writer[W, S2] {
return G.LetTo[Writer[W, S1], Writer[W, S2], W, S1, S2, T](setter, b)
}
// BindTo initializes a new state [S1] from a value [T]
func BindTo[W, S1, T any](
setter func(T) S1,
) func(Writer[W, T]) Writer[W, S1] {
return G.BindTo[Writer[W, S1], Writer[W, T], W, 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[S1, S2, T, W any](s S.Semigroup[W]) func(
setter func(T) func(S1) S2,
fa Writer[W, T],
) func(Writer[W, S1]) Writer[W, S2] {
return G.ApS[Writer[W, S1], Writer[W, S2], Writer[W, T], W, S1, S2, T](s)
}

63
writer/bind_test.go Normal file
View File

@ -0,0 +1,63 @@
// 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 writer
import (
"testing"
A "github.com/IBM/fp-go/array"
F "github.com/IBM/fp-go/function"
"github.com/IBM/fp-go/internal/utils"
M "github.com/IBM/fp-go/monoid"
"github.com/stretchr/testify/assert"
)
var (
monoid = A.Monoid[string]()
sg = M.ToSemigroup(monoid)
)
func getLastName(s utils.Initial) Writer[[]string, string] {
return Of[string](monoid)("Doe")
}
func getGivenName(s utils.WithLastName) Writer[[]string, string] {
return Of[string](monoid)("John")
}
func TestBind(t *testing.T) {
res := F.Pipe3(
Do[utils.Initial](monoid)(utils.Empty),
Bind[utils.Initial, utils.WithLastName, string](sg)(utils.SetLastName, getLastName),
Bind[utils.WithLastName, utils.WithGivenName, string](sg)(utils.SetGivenName, getGivenName),
Map[[]string](utils.GetFullName),
)
assert.Equal(t, res(), Of[string](monoid)("John Doe")())
}
func TestApS(t *testing.T) {
res := F.Pipe3(
Do[utils.Initial](monoid)(utils.Empty),
ApS[utils.Initial, utils.WithLastName, string](sg)(utils.SetLastName, Of[string](monoid)("Doe")),
ApS[utils.WithLastName, utils.WithGivenName, string](sg)(utils.SetGivenName, Of[string](monoid)("John")),
Map[[]string](utils.GetFullName),
)
assert.Equal(t, res(), Of[string](monoid)("John Doe")())
}

View File

@ -25,7 +25,7 @@ func Eq[W, A any](w EQ.Eq[W], a EQ.Eq[A]) EQ.Eq[Writer[W, A]] {
return G.Eq[Writer[W, A]](w, a) return G.Eq[Writer[W, A]](w, a)
} }
// FromStrictEquals constructs an `Eq` from the canonical comparison function // FromStrictEquals constructs an [EQ.Eq] from the canonical comparison function
func FromStrictEquals[W, A comparable]() EQ.Eq[Writer[W, A]] { func FromStrictEquals[W, A comparable]() EQ.Eq[Writer[W, A]] {
return G.FromStrictEquals[Writer[W, A]]() return G.FromStrictEquals[Writer[W, A]]()
} }

102
writer/generic/bind.go Normal file
View File

@ -0,0 +1,102 @@
// 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 (
"github.com/IBM/fp-go/internal/apply"
C "github.com/IBM/fp-go/internal/chain"
F "github.com/IBM/fp-go/internal/functor"
M "github.com/IBM/fp-go/monoid"
S "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
func Do[GS ~func() T.Tuple2[S, W], W, S any](m M.Monoid[W]) func(S) GS {
return Of[GS, W, S](m)
}
// Bind attaches the result of a computation to a context [S1] to produce a context [S2]
func Bind[GS1 ~func() T.Tuple2[S1, W], GS2 ~func() T.Tuple2[S2, W], GT ~func() T.Tuple2[A, W], W, S1, S2, A any](s S.Semigroup[W]) func(
setter func(A) func(S1) S2,
f func(S1) GT,
) func(GS1) GS2 {
ch := Chain[GS2, GS1, func(S1) GS2, W, S1, S2](s)
return func(
setter func(A) func(S1) S2,
f func(S1) GT,
) func(GS1) GS2 {
return C.Bind(
ch,
Map[GS2, GT, func(A) S2, W, A, S2],
setter,
f,
)
}
}
// Let attaches the result of a computation to a context [S1] to produce a context [S2]
func Let[GS1 ~func() T.Tuple2[S1, W], GS2 ~func() T.Tuple2[S2, W], W, S1, S2, A any](
key func(A) func(S1) S2,
f func(S1) A,
) func(GS1) GS2 {
return F.Let(
Map[GS2, GS1, func(S1) S2, W, S1, S2],
key,
f,
)
}
// LetTo attaches the a value to a context [S1] to produce a context [S2]
func LetTo[GS1 ~func() T.Tuple2[S1, W], GS2 ~func() T.Tuple2[S2, W], W, S1, S2, B any](
key func(B) func(S1) S2,
b B,
) func(GS1) GS2 {
return F.LetTo(
Map[GS2, GS1, func(S1) S2, W, S1, S2],
key,
b,
)
}
// BindTo initializes a new state [S1] from a value [T]
func BindTo[GS1 ~func() T.Tuple2[S1, W], GT ~func() T.Tuple2[A, W], W, S1, A any](
setter func(A) S1,
) func(GT) GS1 {
return C.BindTo(
Map[GS1, GT, func(A) S1, W, A, 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() T.Tuple2[S1, W], GS2 ~func() T.Tuple2[S2, W], GT ~func() T.Tuple2[A, W], W, S1, S2, A any](s S.Semigroup[W]) func(
setter func(A) func(S1) S2,
fa GT,
) func(GS1) GS2 {
ap := Ap[GS2, func() T.Tuple2[func(A) S2, W], GT, W, A, S2](s)
return func(
setter func(A) func(S1) S2,
fa GT,
) func(GS1) GS2 {
return apply.ApS(
ap,
Map[func() T.Tuple2[func(A) S2, W], GS1, func(S1) func(A) S2],
setter,
fa,
)
}
}

View File

@ -30,7 +30,7 @@ func Eq[GA ~func() T.Tuple2[A, W], W, A any](w EQ.Eq[W], a EQ.Eq[A]) EQ.Eq[GA] {
}) })
} }
// FromStrictEquals constructs an `Eq` from the canonical comparison function // FromStrictEquals constructs an [EQ.Eq] from the canonical comparison function
func FromStrictEquals[GA ~func() T.Tuple2[A, W], W, A comparable]() EQ.Eq[GA] { func FromStrictEquals[GA ~func() T.Tuple2[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]())
} }

View File

@ -30,6 +30,22 @@ func Of[GA ~func() T.Tuple2[A, W], W, A any](m M.Monoid[W]) func(A) GA {
) )
} }
// Listen modifies the result to include the changes to the accumulator
func Listen[GA ~func() T.Tuple2[A, W], GTA ~func() T.Tuple2[T.Tuple2[A, W], W], W, A any](fa GA) GTA {
return func() T.Tuple2[T.Tuple2[A, W], W] {
t := fa()
return T.MakeTuple2(T.MakeTuple2(t.F1, t.F2), t.F2)
}
}
// Pass applies the returned function to the accumulator
func Pass[GFA ~func() T.Tuple2[T.Tuple2[A, FCT], W], GA ~func() T.Tuple2[A, W], FCT ~func(W) W, W, A any](fa GFA) GA {
return func() T.Tuple2[A, W] {
t := fa()
return T.MakeTuple2(t.F1.F1, t.F1.F2(t.F2))
}
}
func MonadMap[GB ~func() T.Tuple2[B, W], GA ~func() T.Tuple2[A, W], FCT ~func(A) B, W, A, B any](fa GA, f FCT) GB { func MonadMap[GB ~func() T.Tuple2[B, W], GA ~func() T.Tuple2[A, W], FCT ~func(A) B, W, A, B any](fa GA, f FCT) GB {
return IO.MonadMap[GA, GB](fa, T.Map2(f, F.Identity[W])) return IO.MonadMap[GA, GB](fa, T.Map2(f, F.Identity[W]))
} }

View File

@ -16,23 +16,35 @@
package writer package writer
import ( import (
EM "github.com/IBM/fp-go/endomorphism"
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" S "github.com/IBM/fp-go/semigroup"
T "github.com/IBM/fp-go/tuple" T "github.com/IBM/fp-go/tuple"
G "github.com/IBM/fp-go/writer/generic" G "github.com/IBM/fp-go/writer/generic"
) )
type Writer[W, A any] func() T.Tuple2[A, W] type Writer[W, A any] IO.IO[T.Tuple2[A, W]]
func Of[A, W any](m M.Monoid[W]) func(A) Writer[W, A] { func Of[A, W any](m M.Monoid[W]) func(A) Writer[W, A] {
return G.Of[Writer[W, A]](m) return G.Of[Writer[W, A]](m)
} }
// 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]] {
return G.Listen[Writer[W, A], Writer[W, T.Tuple2[A, W]], W, A](fa)
}
// 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] {
return G.Pass[Writer[W, T.Tuple2[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] {
return G.MonadMap[Writer[W, B], Writer[W, A]](fa, f) return G.MonadMap[Writer[W, B], Writer[W, A]](fa, f)
} }
func Map[FCT ~func(A) B, W, A, B any](f FCT) func(Writer[W, A]) Writer[W, B] { 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)
} }
@ -64,10 +76,12 @@ func Flatten[W, A any](s S.Semigroup[W]) func(Writer[W, Writer[W, A]]) Writer[W,
return G.Flatten[Writer[W, Writer[W, A]], Writer[W, A]](s) return G.Flatten[Writer[W, Writer[W, A]], Writer[W, A]](s)
} }
// Execute extracts the accumulator
func Execute[W, A any](fa Writer[W, A]) W { func Execute[W, A any](fa Writer[W, A]) W {
return G.Execute(fa) return G.Execute(fa)
} }
// Evaluate extracts the value
func Evaluate[W, A any](fa Writer[W, A]) A { func Evaluate[W, A any](fa Writer[W, A]) A {
return G.Evaluate(fa) return G.Evaluate(fa)
} }

View File

@ -46,5 +46,4 @@ func ExampleWriter_logging() {
fmt.Println(res()) fmt.Println(res())
// Output: Tuple2[int, []string](40, [Doubled 10 -> 20 Doubled 20 -> 40]) // Output: Tuple2[int, []string](40, [Doubled 10 -> 20 Doubled 20 -> 40])
} }