1
0
mirror of https://github.com/IBM/fp-go.git synced 2025-08-24 19:29:11 +02:00

Compare commits

...

22 Commits

Author SHA1 Message Date
Dr. Carsten Leue
9e04974d0e fix: add ReaderIOEither sample
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-02-04 22:25:51 +01:00
Dr. Carsten Leue
2f99ca471a fix: add example for dependency injection using a Reader
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-02-04 21:52:50 +01:00
Dr. Carsten Leue
3e09a19d68 fix: Writer monad
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-02-01 22:11:29 +01:00
Dr. Carsten Leue
839ef47054 fix: refactor writer
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-02-01 18:26:09 +01:00
Dr. Carsten Leue
144b27233b fix: some internal refactorings
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-02-01 17:26:51 +01:00
Dr. Carsten Leue
668eb85aea fix: some smaller optimizations
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-02-01 09:08:31 +01:00
Dr. Carsten Leue
b077fed094 fix: optimize PipeXXX calls
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-01-31 21:50:33 +01:00
Carsten Leue
7d3759619c 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>
2024-01-31 21:34:46 +01:00
Dr. Carsten Leue
c73467caf5 fix: add WithTime and WithDuration
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-01-26 10:09:39 +01:00
Dr. Carsten Leue
ca606767f3 fix: icorrect order of generics for IO.Flap
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-01-26 09:15:56 +01:00
Dr. Carsten Leue
fb82af9a69 fix: signature of IO.Flap
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-01-26 09:11:27 +01:00
Dr. Carsten Leue
57ad8c6466 fix: expose MkDirAll
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-01-20 16:52:58 +01:00
Dr. Carsten Leue
5a9f405362 fix: better handling of HTTP errors
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-01-20 09:57:23 +01:00
Dr. Carsten Leue
89c34254a9 fix: add Join
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-01-17 11:57:26 +01:00
Dr. Carsten Leue
a14feff1d6 fix: add missing MapLeft
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-01-15 12:04:55 +01:00
Dr. Carsten Leue
f3642bad60 fix: remove 1.22
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-01-15 10:52:16 +01:00
Dr. Carsten Leue
997627b318 fix: node version
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-01-15 10:51:17 +01:00
Dr. Carsten Leue
1e1411c003 fix: add LogJson
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-01-15 10:46:50 +01:00
Dr. Carsten Leue
9ed9f8a171 fix: implement foldMap for records
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-01-12 17:19:38 +01:00
Dr. Carsten Leue
e7428549e4 fix: add consistent Delay and After functions to IO
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-01-12 13:20:50 +01:00
Dr. Carsten Leue
aef0048119 fix: add Wrap and Unwrap to endomorphism
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-01-11 17:18:35 +01:00
Dr. Carsten Leue
709d74b135 fix: refactor builder
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-01-11 14:12:53 +01:00
118 changed files with 5239 additions and 949 deletions

View File

@@ -17,8 +17,8 @@ on:
env:
# Currently no way to detect automatically
DEFAULT_BRANCH: main
GO_VERSION: 1.20.6 # renovate: datasource=golang-version depName=golang
NODE_VERSION: 18
GO_VERSION: 1.21.6 # renovate: datasource=golang-version depName=golang
NODE_VERSION: 20
DRY_RUN: true
jobs:
@@ -26,7 +26,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
go-version: [ '1.20.x', '1.21.x' ]
go-version: [ '1.20.x', '1.21.x']
steps:
# full checkout for semantic-release
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1

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"))
}

View File

@@ -344,7 +344,7 @@ func MonadFlap[FAB ~func(A) B, GFAB ~[]FAB, GB ~[]B, A, B any](fab GFAB, a A) GB
}
func Flap[FAB ~func(A) B, GFAB ~[]FAB, GB ~[]B, A, B any](a A) func(GFAB) GB {
return F.Bind2nd(MonadFlap[FAB, GFAB, GB, A, B], a)
return FC.Flap(Map[GFAB, GB], a)
}
func Prepend[ENDO ~func(AS) AS, AS []A, A any](head A) ENDO {

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,
)
}

View File

@@ -24,10 +24,10 @@ import (
func concat[T any](left, right []T) []T {
// some performance checks
ll := len(left)
lr := len(right)
if ll == 0 {
return right
}
lr := len(right)
if lr == 0 {
return left
}

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

@@ -265,10 +265,15 @@ func generatePipe(f *os.File, i int) {
fmt.Fprintf(f, ", f%d F%d", j, j)
}
fmt.Fprintf(f, ") T%d {\n", i)
for j := 1; j <= i; j++ {
fmt.Fprintf(f, " t%d := f%d(t%d)\n", j, j, j-1)
fmt.Fprintf(f, " return ")
for j := i; j >= 1; j-- {
fmt.Fprintf(f, "f%d(", j)
}
fmt.Fprintf(f, " return t%d\n", i)
fmt.Fprintf(f, "t0")
for j := 1; j <= i; j++ {
fmt.Fprintf(f, ")")
}
fmt.Fprintf(f, "\n")
fmt.Fprintln(f, "}")
}

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,
)
}

View File

@@ -0,0 +1,72 @@
// 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 builder
import (
"bytes"
"context"
"net/http"
"strconv"
RIOE "github.com/IBM/fp-go/context/readerioeither"
RIOEH "github.com/IBM/fp-go/context/readerioeither/http"
E "github.com/IBM/fp-go/either"
F "github.com/IBM/fp-go/function"
R "github.com/IBM/fp-go/http/builder"
H "github.com/IBM/fp-go/http/headers"
LZ "github.com/IBM/fp-go/lazy"
O "github.com/IBM/fp-go/option"
)
func Requester(builder *R.Builder) RIOEH.Requester {
withBody := F.Curry3(func(data []byte, url string, method string) RIOE.ReaderIOEither[*http.Request] {
return RIOE.TryCatch(func(ctx context.Context) func() (*http.Request, error) {
return func() (*http.Request, error) {
req, err := http.NewRequestWithContext(ctx, method, url, bytes.NewReader(data))
if err == nil {
req.Header.Set(H.ContentLength, strconv.Itoa(len(data)))
H.Monoid.Concat(req.Header, builder.GetHeaders())
}
return req, err
}
})
})
withoutBody := F.Curry2(func(url string, method string) RIOE.ReaderIOEither[*http.Request] {
return RIOE.TryCatch(func(ctx context.Context) func() (*http.Request, error) {
return func() (*http.Request, error) {
req, err := http.NewRequestWithContext(ctx, method, url, nil)
if err == nil {
H.Monoid.Concat(req.Header, builder.GetHeaders())
}
return req, err
}
})
})
return F.Pipe5(
builder.GetBody(),
O.Fold(LZ.Of(E.Of[error](withoutBody)), E.Map[error](withBody)),
E.Ap[func(string) RIOE.ReaderIOEither[*http.Request]](builder.GetTargetUrl()),
E.Flap[error, RIOE.ReaderIOEither[*http.Request]](builder.GetMethod()),
E.GetOrElse(RIOE.Left[*http.Request]),
RIOE.Map(func(req *http.Request) *http.Request {
req.Header = H.Monoid.Concat(req.Header, builder.GetHeaders())
return req
}),
)
}

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 builder
import (
"context"
"net/http"
"net/url"
"testing"
RIOE "github.com/IBM/fp-go/context/readerioeither"
E "github.com/IBM/fp-go/either"
F "github.com/IBM/fp-go/function"
R "github.com/IBM/fp-go/http/builder"
IO "github.com/IBM/fp-go/io"
"github.com/stretchr/testify/assert"
)
func TestBuilderWithQuery(t *testing.T) {
// add some query
withLimit := R.WithQueryArg("limit")("10")
withUrl := R.WithUrl("http://www.example.org?a=b")
b := F.Pipe2(
R.Default,
withLimit,
withUrl,
)
req := F.Pipe3(
b,
Requester,
RIOE.Map(func(r *http.Request) *url.URL {
return r.URL
}),
RIOE.ChainFirstIOK(func(u *url.URL) IO.IO[any] {
return IO.FromImpure(func() {
q := u.Query()
assert.Equal(t, "10", q.Get("limit"))
assert.Equal(t, "b", q.Get("a"))
})
}),
)
assert.True(t, E.IsRight(req(context.Background())()))
}

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

@@ -64,19 +64,20 @@ func MonadMapTo[E, A, B any](fa Either[E, A], b B) Either[E, B] {
}
func MapTo[E, A, B any](b B) func(Either[E, A]) Either[E, B] {
return F.Bind2nd(MonadMapTo[E, A, B], b)
return Map[E](F.Constant1[A](b))
}
func MonadMapLeft[E, A, B any](fa Either[E, A], f func(E) B) Either[B, A] {
return MonadFold(fa, F.Flow2(f, Left[A, B]), Right[B, A])
func MonadMapLeft[E1, A, E2 any](fa Either[E1, A], f func(E1) E2) Either[E2, A] {
return MonadFold(fa, F.Flow2(f, Left[A, E2]), Right[E2, A])
}
func Map[E, A, B any](f func(a A) B) func(fa Either[E, A]) Either[E, B] {
return Chain(F.Flow2(f, Right[E, B]))
}
func MapLeft[E, A, B any](f func(E) B) func(fa Either[E, A]) Either[B, A] {
return F.Bind2nd(MonadMapLeft[E, A, B], f)
// MapLeft applies a mapping function to the error channel
func MapLeft[A, E1, E2 any](f func(E1) E2) func(fa Either[E1, A]) Either[E2, A] {
return Fold(F.Flow2(f, Left[A, E2]), Right[E2, A])
}
func MonadChain[E, A, B any](fa Either[E, A], f func(a A) Either[E, B]) Either[E, B] {
@@ -105,11 +106,11 @@ func ChainOptionK[A, B, E any](onNone func() E) func(func(A) O.Option[B]) func(E
}
func ChainTo[A, E, B any](mb Either[E, B]) func(Either[E, A]) Either[E, B] {
return F.Bind2nd(MonadChainTo[A, E, B], mb)
return F.Constant1[Either[E, A]](mb)
}
func Chain[E, A, B any](f func(a A) Either[E, B]) func(Either[E, A]) Either[E, B] {
return F.Bind2nd(MonadChain[E, A, B], f)
return Fold(Left[B, E], f)
}
func ChainFirst[E, A, B any](f func(a A) Either[E, B]) func(Either[E, A]) Either[E, A] {
@@ -250,7 +251,7 @@ func MonadFlap[E, B, A any](fab Either[E, func(A) B], a A) Either[E, B] {
}
func Flap[E, B, A any](a A) func(Either[E, func(A) B]) Either[E, B] {
return F.Bind2nd(MonadFlap[E, B, A], a)
return FC.Flap(Map[E, func(A) B, B], a)
}
func MonadAlt[E, A any](fa Either[E, A], that L.Lazy[Either[E, A]]) Either[E, A] {

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))
}
// 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]] {
return Eq(EQ.FromStrictEquals[E](), EQ.FromStrictEquals[A]())
}

View File

@@ -28,6 +28,16 @@ func Of[ENDO ~func(A) A, F ~func(A) A, A any](f F) ENDO {
}
}
// Wrap converts any function to an [Endomorphism]
func Wrap[ENDO ~func(A) A, F ~func(A) A, A any](f F) ENDO {
return Of[ENDO](f)
}
// Unwrap converts any [Endomorphism] to a normal function
func Unwrap[F ~func(A) A, ENDO ~func(A) A, A any](f ENDO) F {
return Of[F](f)
}
func Identity[ENDO ~func(A) A, A any]() ENDO {
return Of[ENDO](F.Identity[A])
}

View File

@@ -29,6 +29,16 @@ func Of[F ~func(A) A, A any](f F) Endomorphism[A] {
return G.Of[Endomorphism[A]](f)
}
// Wrap converts any function to an [Endomorphism]
func Wrap[F ~func(A) A, A any](f F) Endomorphism[A] {
return G.Wrap[Endomorphism[A]](f)
}
// Unwrap converts any [Endomorphism] to a function
func Unwrap[F ~func(A) A, A any](f Endomorphism[A]) F {
return G.Unwrap[F](f)
}
// Identity returns the identity [Endomorphism]
func Identity[A any]() Endomorphism[A] {
return G.Identity[Endomorphism[A]]()

View File

@@ -35,12 +35,12 @@ func strictEq[A comparable](a, b A) bool {
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] {
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] {
return eq[T]{c: c}
}

View File

@@ -1,6 +1,6 @@
// Code generated by go generate; DO NOT EDIT.
// This file was generated by robots at
// 2023-12-18 09:38:59.1616876 +0100 CET m=+0.008641801
// 2024-01-31 21:45:01.6437619 +0100 CET m=+0.032758901
package function

View File

@@ -1,6 +1,6 @@
// Code generated by go generate; DO NOT EDIT.
// This file was generated by robots at
// 2023-12-18 09:38:51.4946446 +0100 CET m=+0.008838401
// 2024-01-31 21:44:55.7538323 +0100 CET m=+0.013067701
package function
@@ -34,8 +34,7 @@ func Unsliced0[F ~func([]T) R, T, R any](f F) func() R {
// Pipe1 takes an initial value t0 and successively applies 1 functions where the input of a function is the return value of the previous function
// The final return value is the result of the last function application
func Pipe1[F1 ~func(T0) T1, T0, T1 any](t0 T0, f1 F1) T1 {
t1 := f1(t0)
return t1
return f1(t0)
}
// Flow1 creates a function that takes an initial value t0 and successively applies 1 functions where the input of a function is the return value of the previous function
@@ -93,9 +92,7 @@ func Unsliced1[F ~func([]T) R, T, R any](f F) func(T) R {
// Pipe2 takes an initial value t0 and successively applies 2 functions where the input of a function is the return value of the previous function
// The final return value is the result of the last function application
func Pipe2[F1 ~func(T0) T1, F2 ~func(T1) T2, T0, T1, T2 any](t0 T0, f1 F1, f2 F2) T2 {
t1 := f1(t0)
t2 := f2(t1)
return t2
return f2(f1(t0))
}
// Flow2 creates a function that takes an initial value t0 and successively applies 2 functions where the input of a function is the return value of the previous function
@@ -155,10 +152,7 @@ func Unsliced2[F ~func([]T) R, T, R any](f F) func(T, T) R {
// Pipe3 takes an initial value t0 and successively applies 3 functions where the input of a function is the return value of the previous function
// The final return value is the result of the last function application
func Pipe3[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, T0, T1, T2, T3 any](t0 T0, f1 F1, f2 F2, f3 F3) T3 {
t1 := f1(t0)
t2 := f2(t1)
t3 := f3(t2)
return t3
return f3(f2(f1(t0)))
}
// Flow3 creates a function that takes an initial value t0 and successively applies 3 functions where the input of a function is the return value of the previous function
@@ -220,11 +214,7 @@ func Unsliced3[F ~func([]T) R, T, R any](f F) func(T, T, T) R {
// Pipe4 takes an initial value t0 and successively applies 4 functions where the input of a function is the return value of the previous function
// The final return value is the result of the last function application
func Pipe4[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, T0, T1, T2, T3, T4 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4) T4 {
t1 := f1(t0)
t2 := f2(t1)
t3 := f3(t2)
t4 := f4(t3)
return t4
return f4(f3(f2(f1(t0))))
}
// Flow4 creates a function that takes an initial value t0 and successively applies 4 functions where the input of a function is the return value of the previous function
@@ -288,12 +278,7 @@ func Unsliced4[F ~func([]T) R, T, R any](f F) func(T, T, T, T) R {
// Pipe5 takes an initial value t0 and successively applies 5 functions where the input of a function is the return value of the previous function
// The final return value is the result of the last function application
func Pipe5[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, T0, T1, T2, T3, T4, T5 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5) T5 {
t1 := f1(t0)
t2 := f2(t1)
t3 := f3(t2)
t4 := f4(t3)
t5 := f5(t4)
return t5
return f5(f4(f3(f2(f1(t0)))))
}
// Flow5 creates a function that takes an initial value t0 and successively applies 5 functions where the input of a function is the return value of the previous function
@@ -359,13 +344,7 @@ func Unsliced5[F ~func([]T) R, T, R any](f F) func(T, T, T, T, T) R {
// Pipe6 takes an initial value t0 and successively applies 6 functions where the input of a function is the return value of the previous function
// The final return value is the result of the last function application
func Pipe6[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, T0, T1, T2, T3, T4, T5, T6 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6) T6 {
t1 := f1(t0)
t2 := f2(t1)
t3 := f3(t2)
t4 := f4(t3)
t5 := f5(t4)
t6 := f6(t5)
return t6
return f6(f5(f4(f3(f2(f1(t0))))))
}
// Flow6 creates a function that takes an initial value t0 and successively applies 6 functions where the input of a function is the return value of the previous function
@@ -433,14 +412,7 @@ func Unsliced6[F ~func([]T) R, T, R any](f F) func(T, T, T, T, T, T) R {
// Pipe7 takes an initial value t0 and successively applies 7 functions where the input of a function is the return value of the previous function
// The final return value is the result of the last function application
func Pipe7[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, T0, T1, T2, T3, T4, T5, T6, T7 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7) T7 {
t1 := f1(t0)
t2 := f2(t1)
t3 := f3(t2)
t4 := f4(t3)
t5 := f5(t4)
t6 := f6(t5)
t7 := f7(t6)
return t7
return f7(f6(f5(f4(f3(f2(f1(t0)))))))
}
// Flow7 creates a function that takes an initial value t0 and successively applies 7 functions where the input of a function is the return value of the previous function
@@ -510,15 +482,7 @@ func Unsliced7[F ~func([]T) R, T, R any](f F) func(T, T, T, T, T, T, T) R {
// Pipe8 takes an initial value t0 and successively applies 8 functions where the input of a function is the return value of the previous function
// The final return value is the result of the last function application
func Pipe8[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, T0, T1, T2, T3, T4, T5, T6, T7, T8 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8) T8 {
t1 := f1(t0)
t2 := f2(t1)
t3 := f3(t2)
t4 := f4(t3)
t5 := f5(t4)
t6 := f6(t5)
t7 := f7(t6)
t8 := f8(t7)
return t8
return f8(f7(f6(f5(f4(f3(f2(f1(t0))))))))
}
// Flow8 creates a function that takes an initial value t0 and successively applies 8 functions where the input of a function is the return value of the previous function
@@ -590,16 +554,7 @@ func Unsliced8[F ~func([]T) R, T, R any](f F) func(T, T, T, T, T, T, T, T) R {
// Pipe9 takes an initial value t0 and successively applies 9 functions where the input of a function is the return value of the previous function
// The final return value is the result of the last function application
func Pipe9[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9) T9 {
t1 := f1(t0)
t2 := f2(t1)
t3 := f3(t2)
t4 := f4(t3)
t5 := f5(t4)
t6 := f6(t5)
t7 := f7(t6)
t8 := f8(t7)
t9 := f9(t8)
return t9
return f9(f8(f7(f6(f5(f4(f3(f2(f1(t0)))))))))
}
// Flow9 creates a function that takes an initial value t0 and successively applies 9 functions where the input of a function is the return value of the previous function
@@ -673,17 +628,7 @@ func Unsliced9[F ~func([]T) R, T, R any](f F) func(T, T, T, T, T, T, T, T, T) R
// Pipe10 takes an initial value t0 and successively applies 10 functions where the input of a function is the return value of the previous function
// The final return value is the result of the last function application
func Pipe10[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10) T10 {
t1 := f1(t0)
t2 := f2(t1)
t3 := f3(t2)
t4 := f4(t3)
t5 := f5(t4)
t6 := f6(t5)
t7 := f7(t6)
t8 := f8(t7)
t9 := f9(t8)
t10 := f10(t9)
return t10
return f10(f9(f8(f7(f6(f5(f4(f3(f2(f1(t0))))))))))
}
// Flow10 creates a function that takes an initial value t0 and successively applies 10 functions where the input of a function is the return value of the previous function
@@ -759,18 +704,7 @@ func Unsliced10[F ~func([]T) R, T, R any](f F) func(T, T, T, T, T, T, T, T, T, T
// Pipe11 takes an initial value t0 and successively applies 11 functions where the input of a function is the return value of the previous function
// The final return value is the result of the last function application
func Pipe11[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, F11 ~func(T10) T11, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11) T11 {
t1 := f1(t0)
t2 := f2(t1)
t3 := f3(t2)
t4 := f4(t3)
t5 := f5(t4)
t6 := f6(t5)
t7 := f7(t6)
t8 := f8(t7)
t9 := f9(t8)
t10 := f10(t9)
t11 := f11(t10)
return t11
return f11(f10(f9(f8(f7(f6(f5(f4(f3(f2(f1(t0)))))))))))
}
// Flow11 creates a function that takes an initial value t0 and successively applies 11 functions where the input of a function is the return value of the previous function
@@ -848,19 +782,7 @@ func Unsliced11[F ~func([]T) R, T, R any](f F) func(T, T, T, T, T, T, T, T, T, T
// Pipe12 takes an initial value t0 and successively applies 12 functions where the input of a function is the return value of the previous function
// The final return value is the result of the last function application
func Pipe12[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, F11 ~func(T10) T11, F12 ~func(T11) T12, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12) T12 {
t1 := f1(t0)
t2 := f2(t1)
t3 := f3(t2)
t4 := f4(t3)
t5 := f5(t4)
t6 := f6(t5)
t7 := f7(t6)
t8 := f8(t7)
t9 := f9(t8)
t10 := f10(t9)
t11 := f11(t10)
t12 := f12(t11)
return t12
return f12(f11(f10(f9(f8(f7(f6(f5(f4(f3(f2(f1(t0))))))))))))
}
// Flow12 creates a function that takes an initial value t0 and successively applies 12 functions where the input of a function is the return value of the previous function
@@ -940,20 +862,7 @@ func Unsliced12[F ~func([]T) R, T, R any](f F) func(T, T, T, T, T, T, T, T, T, T
// Pipe13 takes an initial value t0 and successively applies 13 functions where the input of a function is the return value of the previous function
// The final return value is the result of the last function application
func Pipe13[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, F11 ~func(T10) T11, F12 ~func(T11) T12, F13 ~func(T12) T13, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12, f13 F13) T13 {
t1 := f1(t0)
t2 := f2(t1)
t3 := f3(t2)
t4 := f4(t3)
t5 := f5(t4)
t6 := f6(t5)
t7 := f7(t6)
t8 := f8(t7)
t9 := f9(t8)
t10 := f10(t9)
t11 := f11(t10)
t12 := f12(t11)
t13 := f13(t12)
return t13
return f13(f12(f11(f10(f9(f8(f7(f6(f5(f4(f3(f2(f1(t0)))))))))))))
}
// Flow13 creates a function that takes an initial value t0 and successively applies 13 functions where the input of a function is the return value of the previous function
@@ -1035,21 +944,7 @@ func Unsliced13[F ~func([]T) R, T, R any](f F) func(T, T, T, T, T, T, T, T, T, T
// Pipe14 takes an initial value t0 and successively applies 14 functions where the input of a function is the return value of the previous function
// The final return value is the result of the last function application
func Pipe14[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, F11 ~func(T10) T11, F12 ~func(T11) T12, F13 ~func(T12) T13, F14 ~func(T13) T14, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12, f13 F13, f14 F14) T14 {
t1 := f1(t0)
t2 := f2(t1)
t3 := f3(t2)
t4 := f4(t3)
t5 := f5(t4)
t6 := f6(t5)
t7 := f7(t6)
t8 := f8(t7)
t9 := f9(t8)
t10 := f10(t9)
t11 := f11(t10)
t12 := f12(t11)
t13 := f13(t12)
t14 := f14(t13)
return t14
return f14(f13(f12(f11(f10(f9(f8(f7(f6(f5(f4(f3(f2(f1(t0))))))))))))))
}
// Flow14 creates a function that takes an initial value t0 and successively applies 14 functions where the input of a function is the return value of the previous function
@@ -1133,22 +1028,7 @@ func Unsliced14[F ~func([]T) R, T, R any](f F) func(T, T, T, T, T, T, T, T, T, T
// Pipe15 takes an initial value t0 and successively applies 15 functions where the input of a function is the return value of the previous function
// The final return value is the result of the last function application
func Pipe15[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, F11 ~func(T10) T11, F12 ~func(T11) T12, F13 ~func(T12) T13, F14 ~func(T13) T14, F15 ~func(T14) T15, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12, f13 F13, f14 F14, f15 F15) T15 {
t1 := f1(t0)
t2 := f2(t1)
t3 := f3(t2)
t4 := f4(t3)
t5 := f5(t4)
t6 := f6(t5)
t7 := f7(t6)
t8 := f8(t7)
t9 := f9(t8)
t10 := f10(t9)
t11 := f11(t10)
t12 := f12(t11)
t13 := f13(t12)
t14 := f14(t13)
t15 := f15(t14)
return t15
return f15(f14(f13(f12(f11(f10(f9(f8(f7(f6(f5(f4(f3(f2(f1(t0)))))))))))))))
}
// Flow15 creates a function that takes an initial value t0 and successively applies 15 functions where the input of a function is the return value of the previous function
@@ -1234,23 +1114,7 @@ func Unsliced15[F ~func([]T) R, T, R any](f F) func(T, T, T, T, T, T, T, T, T, T
// Pipe16 takes an initial value t0 and successively applies 16 functions where the input of a function is the return value of the previous function
// The final return value is the result of the last function application
func Pipe16[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, F11 ~func(T10) T11, F12 ~func(T11) T12, F13 ~func(T12) T13, F14 ~func(T13) T14, F15 ~func(T14) T15, F16 ~func(T15) T16, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12, f13 F13, f14 F14, f15 F15, f16 F16) T16 {
t1 := f1(t0)
t2 := f2(t1)
t3 := f3(t2)
t4 := f4(t3)
t5 := f5(t4)
t6 := f6(t5)
t7 := f7(t6)
t8 := f8(t7)
t9 := f9(t8)
t10 := f10(t9)
t11 := f11(t10)
t12 := f12(t11)
t13 := f13(t12)
t14 := f14(t13)
t15 := f15(t14)
t16 := f16(t15)
return t16
return f16(f15(f14(f13(f12(f11(f10(f9(f8(f7(f6(f5(f4(f3(f2(f1(t0))))))))))))))))
}
// Flow16 creates a function that takes an initial value t0 and successively applies 16 functions where the input of a function is the return value of the previous function
@@ -1338,24 +1202,7 @@ func Unsliced16[F ~func([]T) R, T, R any](f F) func(T, T, T, T, T, T, T, T, T, T
// Pipe17 takes an initial value t0 and successively applies 17 functions where the input of a function is the return value of the previous function
// The final return value is the result of the last function application
func Pipe17[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, F11 ~func(T10) T11, F12 ~func(T11) T12, F13 ~func(T12) T13, F14 ~func(T13) T14, F15 ~func(T14) T15, F16 ~func(T15) T16, F17 ~func(T16) T17, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12, f13 F13, f14 F14, f15 F15, f16 F16, f17 F17) T17 {
t1 := f1(t0)
t2 := f2(t1)
t3 := f3(t2)
t4 := f4(t3)
t5 := f5(t4)
t6 := f6(t5)
t7 := f7(t6)
t8 := f8(t7)
t9 := f9(t8)
t10 := f10(t9)
t11 := f11(t10)
t12 := f12(t11)
t13 := f13(t12)
t14 := f14(t13)
t15 := f15(t14)
t16 := f16(t15)
t17 := f17(t16)
return t17
return f17(f16(f15(f14(f13(f12(f11(f10(f9(f8(f7(f6(f5(f4(f3(f2(f1(t0)))))))))))))))))
}
// Flow17 creates a function that takes an initial value t0 and successively applies 17 functions where the input of a function is the return value of the previous function
@@ -1445,25 +1292,7 @@ func Unsliced17[F ~func([]T) R, T, R any](f F) func(T, T, T, T, T, T, T, T, T, T
// Pipe18 takes an initial value t0 and successively applies 18 functions where the input of a function is the return value of the previous function
// The final return value is the result of the last function application
func Pipe18[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, F11 ~func(T10) T11, F12 ~func(T11) T12, F13 ~func(T12) T13, F14 ~func(T13) T14, F15 ~func(T14) T15, F16 ~func(T15) T16, F17 ~func(T16) T17, F18 ~func(T17) T18, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12, f13 F13, f14 F14, f15 F15, f16 F16, f17 F17, f18 F18) T18 {
t1 := f1(t0)
t2 := f2(t1)
t3 := f3(t2)
t4 := f4(t3)
t5 := f5(t4)
t6 := f6(t5)
t7 := f7(t6)
t8 := f8(t7)
t9 := f9(t8)
t10 := f10(t9)
t11 := f11(t10)
t12 := f12(t11)
t13 := f13(t12)
t14 := f14(t13)
t15 := f15(t14)
t16 := f16(t15)
t17 := f17(t16)
t18 := f18(t17)
return t18
return f18(f17(f16(f15(f14(f13(f12(f11(f10(f9(f8(f7(f6(f5(f4(f3(f2(f1(t0))))))))))))))))))
}
// Flow18 creates a function that takes an initial value t0 and successively applies 18 functions where the input of a function is the return value of the previous function
@@ -1555,26 +1384,7 @@ func Unsliced18[F ~func([]T) R, T, R any](f F) func(T, T, T, T, T, T, T, T, T, T
// Pipe19 takes an initial value t0 and successively applies 19 functions where the input of a function is the return value of the previous function
// The final return value is the result of the last function application
func Pipe19[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, F11 ~func(T10) T11, F12 ~func(T11) T12, F13 ~func(T12) T13, F14 ~func(T13) T14, F15 ~func(T14) T15, F16 ~func(T15) T16, F17 ~func(T16) T17, F18 ~func(T17) T18, F19 ~func(T18) T19, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12, f13 F13, f14 F14, f15 F15, f16 F16, f17 F17, f18 F18, f19 F19) T19 {
t1 := f1(t0)
t2 := f2(t1)
t3 := f3(t2)
t4 := f4(t3)
t5 := f5(t4)
t6 := f6(t5)
t7 := f7(t6)
t8 := f8(t7)
t9 := f9(t8)
t10 := f10(t9)
t11 := f11(t10)
t12 := f12(t11)
t13 := f13(t12)
t14 := f14(t13)
t15 := f15(t14)
t16 := f16(t15)
t17 := f17(t16)
t18 := f18(t17)
t19 := f19(t18)
return t19
return f19(f18(f17(f16(f15(f14(f13(f12(f11(f10(f9(f8(f7(f6(f5(f4(f3(f2(f1(t0)))))))))))))))))))
}
// Flow19 creates a function that takes an initial value t0 and successively applies 19 functions where the input of a function is the return value of the previous function
@@ -1668,27 +1478,7 @@ func Unsliced19[F ~func([]T) R, T, R any](f F) func(T, T, T, T, T, T, T, T, T, T
// Pipe20 takes an initial value t0 and successively applies 20 functions where the input of a function is the return value of the previous function
// The final return value is the result of the last function application
func Pipe20[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, F11 ~func(T10) T11, F12 ~func(T11) T12, F13 ~func(T12) T13, F14 ~func(T13) T14, F15 ~func(T14) T15, F16 ~func(T15) T16, F17 ~func(T16) T17, F18 ~func(T17) T18, F19 ~func(T18) T19, F20 ~func(T19) T20, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12, f13 F13, f14 F14, f15 F15, f16 F16, f17 F17, f18 F18, f19 F19, f20 F20) T20 {
t1 := f1(t0)
t2 := f2(t1)
t3 := f3(t2)
t4 := f4(t3)
t5 := f5(t4)
t6 := f6(t5)
t7 := f7(t6)
t8 := f8(t7)
t9 := f9(t8)
t10 := f10(t9)
t11 := f11(t10)
t12 := f12(t11)
t13 := f13(t12)
t14 := f14(t13)
t15 := f15(t14)
t16 := f16(t15)
t17 := f17(t16)
t18 := f18(t17)
t19 := f19(t18)
t20 := f20(t19)
return t20
return f20(f19(f18(f17(f16(f15(f14(f13(f12(f11(f10(f9(f8(f7(f6(f5(f4(f3(f2(f1(t0))))))))))))))))))))
}
// Flow20 creates a function that takes an initial value t0 and successively applies 20 functions where the input of a function is the return value of the previous function

311
http/builder/builder.go Normal file
View File

@@ -0,0 +1,311 @@
// 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 builder
import (
"net/http"
"net/url"
E "github.com/IBM/fp-go/either"
ENDO "github.com/IBM/fp-go/endomorphism"
F "github.com/IBM/fp-go/function"
C "github.com/IBM/fp-go/http/content"
FM "github.com/IBM/fp-go/http/form"
H "github.com/IBM/fp-go/http/headers"
J "github.com/IBM/fp-go/json"
LZ "github.com/IBM/fp-go/lazy"
L "github.com/IBM/fp-go/optics/lens"
O "github.com/IBM/fp-go/option"
S "github.com/IBM/fp-go/string"
T "github.com/IBM/fp-go/tuple"
)
type (
Builder struct {
method O.Option[string]
url string
headers http.Header
body O.Option[E.Either[error, []byte]]
query url.Values
}
// Endomorphism returns an [ENDO.Endomorphism] that transforms a builder
Endomorphism = ENDO.Endomorphism[*Builder]
)
var (
// Default is the default builder
Default = &Builder{method: O.Some(defaultMethod()), headers: make(http.Header), body: noBody}
defaultMethod = F.Constant(http.MethodGet)
// Monoid is the [M.Monoid] for the [Endomorphism]
Monoid = ENDO.Monoid[*Builder]()
// Url is a [L.Lens] for the URL
Url = L.MakeLensRef((*Builder).GetUrl, (*Builder).SetUrl)
// Method is a [L.Lens] for the HTTP method
Method = L.MakeLensRef((*Builder).GetMethod, (*Builder).SetMethod)
// Body is a [L.Lens] for the request body
Body = L.MakeLensRef((*Builder).GetBody, (*Builder).SetBody)
// Headers is a [L.Lens] for the complete set of request headers
Headers = L.MakeLensRef((*Builder).GetHeaders, (*Builder).SetHeaders)
// Query is a [L.Lens] for the set of query parameters
Query = L.MakeLensRef((*Builder).GetQuery, (*Builder).SetQuery)
rawQuery = L.MakeLensRef(getRawQuery, setRawQuery)
getHeader = F.Bind2of2((*Builder).GetHeader)
delHeader = F.Bind2of2((*Builder).DelHeader)
setHeader = F.Bind2of3((*Builder).SetHeader)
noHeader = O.None[string]()
noBody = O.None[E.Either[error, []byte]]()
noQueryArg = O.None[string]()
parseUrl = E.Eitherize1(url.Parse)
parseQuery = E.Eitherize1(url.ParseQuery)
// WithQuery creates a [Endomorphism] for a complete set of query parameters
WithQuery = Query.Set
// WithMethod creates a [Endomorphism] for a certain method
WithMethod = Method.Set
// WithUrl creates a [Endomorphism] for a certain method
WithUrl = Url.Set
// WithHeaders creates a [Endomorphism] for a set of headers
WithHeaders = Headers.Set
// WithBody creates a [Endomorphism] for a request body
WithBody = F.Flow2(
O.Of[E.Either[error, []byte]],
Body.Set,
)
// WithBytes creates a [Endomorphism] for a request body using bytes
WithBytes = F.Flow2(
E.Of[error, []byte],
WithBody,
)
// WithContentType adds the [H.ContentType] header
WithContentType = WithHeader(H.ContentType)
// WithAuthorization adds the [H.Authorization] header
WithAuthorization = WithHeader(H.Authorization)
// WithGet adds the [http.MethodGet] method
WithGet = WithMethod(http.MethodGet)
// WithPost adds the [http.MethodPost] method
WithPost = WithMethod(http.MethodPost)
// WithPut adds the [http.MethodPut] method
WithPut = WithMethod(http.MethodPut)
// WithDelete adds the [http.MethodDelete] method
WithDelete = WithMethod(http.MethodDelete)
// WithBearer creates a [Endomorphism] to add a Bearer [H.Authorization] header
WithBearer = F.Flow2(
S.Format[string]("Bearer %s"),
WithAuthorization,
)
// WithoutBody creates a [Endomorphism] to remove the body
WithoutBody = F.Pipe1(
noBody,
Body.Set,
)
// WithFormData creates a [Endomorphism] to send form data payload
WithFormData = F.Flow4(
url.Values.Encode,
S.ToBytes,
WithBytes,
ENDO.Chain(WithContentType(C.FormEncoded)),
)
)
func setRawQuery(u *url.URL, raw string) *url.URL {
u.RawQuery = raw
return u
}
func getRawQuery(u *url.URL) string {
return u.RawQuery
}
func (builder *Builder) clone() *Builder {
cpy := *builder
cpy.headers = cpy.headers.Clone()
return &cpy
}
// GetTargetUrl constructs a full URL with query parameters on top of the provided URL string
func (builder *Builder) GetTargetUrl() E.Either[error, string] {
// construct the final URL
return F.Pipe3(
builder,
Url.Get,
parseUrl,
E.Chain(F.Flow4(
T.Replicate2[*url.URL],
T.Map2(
F.Flow2(
F.Curry2(setRawQuery),
E.Of[error, func(string) *url.URL],
),
F.Flow3(
rawQuery.Get,
parseQuery,
E.Map[error](F.Flow2(
F.Curry2(FM.ValuesMonoid.Concat)(builder.GetQuery()),
(url.Values).Encode,
)),
),
),
T.Tupled2(E.MonadAp[*url.URL, error, string]),
E.Map[error]((*url.URL).String),
)),
)
}
func (builder *Builder) GetUrl() string {
return builder.url
}
func (builder *Builder) GetMethod() string {
return F.Pipe1(
builder.method,
O.GetOrElse(defaultMethod),
)
}
func (builder *Builder) GetHeaders() http.Header {
return builder.headers
}
func (builder *Builder) GetQuery() url.Values {
return builder.query
}
func (builder *Builder) SetQuery(query url.Values) *Builder {
builder.query = query
return builder
}
func (builder *Builder) GetBody() O.Option[E.Either[error, []byte]] {
return builder.body
}
func (builder *Builder) SetMethod(method string) *Builder {
builder.method = O.Some(method)
return builder
}
func (builder *Builder) SetUrl(url string) *Builder {
builder.url = url
return builder
}
func (builder *Builder) SetHeaders(headers http.Header) *Builder {
builder.headers = headers
return builder
}
func (builder *Builder) SetBody(body O.Option[E.Either[error, []byte]]) *Builder {
builder.body = body
return builder
}
func (builder *Builder) SetHeader(name, value string) *Builder {
builder.headers.Set(name, value)
return builder
}
func (builder *Builder) DelHeader(name string) *Builder {
builder.headers.Del(name)
return builder
}
func (builder *Builder) GetHeader(name string) O.Option[string] {
return F.Pipe2(
name,
builder.headers.Get,
O.FromPredicate(S.IsNonEmpty),
)
}
func (builder *Builder) GetHeaderValues(name string) []string {
return builder.headers.Values(name)
}
// Header returns a [L.Lens] for a single header
func Header(name string) L.Lens[*Builder, O.Option[string]] {
get := getHeader(name)
set := F.Bind1of2(setHeader(name))
del := F.Flow2(
LZ.Of[*Builder],
LZ.Map(delHeader(name)),
)
return L.MakeLens(get, func(b *Builder, value O.Option[string]) *Builder {
cpy := b.clone()
return F.Pipe1(
value,
O.Fold(del(cpy), set(cpy)),
)
})
}
// WithHeader creates a [Endomorphism] for a certain header
func WithHeader(name string) func(value string) Endomorphism {
return F.Flow2(
O.Of[string],
Header(name).Set,
)
}
// WithoutHeader creates a [Endomorphism] to remove a certain header
func WithoutHeader(name string) Endomorphism {
return Header(name).Set(noHeader)
}
// WithJson creates a [Endomorphism] to send JSON payload
func WithJson[T any](data T) Endomorphism {
return Monoid.Concat(
F.Pipe2(
data,
J.Marshal[T],
WithBody,
),
WithContentType(C.Json),
)
}
// QueryArg is a [L.Lens] for the first value of a query argument
func QueryArg(name string) L.Lens[*Builder, O.Option[string]] {
return F.Pipe1(
Query,
L.Compose[*Builder](FM.AtValue(name)),
)
}
// WithQueryArg creates a [Endomorphism] for a certain query argument
func WithQueryArg(name string) func(value string) Endomorphism {
return F.Flow2(
O.Of[string],
QueryArg(name).Set,
)
}
// WithoutQueryArg creates a [Endomorphism] that removes a query argument
func WithoutQueryArg(name string) Endomorphism {
return QueryArg(name).Set(noQueryArg)
}

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 builder
import (
"testing"
F "github.com/IBM/fp-go/function"
C "github.com/IBM/fp-go/http/content"
FD "github.com/IBM/fp-go/http/form"
H "github.com/IBM/fp-go/http/headers"
O "github.com/IBM/fp-go/option"
"github.com/stretchr/testify/assert"
)
func TestBuilder(t *testing.T) {
name := H.ContentType
withContentType := WithHeader(name)
withoutContentType := WithoutHeader(name)
b1 := F.Pipe1(
Default,
withContentType(C.Json),
)
b2 := F.Pipe1(
b1,
withContentType(C.TextPlain),
)
b3 := F.Pipe1(
b2,
withoutContentType,
)
assert.Equal(t, O.None[string](), Default.GetHeader(name))
assert.Equal(t, O.Of(C.Json), b1.GetHeader(name))
assert.Equal(t, O.Of(C.TextPlain), b2.GetHeader(name))
assert.Equal(t, O.None[string](), b3.GetHeader(name))
}
func TestWithFormData(t *testing.T) {
data := F.Pipe1(
FD.Default,
FD.WithValue("a")("b"),
)
res := F.Pipe1(
Default,
WithFormData(data),
)
assert.Equal(t, C.FormEncoded, Headers.Get(res).Get(H.ContentType))
}

View File

@@ -39,7 +39,7 @@ var (
noField = O.None[string]()
// FormMonoid is the [M.Monoid] for the [Endomorphism]
// Monoid is the [M.Monoid] for the [Endomorphism]
Monoid = ENDO.Monoid[url.Values]()
// ValuesMonoid is a [M.Monoid] to concatenate [url.Values] maps

View File

@@ -20,6 +20,7 @@ import (
"io"
"mime"
H "net/http"
"net/url"
"regexp"
A "github.com/IBM/fp-go/array"
@@ -33,6 +34,13 @@ import (
type (
ParsedMediaType = T.Tuple2[string, map[string]string]
HttpError struct {
statusCode int
headers H.Header
body []byte
url *url.URL
}
)
var (
@@ -72,6 +80,31 @@ func ParseMediaType(mediaType string) E.Either[error, ParsedMediaType] {
return E.TryCatchError(T.MakeTuple2(m, p), err)
}
// Error fulfills the error interface
func (r *HttpError) Error() string {
return fmt.Sprintf("invalid status code [%d] when accessing URL [%s]", r.statusCode, r.url)
}
func (r *HttpError) String() string {
return r.Error()
}
func (r *HttpError) StatusCode() int {
return r.statusCode
}
func (r *HttpError) Headers() H.Header {
return r.headers
}
func (r *HttpError) URL() *url.URL {
return r.url
}
func (r *HttpError) Body() []byte {
return r.body
}
func GetHeader(resp *H.Response) H.Header {
return resp.Header
}
@@ -84,6 +117,13 @@ func isValidStatus(resp *H.Response) bool {
return resp.StatusCode >= H.StatusOK && resp.StatusCode < H.StatusMultipleChoices
}
// StatusCodeError creates an instance of [HttpError] filled with information from the response
func StatusCodeError(resp *H.Response) error {
return fmt.Errorf("invalid status code [%d] when accessing URL [%s]", resp.StatusCode, resp.Request.URL)
// read the body
bodyRdr := GetBody(resp)
defer bodyRdr.Close()
// try to access body content
body, _ := io.ReadAll(bodyRdr)
// return an error with comprehensive information
return &HttpError{statusCode: resp.StatusCode, headers: GetHeader(resp).Clone(), body: body, url: resp.Request.URL}
}

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

@@ -50,7 +50,11 @@ func MonadChainFirst[GAB ~func(A) B, A, B any](fa A, f GAB) A {
}
func ChainFirst[GAB ~func(A) B, A, B any](f GAB) func(A) A {
return C.ChainFirst(MonadChain[func(A) A, A, A], MonadMap[func(B) A, B, A], f)
return C.ChainFirst(
Chain[func(A) A, A, A],
Map[func(B) A, B, A],
f,
)
}
func MonadFlap[GAB ~func(A) B, A, B any](fab GAB, a A) B {
@@ -58,5 +62,5 @@ func MonadFlap[GAB ~func(A) B, A, B any](fab GAB, a A) B {
}
func Flap[GAB ~func(A) B, B, A any](a A) func(GAB) B {
return F.Bind2nd(MonadFlap[GAB, A, B], a)
return FC.Flap(Map[func(GAB) B, GAB, B], a)
}

View File

@@ -19,13 +19,6 @@ import (
F "github.com/IBM/fp-go/function"
)
// HKTFGA = HKT<F, HKT<G, A>>
// HKTFGB = HKT<F, HKT<G, B>>
// HKTFGAB = HKT<F, HKT<G, (a: A) => B>>
// HKTGA = HKT<G, A>
// HKTGB = HKT<G, B>
// HKTGAB = HKT<G, (a: A) => B>
func MonadAp[HKTGA, HKTGB, HKTGAB, HKTFGAB, HKTFGGAB, HKTFGA, HKTFGB any](
fap func(HKTFGGAB, HKTFGA) HKTFGB,
fmap func(HKTFGAB, func(HKTGAB) func(HKTGA) HKTGB) HKTFGGAB,
@@ -37,6 +30,29 @@ func MonadAp[HKTGA, HKTGB, HKTGAB, HKTFGAB, HKTFGGAB, HKTFGA, HKTFGB any](
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 F.Flow2(
fmap(F.Flip(gap)),
fap(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>(
// F: Apply<F>,
// G: Apply<G>
@@ -107,3 +123,25 @@ func ApSecond[HKTGA, HKTGB, HKTGBB, A, B any](
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

@@ -43,19 +43,68 @@ func MonadChain[A, B, HKTA, HKTB any](
// HKTA=HKT[A]
// HKTB=HKT[B]
func ChainFirst[A, B, HKTA, HKTB any](
mchain func(HKTA, func(A) HKTA) HKTA,
mmap func(HKTB, func(B) A) HKTA,
mchain func(func(A) HKTA) func(HKTA) HKTA,
mmap func(func(B) A) func(HKTB) HKTA,
f func(A) HKTB) func(HKTA) HKTA {
return F.Bind2nd(mchain, func(a A) HKTA {
return mmap(f(a), F.Constant1[B](a))
return mchain(func(a A) HKTA {
return mmap(F.Constant1[B](a))(f(a))
})
}
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,
) func(HKTA) HKTB {
return func(first HKTA) HKTB {
return MonadChain[A, B](mchain, first, f)
}
return mchain(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

@@ -51,6 +51,14 @@ func MonadMap[E, A, B, HKTFA, HKTFB any](fmap func(HKTFA, func(ET.Either[E, A])
return FC.MonadMap(fmap, ET.MonadMap[E, A, B], fa, f)
}
func Map[E, A, B, HKTFA, HKTFB any](
fmap func(func(ET.Either[E, A]) ET.Either[E, B]) func(HKTFA) HKTFB,
f func(A) B) func(HKTFA) HKTFB {
// HKTGA = Either[E, A]
// HKTGB = Either[E, B]
return FC.Map(fmap, ET.Map[E, A, B], f)
}
// HKTFA = HKT<F, Either<E, A>>
// HKTFB = HKT<F, Either<E, B>>
func MonadBiMap[E1, E2, A, B, HKTFA, HKTFB any](fmap func(HKTFA, func(ET.Either[E1, A]) ET.Either[E2, B]) HKTFB, fa HKTFA, f func(E1) E2, g func(A) B) HKTFB {
@@ -61,10 +69,12 @@ func MonadBiMap[E1, E2, A, B, HKTFA, HKTFB any](fmap func(HKTFA, func(ET.Either[
// HKTFA = HKT<F, Either<E, A>>
// HKTFB = HKT<F, Either<E, B>>
func BiMap[E1, E2, A, B, HKTFA, HKTFB any](fmap func(HKTFA, func(ET.Either[E1, A]) ET.Either[E2, B]) HKTFB, f func(E1) E2, g func(A) B) func(HKTFA) HKTFB {
func BiMap[E1, E2, A, B, HKTFA, HKTFB any](
fmap func(func(ET.Either[E1, A]) ET.Either[E2, B]) func(HKTFA) HKTFB,
f func(E1) E2, g func(A) B) func(HKTFA) HKTFB {
// HKTGA = Either[E, A]
// HKTGB = Either[E, B]
return F.Bind2nd(fmap, ET.BiMap(f, g))
return fmap(ET.BiMap(f, g))
}
// HKTFA = HKT<F, Either<E, A>>
@@ -78,22 +88,29 @@ func MonadChain[E, A, B, HKTFA, HKTFB any](
return fchain(ma, ET.Fold(F.Flow2(ET.Left[B, E], fof), f))
}
// func(fa func(R) T.Task[ET.Either[E, func(A) B]], f func(ET.Either[E, func(A) B]) func(ET.Either[E, A]) ET.Either[E, B]) GEFAB
func Chain[E, A, B, HKTFA, HKTFB any](
fchain func(func(ET.Either[E, A]) HKTFB) func(HKTFA) HKTFB,
fof func(ET.Either[E, B]) HKTFB,
f func(A) HKTFB) func(HKTFA) HKTFB {
// dispatch to the even more generic implementation
return fchain(ET.Fold(F.Flow2(ET.Left[B, E], fof), f))
}
// HKTFA = HKT[Either[E, A]]
// HKTFB = HKT[Either[E, B]]
// HKTFAB = HKT[Either[E, func(A)B]]
func MonadAp[E, A, B, HKTFAB, HKTFGAB, HKTFA, HKTFB any](
fap func(HKTFGAB, HKTFA) HKTFB,
fmap func(HKTFAB, func(ET.Either[E, func(A) B]) func(ET.Either[E, A]) ET.Either[E, B]) HKTFGAB,
fab HKTFAB,
fa HKTFA) HKTFB {
// HKTGA = ET.Either[E, A]
// HKTGB = ET.Either[E, B]
// HKTGAB = ET.Either[E, func(a A) B]
return apply.MonadAp(fap, fmap, ET.MonadAp[B, E, A], fab, fa)
}
func Ap[E, A, B, HKTFAB, HKTFGAB, HKTFA, HKTFB any](
fap func(HKTFA) func(HKTFGAB) HKTFB,
fmap func(func(ET.Either[E, func(A) B]) func(ET.Either[E, A]) ET.Either[E, B]) func(HKTFAB) HKTFGAB,
fa HKTFA) func(HKTFAB) HKTFB {
return apply.Ap(fap, fmap, ET.Ap[B, E, A], fa)
}
func Right[E, A, HKTA any](fof func(ET.Either[E, A]) HKTA, a A) HKTA {
return F.Pipe2(a, ET.Right[E, A], fof)
}

View File

@@ -60,6 +60,13 @@ func MonadChainEitherK[A, E, B, HKTEA, HKTEB any](
return mchain(ma, F.Flow2(f, fromEither))
}
func ChainEitherK[A, E, B, HKTEA, HKTEB any](
mchain func(func(A) HKTEB) func(HKTEA) HKTEB,
fromEither func(ET.Either[E, B]) HKTEB,
f func(A) ET.Either[E, B]) func(HKTEA) HKTEB {
return mchain(F.Flow2(f, fromEither))
}
func ChainOptionK[A, E, B, HKTEA, HKTEB any](
mchain func(HKTEA, func(A) HKTEB) HKTEB,
fromEither func(ET.Either[E, B]) HKTEB,
@@ -78,8 +85,8 @@ func MonadChainFirstEitherK[A, E, B, HKTEA, HKTEB any](
}
func ChainFirstEitherK[A, E, B, HKTEA, HKTEB any](
mchain func(HKTEA, func(A) HKTEA) HKTEA,
mmap func(HKTEB, func(B) A) HKTEA,
mchain func(func(A) HKTEA) func(HKTEA) HKTEA,
mmap func(func(B) A) func(HKTEB) HKTEA,
fromEither func(ET.Either[E, B]) HKTEB,
f func(A) ET.Either[E, B]) func(HKTEA) HKTEA {
return C.ChainFirst(mchain, mmap, F.Flow2(f, fromEither))

View File

@@ -30,8 +30,8 @@ func MonadChainFirstIOK[A, B, HKTA, HKTB any, GIOB ~func() B](
}
func ChainFirstIOK[A, B, HKTA, HKTB any, GIOB ~func() B](
mchain func(HKTA, func(A) HKTA) HKTA,
mmap func(HKTB, func(B) A) HKTA,
mchain func(func(A) HKTA) func(HKTA) HKTA,
mmap func(func(B) A) func(HKTB) HKTA,
fromio func(GIOB) HKTB,
f func(A) GIOB) func(HKTA) HKTA {
// chain
@@ -47,7 +47,7 @@ func MonadChainIOK[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,
f func(A) GR) func(HKTA) HKTB {
// chain

View File

@@ -31,8 +31,8 @@ func MonadChainFirstIOEitherK[GIOB ~func() ET.Either[E, B], E, A, B, HKTA, HKTB
}
func ChainFirstIOEitherK[GIOB ~func() ET.Either[E, B], E, A, B, HKTA, HKTB any](
mchain func(HKTA, func(A) HKTA) HKTA,
mmap func(HKTB, func(B) A) HKTA,
mchain func(func(A) HKTA) func(HKTA) HKTA,
mmap func(func(B) A) func(HKTB) HKTA,
fromio func(GIOB) HKTB,
f func(A) GIOB) func(HKTA) HKTA {
// chain
@@ -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](
mchain func(HKTA, func(A) HKTB) HKTB,
mchain func(func(A) HKTB) func(HKTA) HKTB,
fromio func(GIOB) HKTB,
f func(A) GIOB) func(HKTA) HKTB {
// chain

View File

@@ -15,23 +15,24 @@
package functor
func flap[FAB ~func(A) B, A, B any](a A) func(FAB) B {
return func(f FAB) B {
return f(a)
}
}
func MonadFlap[FAB ~func(A) B, A, B, HKTFAB, HKTB any](
fmap func(HKTFAB, func(FAB) B) HKTB,
fab HKTFAB,
a A,
) HKTB {
return fmap(fab, func(f FAB) B {
return f(a)
})
return fmap(fab, flap[FAB, A, B](a))
}
func Flap[FAB ~func(A) B, A, B, HKTFAB, HKTB any](
fmap func(HKTFAB, func(FAB) B) HKTB,
fmap func(func(FAB) B) func(HKTFAB) HKTB,
a A,
) func(HKTFAB) HKTB {
return func(fab HKTFAB) HKTB {
return MonadFlap(fmap, fab, a)
}
return fmap(flap[FAB, A, B](a))
}

View File

@@ -21,6 +21,46 @@ import (
// HKTFGA = HKT[F, HKT[G, A]]
// 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))
}
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 fmap(gmap(f))
}
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 mmap(key(b))
}

View File

@@ -50,13 +50,11 @@ func MonadChain[A, B, HKTFA, HKTFB any](
}
func Chain[A, B, HKTFA, HKTFB any](
fchain func(HKTFA, func(O.Option[A]) HKTFB) HKTFB,
fchain func(func(O.Option[A]) HKTFB) func(HKTFA) HKTFB,
fof func(O.Option[B]) HKTFB,
f func(A) HKTFB) func(ma HKTFA) HKTFB {
// dispatch to the even more generic implementation
return func(ma HKTFA) HKTFB {
return MonadChain(fchain, fof, ma, f)
}
return fchain(O.Fold(F.Nullary2(O.None[B], fof), f))
}
func MonadAp[A, B, HKTFAB, HKTFGAB, HKTFA, HKTFB any](
@@ -64,12 +62,16 @@ 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,
fab HKTFAB,
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)
}
func Ap[A, B, HKTFAB, HKTFGAB, HKTFA, HKTFB any](
fap func(HKTFA) func(HKTFGAB) HKTFB,
fmap func(func(O.Option[func(A) B]) func(O.Option[A]) O.Option[B]) func(HKTFAB) HKTFGAB,
fa HKTFA) func(HKTFAB) HKTFB {
return apply.Ap(fap, fmap, O.Ap[B, A], fa)
}
func MatchE[A, HKTEA, HKTB any](mchain func(HKTEA, func(O.Option[A]) HKTB) HKTB, onNone func() HKTB, onSome func(A) HKTB) func(HKTEA) HKTB {
return F.Bind2nd(mchain, O.Fold(onNone, onSome))
}
@@ -89,6 +91,14 @@ func MonadChainOptionK[A, B, HKTA, HKTB any](
return MonadChain(fchain, fof, ma, FromOptionK(fof, f))
}
func ChainOptionK[A, B, HKTA, HKTB any](
fchain func(func(O.Option[A]) HKTB) func(HKTA) HKTB,
fof func(O.Option[B]) HKTB,
f func(A) O.Option[B],
) func(HKTA) HKTB {
return Chain(fchain, fof, FromOptionK(fof, f))
}
func MonadAlt[LAZY ~func() HKTFA, A, HKTFA any](
fof func(O.Option[A]) HKTFA,
fchain func(HKTFA, func(O.Option[A]) HKTFA) HKTFA,
@@ -101,11 +111,9 @@ func MonadAlt[LAZY ~func() HKTFA, A, HKTFA any](
func Alt[LAZY ~func() HKTFA, A, HKTFA any](
fof func(O.Option[A]) HKTFA,
fchain func(HKTFA, func(O.Option[A]) HKTFA) HKTFA,
fchain func(func(O.Option[A]) HKTFA) func(HKTFA) HKTFA,
second LAZY) func(HKTFA) HKTFA {
return func(fa HKTFA) HKTFA {
return MonadAlt(fof, fchain, fa, second)
}
return fchain(O.Fold(second, F.Flow2(O.Of[A], fof)))
}

View File

@@ -23,10 +23,25 @@ import (
// here we implement the monadic operations using callbacks from
// 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))
}
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 {
return R.MakeReader(func(r E) 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

@@ -22,6 +22,7 @@ import (
C "github.com/IBM/fp-go/internal/chain"
FC "github.com/IBM/fp-go/internal/functor"
L "github.com/IBM/fp-go/internal/lazy"
T "github.com/IBM/fp-go/tuple"
)
// type IO[A any] = func() A
@@ -52,7 +53,7 @@ func MonadOf[GA ~func() A, A any](a A) GA {
func MonadMap[GA ~func() A, GB ~func() B, A, B any](fa GA, f func(A) B) GB {
return MakeIO[GB](func() B {
return F.Pipe1(fa(), f)
return f(fa())
})
}
@@ -65,13 +66,13 @@ func MonadMapTo[GA ~func() A, GB ~func() B, A, B any](fa GA, b B) GB {
}
func MapTo[GA ~func() A, GB ~func() B, A, B any](b B) func(GA) GB {
return F.Bind2nd(MonadMapTo[GA, GB, A, B], b)
return Map[GA, GB](F.Constant1[A](b))
}
// MonadChain composes computations in sequence, using the return value of one computation to determine the next computation.
func MonadChain[GA ~func() A, GB ~func() B, A, B any](fa GA, f func(A) GB) GB {
return MakeIO[GB](func() B {
return F.Pipe1(fa(), f)()
return f(fa())()
})
}
@@ -87,7 +88,7 @@ func MonadChainTo[GA ~func() A, GB ~func() B, A, B any](fa GA, fb GB) GB {
// ChainTo composes computations in sequence, ignoring the return value of the first computation
func ChainTo[GA ~func() A, GB ~func() B, A, B any](fb GB) func(GA) GB {
return F.Bind2nd(MonadChainTo[GA, GB, A, B], fb)
return Chain[GA, GB](F.Constant1[A](fb))
}
// MonadChainFirst composes computations in sequence, using the return value of one computation to determine the next computation and
@@ -99,7 +100,11 @@ func MonadChainFirst[GA ~func() A, GB ~func() B, A, B any](fa GA, f func(A) GB)
// ChainFirst composes computations in sequence, using the return value of one computation to determine the next computation and
// keeping only the result of the first.
func ChainFirst[GA ~func() A, GB ~func() B, A, B any](f func(A) GB) func(GA) GA {
return C.ChainFirst(MonadChain[GA, GA, A, A], MonadMap[GB, GA, B, A], f)
return C.ChainFirst(
Chain[GA, GA, A, A],
Map[GB, GA, B, A],
f,
)
}
func ApSeq[GB ~func() B, GAB ~func() func(A) B, GA ~func() A, B, A any](ma GA) func(GAB) GB {
@@ -133,6 +138,29 @@ func Delay[GA ~func() A, A any](delay time.Duration) func(GA) GA {
}
}
func after(timestamp time.Time) func() {
return func() {
// check if we need to wait
current := time.Now()
if current.Before(timestamp) {
time.Sleep(timestamp.Sub(current))
}
}
}
// After creates an operation that passes after the given timestamp
func After[GA ~func() A, A any](timestamp time.Time) func(GA) GA {
aft := after(timestamp)
return func(ga GA) GA {
return MakeIO[GA](func() A {
// wait as long as necessary
aft()
// execute after wait
return ga()
})
}
}
// Now returns the current timestamp
func Now[GA ~func() time.Time]() GA {
return MakeIO[GA](time.Now)
@@ -150,5 +178,25 @@ func MonadFlap[FAB ~func(A) B, GFAB ~func() FAB, GB ~func() B, A, B any](fab GFA
}
func Flap[FAB ~func(A) B, GFAB ~func() FAB, GB ~func() B, A, B any](a A) func(GFAB) GB {
return F.Bind2nd(MonadFlap[FAB, GFAB, GB, A, B], a)
return FC.Flap(Map[GFAB, GB, FAB, B], a)
}
// WithTime returns an operation that measures the start and end timestamp of the operation
func WithTime[GTA ~func() T.Tuple3[A, time.Time, time.Time], GA ~func() A, A any](a GA) GTA {
return MakeIO[GTA](func() T.Tuple3[A, time.Time, time.Time] {
t0 := time.Now()
res := a()
t1 := time.Now()
return T.MakeTuple3(res, t0, t1)
})
}
// WithDuration returns an operation that measures the duration of the operation
func WithDuration[GTA ~func() T.Tuple2[A, time.Duration], GA ~func() A, A any](a GA) GTA {
return MakeIO[GTA](func() T.Tuple2[A, time.Duration] {
t0 := time.Now()
res := a()
t1 := time.Now()
return T.MakeTuple2(res, t1.Sub(t0))
})
}

View File

@@ -19,6 +19,7 @@ import (
"time"
G "github.com/IBM/fp-go/io/generic"
T "github.com/IBM/fp-go/tuple"
)
// IO represents a synchronous computation that cannot fail
@@ -143,6 +144,26 @@ func MonadFlap[B, A any](fab IO[func(A) B], a A) IO[B] {
return G.MonadFlap[func(A) B, IO[func(A) B], IO[B], A, B](fab, a)
}
func Flap[FAB ~func(A) B, GFAB ~func() FAB, GB ~func() B, A, B any](a A) func(IO[func(A) B]) IO[B] {
func Flap[B, A any](a A) func(IO[func(A) B]) IO[B] {
return G.Flap[func(A) B, IO[func(A) B], IO[B], A, B](a)
}
// Delay creates an operation that passes in the value after some delay
func Delay[A any](delay time.Duration) func(IO[A]) IO[A] {
return G.Delay[IO[A]](delay)
}
// After creates an operation that passes after the given timestamp
func After[A any](timestamp time.Time) func(IO[A]) IO[A] {
return G.After[IO[A]](timestamp)
}
// WithTime returns an operation that measures the start and end [time.Time] of the operation
func WithTime[A any](a IO[A]) IO[T.Tuple3[A, time.Time, time.Time]] {
return G.WithTime[IO[T.Tuple3[A, time.Time, time.Time]], IO[A]](a)
}
// WithDuration returns an operation that measures the [time.Duration]
func WithDuration[A any](a IO[A]) IO[T.Tuple2[A, time.Duration]] {
return G.WithDuration[IO[T.Tuple2[A, time.Duration]], IO[A]](a)
}

View File

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

57
ioeither/bind_test.go Normal file
View File

@@ -0,0 +1,57 @@
// Copyright (c) 2023 IBM Corp.
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package ioeither
import (
"testing"
E "github.com/IBM/fp-go/either"
F "github.com/IBM/fp-go/function"
"github.com/IBM/fp-go/internal/utils"
"github.com/stretchr/testify/assert"
)
func getLastName(s utils.Initial) IOEither[error, string] {
return Of[error]("Doe")
}
func getGivenName(s utils.WithLastName) IOEither[error, string] {
return Of[error]("John")
}
func TestBind(t *testing.T) {
res := F.Pipe3(
Do[error](utils.Empty),
Bind(utils.SetLastName, getLastName),
Bind(utils.SetGivenName, getGivenName),
Map[error](utils.GetFullName),
)
assert.Equal(t, res(), E.Of[error]("John Doe"))
}
func TestApS(t *testing.T) {
res := F.Pipe3(
Do[error](utils.Empty),
ApS(utils.SetLastName, Of[error]("Doe")),
ApS(utils.SetGivenName, Of[error]("John")),
Map[error](utils.GetFullName),
)
assert.Equal(t, res(), E.Of[error]("John Doe"))
}

View File

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

36
ioeither/file/dir.go Normal file
View File

@@ -0,0 +1,36 @@
// 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 file
import (
"os"
IOE "github.com/IBM/fp-go/ioeither"
)
// MkdirAll create a sequence of directories, see [os.MkdirAll]
func MkdirAll(path string, perm os.FileMode) IOE.IOEither[error, string] {
return IOE.TryCatchError(func() (string, error) {
return path, os.MkdirAll(path, perm)
})
}
// Mkdir create a directory, see [os.Mkdir]
func Mkdir(path string, perm os.FileMode) IOE.IOEither[error, string] {
return IOE.TryCatchError(func() (string, error) {
return path, os.Mkdir(path, perm)
})
}

View File

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

View File

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

View File

@@ -87,7 +87,7 @@ func MonadMap[GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B], E, A, B an
}
func Map[GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B], E, A, B any](f func(A) B) func(GA) GB {
return F.Bind2nd(MonadMap[GA, GB, E, A, B], f)
return eithert.Map(IO.Map[GA, GB, ET.Either[E, A], ET.Either[E, B]], f)
}
func MonadMapTo[GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B], E, A, B any](fa GA, b B) GB {
@@ -95,7 +95,7 @@ func MonadMapTo[GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B], E, A, B
}
func MapTo[GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B], E, A, B any](b B) func(GA) GB {
return F.Bind2nd(MonadMapTo[GA, GB, E, A, B], b)
return Map[GA, GB](F.Constant1[A](b))
}
func MonadChain[GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B], E, A, B any](fa GA, f func(A) GB) GB {
@@ -103,7 +103,7 @@ func MonadChain[GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B], E, A, B
}
func Chain[GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B], E, A, B any](f func(A) GB) func(GA) GB {
return F.Bind2nd(MonadChain[GA, GB, E, A, B], f)
return eithert.Chain(IO.Chain[GA, GB, ET.Either[E, A], ET.Either[E, B]], IO.Of[GB, ET.Either[E, B]], f)
}
func MonadChainTo[GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B], E, A, B any](fa GA, fb GB) GB {
@@ -111,7 +111,7 @@ func MonadChainTo[GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B], E, A,
}
func ChainTo[GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B], E, A, B any](fb GB) func(GA) GB {
return F.Bind2nd(MonadChainTo[GA, GB, E, A, B], fb)
return Chain[GA, GB, E, A, B](F.Constant1[A](fb))
}
func MonadChainEitherK[GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B], E, A, B any](ma GA, f func(A) ET.Either[E, B]) GB {
@@ -134,14 +134,18 @@ func MonadChainIOK[GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B], GR ~f
func ChainIOK[GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B], GR ~func() B, E, A, B any](f func(A) GR) func(GA) GB {
return FI.ChainIOK(
MonadChain[GA, GB, E, A, B],
Chain[GA, GB, E, A, B],
FromIO[GB, GR, E, B],
f,
)
}
func ChainEitherK[GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B], E, A, B any](f func(A) ET.Either[E, B]) func(GA) GB {
return F.Bind2nd(MonadChainEitherK[GA, GB, E, A, B], f)
return FE.ChainEitherK(
Chain[GA, GB, E, A, B],
FromEither[GB, E, B],
f,
)
}
func MonadAp[GB ~func() ET.Either[E, B], GAB ~func() ET.Either[E, func(A) B], GA ~func() ET.Either[E, A], E, A, B any](mab GAB, ma GA) GB {
@@ -207,18 +211,23 @@ func MapLeft[GA1 ~func() ET.Either[E1, A], GA2 ~func() ET.Either[E2, A], E1, E2,
return F.Bind2nd(MonadMapLeft[GA1, GA2, E1, E2, A], f)
}
// Delay creates an operation that passes in the value after some delay
// Delay creates an operation that passes in the value after some [time.Duration]
func Delay[GA ~func() ET.Either[E, A], E, A any](delay time.Duration) func(GA) GA {
return IO.Delay[GA](delay)
}
// After creates an operation that passes after the given [time.Time]
func After[GA ~func() ET.Either[E, A], E, A any](timestamp time.Time) func(GA) GA {
return IO.After[GA](timestamp)
}
func MonadBiMap[GA ~func() ET.Either[E1, A], GB ~func() ET.Either[E2, B], E1, E2, A, B any](fa GA, f func(E1) E2, g func(A) B) GB {
return eithert.MonadBiMap(IO.MonadMap[GA, GB, ET.Either[E1, A], ET.Either[E2, B]], fa, f, g)
}
// BiMap maps a pair of functions over the two type arguments of the bifunctor.
func BiMap[GA ~func() ET.Either[E1, A], GB ~func() ET.Either[E2, B], E1, E2, A, B any](f func(E1) E2, g func(A) B) func(GA) GB {
return eithert.BiMap(IO.MonadMap[GA, GB, ET.Either[E1, A], ET.Either[E2, B]], f, g)
return eithert.BiMap(IO.Map[GA, GB, ET.Either[E1, A], ET.Either[E2, B]], f, g)
}
// Fold convers an IOEither into an IO
@@ -248,8 +257,8 @@ func MonadChainFirst[GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B], E,
// ChainFirst runs the monad returned by the function but returns the result of the original monad
func ChainFirst[GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B], E, A, B any](f func(A) GB) func(GA) GA {
return C.ChainFirst(
MonadChain[GA, GA, E, A, A],
MonadMap[GB, GA, E, B, A],
Chain[GA, GA, E, A, A],
Map[GB, GA, E, B, A],
f,
)
}
@@ -268,8 +277,8 @@ func MonadChainFirstIOK[GA ~func() ET.Either[E, A], GIOB ~func() B, E, A, B any]
// ChainFirstIOK runs the monad returned by the function but returns the result of the original monad
func ChainFirstIOK[GA ~func() ET.Either[E, A], GIOB ~func() B, E, A, B any](f func(A) GIOB) func(GA) GA {
return FI.ChainFirstIOK(
MonadChain[GA, GA, E, A, A],
MonadMap[func() ET.Either[E, B], GA, E, B, A],
Chain[GA, GA, E, A, A],
Map[func() ET.Either[E, B], GA, E, B, A],
FromIO[func() ET.Either[E, B], GIOB, E, B],
f,
)
@@ -289,8 +298,8 @@ func MonadChainFirstEitherK[GA ~func() ET.Either[E, A], E, A, B any](first GA, f
// ChainFirstEitherK runs the monad returned by the function but returns the result of the original monad
func ChainFirstEitherK[GA ~func() ET.Either[E, A], E, A, B any](f func(A) ET.Either[E, B]) func(GA) GA {
return FE.ChainFirstEitherK(
MonadChain[GA, GA, E, A, A],
MonadMap[func() ET.Either[E, B], GA, E, B, A],
Chain[GA, GA, E, A, A],
Map[func() ET.Either[E, B], GA, E, B, A],
FromEither[func() ET.Either[E, B], E, B],
f,
)
@@ -334,7 +343,7 @@ func MonadFlap[GEAB ~func() ET.Either[E, func(A) B], GEB ~func() ET.Either[E, B]
}
func Flap[GEAB ~func() ET.Either[E, func(A) B], GEB ~func() ET.Either[E, B], E, B, A any](a A) func(GEAB) GEB {
return FC.Flap(MonadMap[GEAB, GEB], a)
return FC.Flap(Map[GEAB, GEB], a)
}
func ToIOOption[GA ~func() O.Option[A], GEA ~func() ET.Either[E, A], E, A any](ioe GEA) GA {

View File

@@ -0,0 +1,43 @@
// 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 (
"encoding/json"
"log"
B "github.com/IBM/fp-go/bytes"
ET "github.com/IBM/fp-go/either"
F "github.com/IBM/fp-go/function"
)
// LogJson converts the argument to JSON and then logs it via the format string
// Can be used with [ChainFirst]
func LogJson[GA ~func() ET.Either[error, any], A any](prefix string) func(A) GA {
return func(a A) GA {
// log this
return F.Pipe3(
ET.TryCatchError(json.MarshalIndent(a, "", " ")),
ET.Map[error](B.ToString),
FromEither[func() ET.Either[error, string]],
Chain[func() ET.Either[error, string], GA](func(data string) GA {
return FromImpure[GA](func() {
log.Printf(prefix, data)
})
}),
)
}
}

View File

@@ -18,213 +18,26 @@ package builder
import (
"bytes"
"net/http"
"net/url"
"strconv"
E "github.com/IBM/fp-go/either"
ENDO "github.com/IBM/fp-go/endomorphism"
F "github.com/IBM/fp-go/function"
C "github.com/IBM/fp-go/http/content"
FM "github.com/IBM/fp-go/http/form"
R "github.com/IBM/fp-go/http/builder"
H "github.com/IBM/fp-go/http/headers"
IOG "github.com/IBM/fp-go/io/generic"
IOE "github.com/IBM/fp-go/ioeither"
IOEH "github.com/IBM/fp-go/ioeither/http"
J "github.com/IBM/fp-go/json"
LZ "github.com/IBM/fp-go/lazy"
L "github.com/IBM/fp-go/optics/lens"
O "github.com/IBM/fp-go/option"
S "github.com/IBM/fp-go/string"
T "github.com/IBM/fp-go/tuple"
)
type (
Builder struct {
method O.Option[string]
url string
headers http.Header
body O.Option[IOE.IOEither[error, []byte]]
query url.Values
}
// Endomorphism returns an [ENDO.Endomorphism] that transforms a builder
Endomorphism = ENDO.Endomorphism[*Builder]
)
var (
// Default is the default builder
Default = &Builder{method: O.Some(defaultMethod()), headers: make(http.Header), body: noBody}
defaultMethod = F.Constant(http.MethodGet)
// Monoid is the [M.Monoid] for the [Endomorphism]
Monoid = ENDO.Monoid[*Builder]()
// Url is a [L.Lens] for the URL
Url = L.MakeLensRef((*Builder).GetUrl, (*Builder).SetUrl)
// Method is a [L.Lens] for the HTTP method
Method = L.MakeLensRef((*Builder).GetMethod, (*Builder).SetMethod)
// Body is a [L.Lens] for the request body
Body = L.MakeLensRef((*Builder).GetBody, (*Builder).SetBody)
// Headers is a [L.Lens] for the complete set of request headers
Headers = L.MakeLensRef((*Builder).GetHeaders, (*Builder).SetHeaders)
// Query is a [L.Lens] for the set of query parameters
Query = L.MakeLensRef((*Builder).GetQuery, (*Builder).SetQuery)
rawQuery = L.MakeLensRef(getRawQuery, setRawQuery)
getHeader = F.Bind2of2((*Builder).GetHeader)
delHeader = F.Bind2of2((*Builder).DelHeader)
setHeader = F.Bind2of3((*Builder).SetHeader)
noHeader = O.None[string]()
noBody = O.None[IOE.IOEither[error, []byte]]()
noQueryArg = O.None[string]()
parseUrl = E.Eitherize1(url.Parse)
parseQuery = E.Eitherize1(url.ParseQuery)
// WithQuery creates a [Endomorphism] for a complete set of query parameters
WithQuery = Query.Set
// WithMethod creates a [Endomorphism] for a certain method
WithMethod = Method.Set
// WithUrl creates a [Endomorphism] for a certain method
WithUrl = Url.Set
// WithHeaders creates a [Endomorphism] for a set of headers
WithHeaders = Headers.Set
// WithBody creates a [Endomorphism] for a request body
WithBody = F.Flow2(
O.Of[IOE.IOEither[error, []byte]],
Body.Set,
)
// WithBytes creates a [Endomorphism] for a request body using bytes
WithBytes = F.Flow2(
IOE.Of[error, []byte],
WithBody,
)
// WithContentType adds the [H.ContentType] header
WithContentType = WithHeader(H.ContentType)
// WithAuthorization adds the [H.Authorization] header
WithAuthorization = WithHeader(H.Authorization)
// WithGet adds the [http.MethodGet] method
WithGet = WithMethod(http.MethodGet)
// WithPost adds the [http.MethodPost] method
WithPost = WithMethod(http.MethodPost)
// WithPut adds the [http.MethodPut] method
WithPut = WithMethod(http.MethodPut)
// WithDelete adds the [http.MethodDelete] method
WithDelete = WithMethod(http.MethodDelete)
// WithBearer creates a [Endomorphism] to add a Bearer [H.Authorization] header
WithBearer = F.Flow2(
S.Format[string]("Bearer %s"),
WithAuthorization,
)
// Requester creates a requester from a builder
Requester = (*Builder).Requester
// WithoutBody creates a [Endomorphism] to remove the body
WithoutBody = F.Pipe1(
noBody,
Body.Set,
)
)
func setRawQuery(u *url.URL, raw string) *url.URL {
u.RawQuery = raw
return u
}
func getRawQuery(u *url.URL) string {
return u.RawQuery
}
func (builder *Builder) clone() *Builder {
cpy := *builder
cpy.headers = cpy.headers.Clone()
return &cpy
}
func (builder *Builder) GetUrl() string {
return builder.url
}
func (builder *Builder) GetMethod() string {
return F.Pipe1(
builder.method,
O.GetOrElse(defaultMethod),
)
}
func (builder *Builder) GetHeaders() http.Header {
return builder.headers
}
func (builder *Builder) GetQuery() url.Values {
return builder.query
}
func (builder *Builder) SetQuery(query url.Values) *Builder {
builder.query = query
return builder
}
func (builder *Builder) GetBody() O.Option[IOE.IOEither[error, []byte]] {
return builder.body
}
func (builder *Builder) SetMethod(method string) *Builder {
builder.method = O.Some(method)
return builder
}
func (builder *Builder) SetUrl(url string) *Builder {
builder.url = url
return builder
}
func (builder *Builder) SetHeaders(headers http.Header) *Builder {
builder.headers = headers
return builder
}
func (builder *Builder) SetBody(body O.Option[IOE.IOEither[error, []byte]]) *Builder {
builder.body = body
return builder
}
func (builder *Builder) SetHeader(name, value string) *Builder {
builder.headers.Set(name, value)
return builder
}
func (builder *Builder) DelHeader(name string) *Builder {
builder.headers.Del(name)
return builder
}
func (builder *Builder) GetHeader(name string) O.Option[string] {
return F.Pipe2(
name,
builder.headers.Get,
O.FromPredicate(S.IsNonEmpty),
)
}
func (builder *Builder) GetHeaderValues(name string) []string {
return builder.headers.Values(name)
}
func (builder *Builder) Requester() IOEH.Requester {
func Requester(builder *R.Builder) IOEH.Requester {
withBody := F.Curry3(func(data []byte, url string, method string) IOE.IOEither[error, *http.Request] {
return IOE.TryCatchError(func() (*http.Request, error) {
req, err := http.NewRequest(method, url, bytes.NewReader(data))
if err == nil {
req.Header.Set(H.ContentLength, strconv.Itoa(len(data)))
H.Monoid.Concat(req.Header, builder.headers)
H.Monoid.Concat(req.Header, builder.GetHeaders())
}
return req, err
})
@@ -234,127 +47,21 @@ func (builder *Builder) Requester() IOEH.Requester {
return IOE.TryCatchError(func() (*http.Request, error) {
req, err := http.NewRequest(method, url, nil)
if err == nil {
H.Monoid.Concat(req.Header, builder.headers)
H.Monoid.Concat(req.Header, builder.GetHeaders())
}
return req, err
})
})
// construct the final URL
targetUrl := F.Pipe3(
builder,
Url.Get,
parseUrl,
E.Chain(F.Flow4(
T.Replicate2[*url.URL],
T.Map2(
F.Flow2(
F.Curry2(setRawQuery),
E.Of[error, func(string) *url.URL],
),
F.Flow3(
rawQuery.Get,
parseQuery,
E.Map[error](F.Flow2(
F.Curry2(FM.ValuesMonoid.Concat)(builder.GetQuery()),
(url.Values).Encode,
)),
),
),
T.Tupled2(E.MonadAp[*url.URL, error, string]),
E.Map[error]((*url.URL).String),
)),
)
return F.Pipe6(
builder,
Body.Get,
O.Fold(LZ.Of(IOE.Of[error](withoutBody)), IOE.Map[error](withBody)),
IOG.Map[IOE.IOEither[error, func(string) func(string) IOE.IOEither[error, *http.Request]], IOE.IOEither[error, func(string) IOE.IOEither[error, *http.Request]]](E.Ap[func(string) IOE.IOEither[error, *http.Request]](targetUrl)),
IOE.Flap[error, IOE.IOEither[error, *http.Request]](builder.GetMethod()),
IOE.Flatten[error, *http.Request],
return F.Pipe5(
builder.GetBody(),
O.Fold(LZ.Of(E.Of[error](withoutBody)), E.Map[error](withBody)),
E.Ap[func(string) IOE.IOEither[error, *http.Request]](builder.GetTargetUrl()),
E.Flap[error, IOE.IOEither[error, *http.Request]](builder.GetMethod()),
E.GetOrElse(IOE.Left[*http.Request, error]),
IOE.Map[error](func(req *http.Request) *http.Request {
req.Header = H.Monoid.Concat(req.Header, builder.GetHeaders())
return req
}),
)
}
// Header returns a [L.Lens] for a single header
func Header(name string) L.Lens[*Builder, O.Option[string]] {
get := getHeader(name)
set := F.Bind1of2(setHeader(name))
del := F.Flow2(
LZ.Of[*Builder],
LZ.Map(delHeader(name)),
)
return L.MakeLens(get, func(b *Builder, value O.Option[string]) *Builder {
cpy := b.clone()
return F.Pipe1(
value,
O.Fold(del(cpy), set(cpy)),
)
})
}
// WithHeader creates a [Endomorphism] for a certain header
func WithHeader(name string) func(value string) Endomorphism {
return F.Flow2(
O.Of[string],
Header(name).Set,
)
}
// WithoutHeader creates a [Endomorphism] to remove a certain header
func WithoutHeader(name string) Endomorphism {
return Header(name).Set(noHeader)
}
// WithFormData creates a [Endomorphism] to send form data payload
func WithFormData(value url.Values) Endomorphism {
return F.Flow2(
F.Pipe4(
value,
url.Values.Encode,
S.ToBytes,
IOE.Of[error, []byte],
WithBody,
),
WithContentType(C.FormEncoded),
)
}
// WithJson creates a [Endomorphism] to send JSON payload
func WithJson[T any](data T) Endomorphism {
return F.Flow2(
F.Pipe3(
data,
J.Marshal[T],
IOE.FromEither[error, []byte],
WithBody,
),
WithContentType(C.Json),
)
}
// QueryArg is a [L.Lens] for the first value of a query argument
func QueryArg(name string) L.Lens[*Builder, O.Option[string]] {
return F.Pipe1(
Query,
L.Compose[*Builder](FM.AtValue(name)),
)
}
// WithQueryArg creates a [Endomorphism] for a certain query argument
func WithQueryArg(name string) func(value string) Endomorphism {
return F.Flow2(
O.Of[string],
QueryArg(name).Set,
)
}
// WithoutQueryArg creates a [Endomorphism] that removes a query argument
func WithoutQueryArg(name string) Endomorphism {
return QueryArg(name).Set(noQueryArg)
}

View File

@@ -22,54 +22,26 @@ import (
E "github.com/IBM/fp-go/either"
F "github.com/IBM/fp-go/function"
C "github.com/IBM/fp-go/http/content"
H "github.com/IBM/fp-go/http/headers"
R "github.com/IBM/fp-go/http/builder"
IO "github.com/IBM/fp-go/io"
IOE "github.com/IBM/fp-go/ioeither"
O "github.com/IBM/fp-go/option"
"github.com/stretchr/testify/assert"
)
func TestBuilder(t *testing.T) {
name := H.ContentType
withContentType := WithHeader(name)
withoutContentType := WithoutHeader(name)
b1 := F.Pipe1(
Default,
withContentType(C.Json),
)
b2 := F.Pipe1(
b1,
withContentType(C.TextPlain),
)
b3 := F.Pipe1(
b2,
withoutContentType,
)
assert.Equal(t, O.None[string](), Default.GetHeader(name))
assert.Equal(t, O.Of(C.Json), b1.GetHeader(name))
assert.Equal(t, O.Of(C.TextPlain), b2.GetHeader(name))
assert.Equal(t, O.None[string](), b3.GetHeader(name))
}
func TestBuilderWithQuery(t *testing.T) {
// add some query
withLimit := WithQueryArg("limit")("10")
withUrl := WithUrl("http://www.example.org?a=b")
withLimit := R.WithQueryArg("limit")("10")
withUrl := R.WithUrl("http://www.example.org?a=b")
b := F.Pipe2(
Default,
R.Default,
withLimit,
withUrl,
)
req := F.Pipe2(
b.Requester(),
req := F.Pipe3(
b,
Requester,
IOE.Map[error](func(r *http.Request) *url.URL {
return r.URL
}),

View File

@@ -16,6 +16,8 @@
package ioeither
import (
"time"
ET "github.com/IBM/fp-go/either"
I "github.com/IBM/fp-go/io"
G "github.com/IBM/fp-go/ioeither/generic"
@@ -173,7 +175,7 @@ func MonadMapLeft[E1, E2, A any](fa IOEither[E1, A], f func(E1) E2) IOEither[E2,
return G.MonadMapLeft[IOEither[E1, A], IOEither[E2, A]](fa, f)
}
func MapLeft[E1, E2, A any](f func(E1) E2) func(IOEither[E1, A]) IOEither[E2, A] {
func MapLeft[A, E1, E2 any](f func(E1) E2) func(IOEither[E1, A]) IOEither[E2, A] {
return G.MapLeft[IOEither[E1, A], IOEither[E2, A]](f)
}
@@ -276,3 +278,13 @@ func Flap[E, B, A any](a A) func(IOEither[E, func(A) B]) IOEither[E, B] {
func ToIOOption[E, A any](ioe IOEither[E, A]) IOO.IOOption[A] {
return G.ToIOOption[IOO.IOOption[A]](ioe)
}
// Delay creates an operation that passes in the value after some delay
func Delay[E, A any](delay time.Duration) func(IOEither[E, A]) IOEither[E, A] {
return G.Delay[IOEither[E, A]](delay)
}
// After creates an operation that passes after the given [time.Time]
func After[E, A any](timestamp time.Time) func(IOEither[E, A]) IOEither[E, A] {
return G.After[IOEither[E, A]](timestamp)
}

26
ioeither/logging.go Normal file
View File

@@ -0,0 +1,26 @@
// 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 (
G "github.com/IBM/fp-go/ioeither/generic"
)
// LogJson converts the argument to pretty printed JSON and then logs it via the format string
// Can be used with [ChainFirst]
func LogJson[A any](prefix string) func(A) IOEither[error, any] {
return G.LogJson[IOEither[error, any], A](prefix)
}

42
ioeither/logging_test.go Normal file
View File

@@ -0,0 +1,42 @@
// 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/stretchr/testify/assert"
)
func TestLogging(t *testing.T) {
type SomeData struct {
Key string `json:"key"`
Value string `json:"value"`
}
src := &SomeData{Key: "key", Value: "value"}
res := F.Pipe1(
Of[error](src),
ChainFirst(LogJson[*SomeData]("Data: \n%s")),
)
dst := res()
assert.Equal(t, E.Of[error](src), dst)
}

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

@@ -90,8 +90,8 @@ func MonadChainFirst[GA ~func() O.Option[A], GB ~func() O.Option[B], A, B any](m
// ChainFirst runs the monad returned by the function but returns the result of the original monad
func ChainFirst[GA ~func() O.Option[A], GB ~func() O.Option[B], A, B any](f func(A) GB) func(GA) GA {
return C.ChainFirst(
MonadChain[GA, GA, A, A],
MonadMap[GB, GA, B, A],
Chain[GA, GA, A, A],
Map[GB, GA, B, A],
f,
)
}
@@ -110,15 +110,15 @@ func MonadChainFirstIOK[GA ~func() O.Option[A], GIOB ~func() B, A, B any](first
// ChainFirstIOK runs the monad returned by the function but returns the result of the original monad
func ChainFirstIOK[GA ~func() O.Option[A], GIOB ~func() B, A, B any](f func(A) GIOB) func(GA) GA {
return FI.ChainFirstIOK(
MonadChain[GA, GA, A, A],
MonadMap[func() O.Option[B], GA, B, A],
Chain[GA, GA, A, A],
Map[func() O.Option[B], GA, B, A],
FromIO[func() O.Option[B], GIOB, B],
f,
)
}
func Chain[GA ~func() O.Option[A], GB ~func() O.Option[B], A, B any](f func(A) GB) func(GA) GB {
return optiont.Chain(IO.MonadChain[GA, GB, O.Option[A], O.Option[B]], IO.MonadOf[GB, O.Option[B]], f)
return optiont.Chain(IO.Chain[GA, GB, O.Option[A], O.Option[B]], IO.Of[GB, O.Option[B]], f)
}
func MonadChainOptionK[GA ~func() O.Option[A], GB ~func() O.Option[B], A, B any](ma GA, f func(A) O.Option[B]) GB {
@@ -131,7 +131,11 @@ func MonadChainOptionK[GA ~func() O.Option[A], GB ~func() O.Option[B], A, B any]
}
func ChainOptionK[GA ~func() O.Option[A], GB ~func() O.Option[B], A, B any](f func(A) O.Option[B]) func(GA) GB {
return F.Bind2nd(MonadChainOptionK[GA, GB, A, B], f)
return optiont.ChainOptionK(
IO.Chain[GA, GB, O.Option[A], O.Option[B]],
FromOption[GB, B],
f,
)
}
func MonadChainIOK[GA ~func() O.Option[A], GB ~func() O.Option[B], GR ~func() B, A, B any](ma GA, f func(A) GR) GB {
@@ -145,7 +149,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 {
return FI.ChainIOK(
MonadChain[GA, GB, A, B],
Chain[GA, GB, A, B],
FromIO[GB, GR, B],
f,
)
@@ -159,7 +163,10 @@ func MonadAp[GB ~func() O.Option[B], GAB ~func() O.Option[func(A) B], GA ~func()
}
func Ap[GB ~func() O.Option[B], GAB ~func() O.Option[func(A) B], GA ~func() O.Option[A], A, B any](ma GA) func(GAB) GB {
return F.Bind2nd(MonadAp[GB, GAB, GA, A, B], ma)
return optiont.Ap(
IO.Ap[GB, func() func(O.Option[A]) O.Option[B], GA, O.Option[B], O.Option[A]],
IO.Map[GAB, func() func(O.Option[A]) O.Option[B], O.Option[func(A) B], func(O.Option[A]) O.Option[B]],
ma)
}
func Flatten[GA ~func() O.Option[A], GAA ~func() O.Option[GA], A any](mma GAA) GA {
@@ -219,6 +226,11 @@ func Delay[GA ~func() O.Option[A], A any](delay time.Duration) func(GA) GA {
return IO.Delay[GA](delay)
}
// After creates an operation that passes after the given [time.Time]
func After[GA ~func() O.Option[A], A any](timestamp time.Time) func(GA) GA {
return IO.After[GA](timestamp)
}
// Fold convers an IOOption into an IO
func Fold[GA ~func() O.Option[A], GB ~func() B, A, B any](onNone func() GB, onSome func(A) GB) func(GA) GB {
return optiont.MatchE(IO.MonadChain[GA, GB, O.Option[A], B], onNone, onSome)
@@ -231,7 +243,7 @@ func Defer[GA ~func() O.Option[A], A any](gen func() GA) GA {
func MonadAlt[LAZY ~func() GIOA, GIOA ~func() O.Option[A], A any](first GIOA, second LAZY) GIOA {
return optiont.MonadAlt(
IO.Of[GIOA],
IO.MonadOf[GIOA],
IO.MonadChain[GIOA, GIOA],
first,
@@ -240,5 +252,10 @@ func MonadAlt[LAZY ~func() GIOA, GIOA ~func() O.Option[A], A any](first GIOA, se
}
func Alt[LAZY ~func() GIOA, GIOA ~func() O.Option[A], A any](second LAZY) func(GIOA) GIOA {
return F.Bind2nd(MonadAlt[LAZY], second)
return optiont.Alt(
IO.Of[GIOA],
IO.Chain[GIOA, GIOA],
second,
)
}

View File

@@ -16,6 +16,8 @@
package iooption
import (
"time"
ET "github.com/IBM/fp-go/either"
I "github.com/IBM/fp-go/io"
IO "github.com/IBM/fp-go/io"
@@ -164,3 +166,13 @@ func MonadChainFirstIOK[A, B any](first IOOption[A], f func(A) IO.IO[B]) IOOptio
func ChainFirstIOK[A, B any](f func(A) IO.IO[B]) func(IOOption[A]) IOOption[A] {
return G.ChainFirstIOK[IOOption[A], IO.IO[B]](f)
}
// Delay creates an operation that passes in the value after some delay
func Delay[A any](delay time.Duration) func(IOOption[A]) IOOption[A] {
return G.Delay[IOOption[A]](delay)
}
// After creates an operation that passes after the given [time.Time]
func After[A any](timestamp time.Time) func(IOOption[A]) IOOption[A] {
return G.After[IOOption[A]](timestamp)
}

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))
}
// 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]] {
return Eq(EQ.FromStrictEquals[A]())
}

View File

@@ -156,5 +156,5 @@ func MonadFlap[B, A any](fab Option[func(A) B], a A) Option[B] {
}
func Flap[B, A any](a A) func(Option[func(A) B]) Option[B] {
return F.Bind2nd(MonadFlap[B, A], a)
return FC.Flap(Map[func(A) B, B], 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,
)
}

View File

@@ -127,5 +127,5 @@ func MonadFlap[GAB ~func(R) func(A) B, GB ~func(R) B, R, A, B any](fab GAB, a A)
}
func Flap[GAB ~func(R) func(A) B, GB ~func(R) B, R, A, B any](a A) func(GAB) GB {
return FC.Flap(MonadMap[GAB, GB], a)
return FC.Flap(Map[GAB, GB], a)
}

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 {
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 {
@@ -143,7 +143,7 @@ func MonadBiMap[GA ~func(E) ET.Either[E1, A], GB ~func(E) ET.Either[E2, B], E, E
// BiMap maps a pair of functions over the two type arguments of the bifunctor.
func BiMap[GA ~func(E) ET.Either[E1, A], GB ~func(E) ET.Either[E2, B], E, E1, E2, A, B any](f func(E1) E2, g func(A) B) func(GA) GB {
return eithert.BiMap(R.MonadMap[GA, GB, E, ET.Either[E1, A], ET.Either[E2, B]], f, g)
return eithert.BiMap(R.Map[GA, GB, E, ET.Either[E1, A], ET.Either[E2, B]], f, g)
}
// Local changes the value of the local context during the execution of the action `ma` (similar to `Contravariant`'s
@@ -162,5 +162,14 @@ func MonadFlap[GEFAB ~func(E) ET.Either[L, func(A) B], GEB ~func(E) ET.Either[L,
}
func Flap[GEFAB ~func(E) ET.Either[L, func(A) B], GEB ~func(E) ET.Either[L, B], L, E, A, B any](a A) func(GEFAB) GEB {
return FC.Flap(MonadMap[GEFAB, GEB], a)
return FC.Flap(Map[GEFAB, GEB], a)
}
func MonadMapLeft[GA1 ~func(C) ET.Either[E1, A], GA2 ~func(C) ET.Either[E2, A], C, E1, E2, A any](fa GA1, f func(E1) E2) GA2 {
return eithert.MonadMapLeft(R.MonadMap[GA1, GA2, C, ET.Either[E1, A], ET.Either[E2, A]], fa, f)
}
// MapLeft applies a mapping function to the error channel
func MapLeft[GA1 ~func(C) ET.Either[E1, A], GA2 ~func(C) ET.Either[E2, A], C, E1, E2, A any](f func(E1) E2) func(GA1) GA2 {
return F.Bind2nd(MonadMapLeft[GA1, GA2, C, E1, E2, A], f)
}

View File

@@ -151,3 +151,12 @@ func MonadFlap[L, E, A, B any](fab ReaderEither[L, E, func(A) B], a A) ReaderEit
func Flap[L, E, B, A any](a A) func(ReaderEither[L, E, func(A) B]) ReaderEither[L, E, B] {
return G.Flap[ReaderEither[L, E, func(A) B], ReaderEither[L, E, B]](a)
}
func MonadMapLeft[C, E1, E2, A any](fa ReaderEither[C, E1, A], f func(E1) E2) ReaderEither[C, E2, A] {
return G.MonadMapLeft[ReaderEither[C, E1, A], ReaderEither[C, E2, A]](fa, f)
}
// MapLeft applies a mapping function to the error channel
func MapLeft[C, E1, E2, A any](f func(E1) E2) func(ReaderEither[C, E1, A]) ReaderEither[C, E2, A] {
return G.MapLeft[ReaderEither[C, E1, A], ReaderEither[C, E2, A]](f)
}

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 {
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 {
@@ -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 {
return FIO.ChainIOK(
MonadChain[GEA, GEB],
Chain[GEA, GEB],
FromIO[GEB],
f,
)
@@ -114,8 +114,8 @@ func MonadChainFirstIOK[GEA ~func(E) GIOA, GEB ~func(E) GIOB, GIOA ~func() A, GI
func ChainFirstIOK[GEA ~func(E) GIOA, GEB ~func(E) GIOB, GIOA ~func() A, GIOB ~func() B, E, A, B any](f func(A) GIOB) func(GEA) GEA {
return FIO.ChainFirstIOK(
MonadChain[GEA, GEA],
MonadMap[GEB, GEA],
Chain[GEA, GEA],
Map[GEB, GEA],
FromIO[GEB],
f,
)
@@ -162,5 +162,5 @@ func MonadFlap[GEFAB ~func(E) GIOFAB, GEB ~func(E) GIOB, GIOFAB ~func() func(A)
}
func Flap[GEFAB ~func(E) GIOFAB, GEB ~func(E) GIOB, GIOFAB ~func() func(A) B, GIOB ~func() B, E, A, B any](a A) func(GEFAB) GEB {
return FC.Flap(MonadMap[GEFAB, GEB], a)
return FC.Flap(Map[GEFAB, GEB], a)
}

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)
}
// 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]] {
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)
}
// 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] {
return Eq[GEA](ET.FromStrictEquals[E, A]())
}

View File

@@ -405,7 +405,7 @@ func MonadBiMap[GA ~func(R) GE1A, GB ~func(R) GE2B, GE1A ~func() ET.Either[E1, A
// BiMap maps a pair of functions over the two type arguments of the bifunctor.
func BiMap[GA ~func(R) GE1A, GB ~func(R) GE2B, GE1A ~func() ET.Either[E1, A], GE2B ~func() ET.Either[E2, B], R, E1, E2, A, B any](f func(E1) E2, g func(A) B) func(GA) GB {
return eithert.BiMap(G.MonadMap[GA, GB, GE1A, GE2B, R, ET.Either[E1, A], ET.Either[E2, B]], f, g)
return eithert.BiMap(G.Map[GA, GB, GE1A, GE2B, R, ET.Either[E1, A], ET.Either[E2, B]], f, g)
}
// Swap changes the order of type parameters
@@ -438,5 +438,14 @@ func MonadFlap[GREAB ~func(R) GEAB, GREB ~func(R) GEB, GEAB ~func() ET.Either[E,
}
func Flap[GREAB ~func(R) GEAB, GREB ~func(R) GEB, GEAB ~func() ET.Either[E, func(A) B], GEB ~func() ET.Either[E, B], R, E, B, A any](a A) func(GREAB) GREB {
return FC.Flap(MonadMap[GREAB, GREB], a)
return FC.Flap(Map[GREAB, GREB], a)
}
func MonadMapLeft[GREA1 ~func(R) GEA1, GREA2 ~func(R) GEA2, GEA1 ~func() ET.Either[E1, A], GEA2 ~func() ET.Either[E2, A], R, E1, E2, A any](fa GREA1, f func(E1) E2) GREA2 {
return eithert.MonadMapLeft(G.MonadMap[GREA1, GREA2], fa, f)
}
// MapLeft applies a mapping function to the error channel
func MapLeft[GREA1 ~func(R) GEA1, GREA2 ~func(R) GEA2, GEA1 ~func() ET.Either[E1, A], GEA2 ~func() ET.Either[E2, A], R, E1, E2, A any](f func(E1) E2) func(GREA1) GREA2 {
return F.Bind2nd(MonadMapLeft[GREA1, GREA2], f)
}

View File

@@ -280,3 +280,12 @@ func MonadFlap[R, E, B, A any](fab ReaderIOEither[R, E, func(A) B], a A) ReaderI
func Flap[R, E, B, A any](a A) func(ReaderIOEither[R, E, func(A) B]) ReaderIOEither[R, E, B] {
return G.Flap[ReaderIOEither[R, E, func(A) B], ReaderIOEither[R, E, B]](a)
}
func MonadMapLeft[R, E1, E2, A any](fa ReaderIOEither[R, E1, A], f func(E1) E2) ReaderIOEither[R, E2, A] {
return G.MonadMapLeft[ReaderIOEither[R, E1, A], ReaderIOEither[R, E2, A]](fa, f)
}
// MapLeft applies a mapping function to the error channel
func MapLeft[R, A, E1, E2 any](f func(E1) E2) func(ReaderIOEither[R, E1, A]) ReaderIOEither[R, E2, A] {
return G.MapLeft[ReaderIOEither[R, E1, A], ReaderIOEither[R, E2, A]](f)
}

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)
}

Some files were not shown because too many files have changed in this diff Show More