1
0
mirror of https://github.com/IBM/fp-go.git synced 2025-08-28 19:49:07 +02:00

Compare commits

...

42 Commits

Author SHA1 Message Date
Dr. Carsten Leue
01d490b710 fix: add State monad
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-02-13 14:23:33 +01:00
Dr. Carsten Leue
01786a054b fix: refactor Writer monad
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-02-13 10:44:57 +01:00
Dr. Carsten Leue
d0e4984b60 fix: switch internal implementation of iterator from Tuple2 to Pair
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-02-12 10:33:42 +01:00
Dr. Carsten Leue
51ed1693a5 fix: refactory tests a bit
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-02-09 15:06:06 +01:00
Dr. Carsten Leue
0afedbd7fe fix: generic order on ContramapCache
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-02-08 13:19:01 +01:00
Dr. Carsten Leue
3f1bde219a fix: add go 1.22 to test
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-02-08 10:08:25 +01:00
Dr. Carsten Leue
6f91e91eb9 fix: add hash to http builder
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-02-08 09:42:29 +01:00
Dr. Carsten Leue
9f6b6d4968 fix: optimize use of tuples
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-02-08 08:41:51 +01:00
Dr. Carsten Leue
79652d8474 fix: disable inlining of debug functions
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-02-07 13:41:47 +01:00
Dr. Carsten Leue
a774d63e66 fix: try a more efficient implementation of standard interfaces
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-02-07 13:26:17 +01:00
Dr. Carsten Leue
d86cf55a3d fix: add correct _test suffix to examples
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-02-07 11:32:14 +01:00
Carsten Leue
8150ae2a68 fix: refactor either type (#102)
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-02-07 11:03:20 +01:00
Dr. Carsten Leue
7daf65effc Merge branch 'main' of github.com:IBM/fp-go 2024-02-07 10:10:50 +01:00
Dr. Carsten Leue
909f7c3bce fix: linter bugs
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-02-07 10:07:37 +01:00
renovate[bot]
5f0c644c6d chore(deps): update actions/setup-node action to v4.0.2 2024-02-07 09:04:33 +00:00
Dr. Carsten Leue
9b3d9c6930 fix: shrink size of DI a bit
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-02-06 22:13:34 +01:00
Dr. Carsten Leue
59381c1e50 fix: some internal refactorings
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-02-06 21:35:33 +01:00
Dr. Carsten Leue
358573cc20 fix: optimize strings package
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-02-06 14:43:23 +01:00
Dr. Carsten Leue
e166806d1b fix: adjust to some linter findings
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-02-05 13:46:12 +01:00
Dr. Carsten Leue
02ec50c91d fix: attempt to expose Monad as an interface
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-02-05 12:29:45 +01:00
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
242 changed files with 8644 additions and 1835 deletions

View File

@@ -17,8 +17,8 @@ on:
env: env:
# Currently no way to detect automatically # Currently no way to detect automatically
DEFAULT_BRANCH: main DEFAULT_BRANCH: main
GO_VERSION: 1.20.6 # renovate: datasource=golang-version depName=golang GO_VERSION: 1.21.6 # renovate: datasource=golang-version depName=golang
NODE_VERSION: 18 NODE_VERSION: 20
DRY_RUN: true DRY_RUN: true
jobs: jobs:
@@ -26,7 +26,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy: strategy:
matrix: matrix:
go-version: [ '1.20.x', '1.21.x' ] go-version: [ '1.20.x', '1.21.x', '1.22.x']
steps: steps:
# full checkout for semantic-release # full checkout for semantic-release
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
@@ -60,7 +60,7 @@ jobs:
fetch-depth: 0 fetch-depth: 0
- name: Set up Node.js ${{ env.NODE_VERSION }} - name: Set up Node.js ${{ env.NODE_VERSION }}
uses: actions/setup-node@b39b52d1213e96004bfcb1c61a8a6fa8ab84f3e8 # v4.0.1 uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
with: with:
node-version: ${{ env.NODE_VERSION }} node-version: ${{ env.NODE_VERSION }}

View File

@@ -58,7 +58,7 @@ func MapWithIndex[A, B any](f func(int, A) B) func([]A) []B {
} }
func Map[A, B any](f func(a A) B) func([]A) []B { func Map[A, B any](f func(a A) B) func([]A) []B {
return F.Bind2nd(MonadMap[A, B], f) return G.Map[[]A, []B, A, B](f)
} }
func MapRef[A, B any](f func(a *A) B) func([]A) []B { func MapRef[A, B any](f func(a *A) B) func([]A) []B {

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

@@ -147,7 +147,7 @@ func MonadMap[GA ~[]A, GB ~[]B, A, B any](as GA, f func(a A) B) GB {
} }
func Map[GA ~[]A, GB ~[]B, A, B any](f func(a A) B) func(GA) GB { func Map[GA ~[]A, GB ~[]B, A, B any](f func(a A) B) func(GA) GB {
return F.Bind2nd(MonadMap[GA, GB, A, B], f) return array.Map[GA, GB](f)
} }
func MonadMapWithIndex[GA ~[]A, GB ~[]B, A, B any](as GA, f func(int, A) B) GB { func MonadMapWithIndex[GA ~[]A, GB ~[]B, A, B any](as GA, f func(int, A) B) GB {
@@ -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 { 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 { 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,
)
}

43
array/generic/monad.go Normal file
View File

@@ -0,0 +1,43 @@
// Copyright (c) 2024 IBM Corp.
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package generic
import (
"github.com/IBM/fp-go/internal/monad"
)
type arrayMonad[A, B any, GA ~[]A, GB ~[]B, GAB ~[]func(A) B] struct{}
func (o *arrayMonad[A, B, GA, GB, GAB]) Of(a A) GA {
return Of[GA, A](a)
}
func (o *arrayMonad[A, B, GA, GB, GAB]) Map(f func(A) B) func(GA) GB {
return Map[GA, GB, A, B](f)
}
func (o *arrayMonad[A, B, GA, GB, GAB]) Chain(f func(A) GB) func(GA) GB {
return Chain[GA, GB, A, B](f)
}
func (o *arrayMonad[A, B, GA, GB, GAB]) Ap(fa GA) func(GAB) GB {
return Ap[GB, GAB, GA, B, A](fa)
}
// Monad implements the monadic operations for an array
func Monad[A, B any, GA ~[]A, GB ~[]B, GAB ~[]func(A) B]() monad.Monad[A, B, GA, GB, GAB] {
return &arrayMonad[A, B, GA, GB, GAB]{}
}

26
array/monad.go Normal file
View File

@@ -0,0 +1,26 @@
// Copyright (c) 2024 IBM Corp.
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package array
import (
G "github.com/IBM/fp-go/array/generic"
"github.com/IBM/fp-go/internal/monad"
)
// Monad returns the monadic operations for an array
func Monad[A, B any]() monad.Monad[A, B, []A, []B, []func(A) B] {
return G.Monad[A, B, []A, []B, []func(A) B]()
}

View File

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

View File

@@ -44,11 +44,11 @@ func From[A any](first A, data ...A) NonEmptyArray[A] {
return buffer return buffer
} }
func IsEmpty[A any](as NonEmptyArray[A]) bool { func IsEmpty[A any](_ NonEmptyArray[A]) bool {
return false return false
} }
func IsNonEmpty[A any](as NonEmptyArray[A]) bool { func IsNonEmpty[A any](_ NonEmptyArray[A]) bool {
return true return true
} }

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

@@ -32,20 +32,20 @@ type bounded[T any] struct {
b T b T
} }
func (self bounded[T]) Equals(x, y T) bool { func (b bounded[T]) Equals(x, y T) bool {
return self.e(x, y) return b.e(x, y)
} }
func (self bounded[T]) Compare(x, y T) int { func (b bounded[T]) Compare(x, y T) int {
return self.c(x, y) return b.c(x, y)
} }
func (self bounded[T]) Top() T { func (b bounded[T]) Top() T {
return self.t return b.t
} }
func (self bounded[T]) Bottom() T { func (b bounded[T]) Bottom() T {
return self.b return b.b
} }
// MakeBounded creates an instance of a bounded type // MakeBounded creates an instance of a bounded type

View File

@@ -15,6 +15,10 @@
package bytes package bytes
func Empty() []byte {
return Monoid.Empty()
}
func ToString(a []byte) string { func ToString(a []byte) string {
return string(a) return string(a)
} }

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, ", f%d F%d", j, j)
} }
fmt.Fprintf(f, ") T%d {\n", i) fmt.Fprintf(f, ") T%d {\n", i)
for j := 1; j <= i; j++ { fmt.Fprintf(f, " return ")
fmt.Fprintf(f, " t%d := f%d(t%d)\n", j, j, j-1) 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, "}") fmt.Fprintln(f, "}")
} }

View File

@@ -405,8 +405,6 @@ func generateTupleHelpers(filename string, count int) error {
fmt.Fprintf(f, ` fmt.Fprintf(f, `
import ( import (
"fmt"
"encoding/json"
M "github.com/IBM/fp-go/monoid" M "github.com/IBM/fp-go/monoid"
O "github.com/IBM/fp-go/ord" O "github.com/IBM/fp-go/ord"
) )
@@ -457,7 +455,7 @@ func generateTupleMarshal(f *os.File, i int) {
fmt.Fprintf(f, "func (t ") fmt.Fprintf(f, "func (t ")
writeTupleType(f, "T", i) writeTupleType(f, "T", i)
fmt.Fprintf(f, ") MarshalJSON() ([]byte, error) {\n") fmt.Fprintf(f, ") MarshalJSON() ([]byte, error) {\n")
fmt.Fprintf(f, " return json.Marshal([]any{") fmt.Fprintf(f, " return tupleMarshalJSON(")
// function prototypes // function prototypes
for j := 1; j <= i; j++ { for j := 1; j <= i; j++ {
if j > 1 { if j > 1 {
@@ -465,7 +463,7 @@ func generateTupleMarshal(f *os.File, i int) {
} }
fmt.Fprintf(f, "t.F%d", j) fmt.Fprintf(f, "t.F%d", j)
} }
fmt.Fprintf(f, "})\n") fmt.Fprintf(f, ")\n")
fmt.Fprintf(f, "}\n") fmt.Fprintf(f, "}\n")
} }
@@ -475,19 +473,12 @@ func generateTupleUnmarshal(f *os.File, i int) {
fmt.Fprintf(f, "func (t *") fmt.Fprintf(f, "func (t *")
writeTupleType(f, "T", i) writeTupleType(f, "T", i)
fmt.Fprintf(f, ") UnmarshalJSON(data []byte) error {\n") fmt.Fprintf(f, ") UnmarshalJSON(data []byte) error {\n")
fmt.Fprintf(f, " var tmp []json.RawMessage\n") fmt.Fprintf(f, " return tupleUnmarshalJSON(data")
fmt.Fprintf(f, " if err := json.Unmarshal(data, &tmp); err != nil {return err}\n") // function prototypes
fmt.Fprintf(f, " l := len(tmp)\n")
// unmarshal fields
for j := 1; j <= i; j++ { for j := 1; j <= i; j++ {
fmt.Fprintf(f, " if l > %d {\n", j-1) fmt.Fprintf(f, ", &t.F%d", j)
fmt.Fprintf(f, " if err := json.Unmarshal(tmp[%d], &t.F%d); err != nil {return err}\n", j-1, j)
} }
fmt.Fprintf(f, " ") fmt.Fprintf(f, ")\n")
for j := 1; j <= i; j++ {
fmt.Fprintf(f, "}")
}
fmt.Fprintf(f, "\n return nil\n")
fmt.Fprintf(f, "}\n") fmt.Fprintf(f, "}\n")
} }
@@ -570,30 +561,13 @@ func generateTupleString(f *os.File, i int) {
writeTupleType(f, "T", i) writeTupleType(f, "T", i)
fmt.Fprintf(f, ") String() string {\n") fmt.Fprintf(f, ") String() string {\n")
// convert to string // convert to string
fmt.Fprintf(f, " return fmt.Sprintf(\"Tuple%d[", i) fmt.Fprint(f, " return tupleString(")
for j := 1; j <= i; j++ {
if j > 1 {
fmt.Fprintf(f, ", ")
}
fmt.Fprintf(f, "%s", "%T")
}
fmt.Fprintf(f, "](")
for j := 1; j <= i; j++ {
if j > 1 {
fmt.Fprintf(f, ", ")
}
fmt.Fprintf(f, "%s", "%v")
}
fmt.Fprintf(f, ")\", ")
for j := 1; j <= i; j++ { for j := 1; j <= i; j++ {
if j > 1 { if j > 1 {
fmt.Fprintf(f, ", ") fmt.Fprintf(f, ", ")
} }
fmt.Fprintf(f, "t.F%d", j) fmt.Fprintf(f, "t.F%d", j)
} }
for j := 1; j <= i; j++ {
fmt.Fprintf(f, ", t.F%d", j)
}
fmt.Fprintf(f, ")\n") fmt.Fprintf(f, ")\n")
fmt.Fprintf(f, "}\n") fmt.Fprintf(f, "}\n")
} }

View File

@@ -37,7 +37,7 @@ func Of[E, A any](m M.Monoid[E]) func(A) Const[E, A] {
return F.Constant1[A](Make[E, A](m.Empty())) return F.Constant1[A](Make[E, A](m.Empty()))
} }
func MonadMap[E, A, B any](fa Const[E, A], f func(A) B) Const[E, B] { func MonadMap[E, A, B any](fa Const[E, A], _ func(A) B) Const[E, B] {
return Make[E, B](fa.value) return Make[E, B](fa.value)
} }

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

@@ -47,5 +47,5 @@ func ExampleReadFile() {
fmt.Println(result()) fmt.Println(result())
// Output: // Output:
// Right[<nil>, string](Carsten) // Right[string](Carsten)
} }

View File

@@ -26,7 +26,7 @@ import (
func onWriteAll[W io.Writer](data []byte) func(w W) RIOE.ReaderIOEither[[]byte] { func onWriteAll[W io.Writer](data []byte) func(w W) RIOE.ReaderIOEither[[]byte] {
return func(w W) RIOE.ReaderIOEither[[]byte] { return func(w W) RIOE.ReaderIOEither[[]byte] {
return F.Pipe1( return F.Pipe1(
RIOE.TryCatch(func(ctx context.Context) func() ([]byte, error) { RIOE.TryCatch(func(_ context.Context) func() ([]byte, error) {
return func() ([]byte, error) { return func() ([]byte, error) {
_, err := w.Write(data) _, err := w.Write(data)
return data, err return data, err

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

@@ -182,10 +182,7 @@ func withCancelCauseFunc[
ma, ma,
IOE.Swap[GIOA, func() E.Either[A, error]], IOE.Swap[GIOA, func() E.Either[A, error]],
IOE.ChainFirstIOK[func() E.Either[A, error], func() any](func(err error) func() any { IOE.ChainFirstIOK[func() E.Either[A, error], func() any](func(err error) func() any {
return IO.MakeIO[func() any](func() any { return IO.FromImpure[func() any](func() { cancel(err) })
cancel(err)
return nil
})
}), }),
IOE.Swap[func() E.Either[A, error], GIOA], IOE.Swap[func() E.Either[A, error], GIOA],
) )

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

View File

@@ -26,7 +26,7 @@ import (
IOE "github.com/IBM/fp-go/ioeither" IOE "github.com/IBM/fp-go/ioeither"
IOEF "github.com/IBM/fp-go/ioeither/file" IOEF "github.com/IBM/fp-go/ioeither/file"
J "github.com/IBM/fp-go/json" J "github.com/IBM/fp-go/json"
T "github.com/IBM/fp-go/tuple" P "github.com/IBM/fp-go/pair"
) )
type ( type (
@@ -79,7 +79,7 @@ func ReadFullResponse(client Client) func(Requester) RIOE.ReaderIOEither[H.FullR
IOE.Of[error, io.ReadCloser], IOE.Of[error, io.ReadCloser],
IOEF.ReadAll[io.ReadCloser], IOEF.ReadAll[io.ReadCloser],
), ),
IOE.Map[error](F.Bind1st(T.MakeTuple2[*http.Response, []byte], resp)), IOE.Map[error](F.Bind1st(P.MakePair[*http.Response, []byte], resp)),
) )
}), }),
) )
@@ -103,16 +103,27 @@ func ReadText(client Client) func(Requester) RIOE.ReaderIOEither[string] {
} }
// ReadJson sends a request, reads the response and parses the response as JSON // ReadJson sends a request, reads the response and parses the response as JSON
//
// Deprecated: use [ReadJSON] instead
func ReadJson[A any](client Client) func(Requester) RIOE.ReaderIOEither[A] { func ReadJson[A any](client Client) func(Requester) RIOE.ReaderIOEither[A] {
return ReadJSON[A](client)
}
func readJSON(client Client) func(Requester) RIOE.ReaderIOEither[[]byte] {
return F.Flow3( return F.Flow3(
ReadFullResponse(client), ReadFullResponse(client),
RIOE.ChainFirstEitherK(F.Flow2( RIOE.ChainFirstEitherK(F.Flow2(
H.Response, H.Response,
H.ValidateJsonResponse, H.ValidateJSONResponse,
)),
RIOE.ChainEitherK(F.Flow2(
H.Body,
J.Unmarshal[A],
)), )),
RIOE.Map(H.Body),
)
}
// ReadJSON sends a request, reads the response and parses the response as JSON
func ReadJSON[A any](client Client) func(Requester) RIOE.ReaderIOEither[A] {
return F.Flow2(
readJSON(client),
RIOE.ChainEitherK(J.Unmarshal[A]),
) )
} }

View File

@@ -31,7 +31,7 @@ import (
) )
type PostItem struct { type PostItem struct {
UserId uint `json:"userId"` UserID uint `json:"userId"`
Id uint `json:"id"` Id uint `json:"id"`
Title string `json:"title"` Title string `json:"title"`
Body string `json:"body"` Body string `json:"body"`
@@ -84,7 +84,7 @@ func TestSendSingleRequest(t *testing.T) {
req1 := MakeGetRequest("https://jsonplaceholder.typicode.com/posts/1") req1 := MakeGetRequest("https://jsonplaceholder.typicode.com/posts/1")
readItem := ReadJson[PostItem](client) readItem := ReadJSON[PostItem](client)
resp1 := readItem(req1) resp1 := readItem(req1)
@@ -112,7 +112,7 @@ func TestSendSingleRequestWithHeaderUnsafe(t *testing.T) {
R.Map(setHeaderUnsafe("Content-Type", "text/html")), R.Map(setHeaderUnsafe("Content-Type", "text/html")),
) )
readItem := ReadJson[PostItem](client) readItem := ReadJSON[PostItem](client)
resp1 := F.Pipe2( resp1 := F.Pipe2(
req1, req1,
@@ -140,7 +140,7 @@ func TestSendSingleRequestWithHeaderSafe(t *testing.T) {
WithHeader("Content-Type", "text/html"). WithHeader("Content-Type", "text/html").
Build() Build()
readItem := ReadJson[PostItem](client) readItem := ReadJSON[PostItem](client)
response := F.Pipe2( response := F.Pipe2(
request, request,

View File

@@ -127,7 +127,7 @@ func MakeInjector(providers []Provider) InjectableFactory {
var resolved sync.Map var resolved sync.Map
// provide a mapping for all providers // provide a mapping for all providers
factoryById := assembleProviders(providers) factoryByID := assembleProviders(providers)
// the actual factory, we need lazy initialization // the actual factory, we need lazy initialization
var injFct InjectableFactory var injFct InjectableFactory
@@ -149,7 +149,7 @@ func MakeInjector(providers []Provider) InjectableFactory {
T.Map2(F.Flow3( T.Map2(F.Flow3(
Dependency.Id, Dependency.Id,
R.Lookup[ProviderFactory, string], R.Lookup[ProviderFactory, string],
I.Ap[O.Option[ProviderFactory]](factoryById), I.Ap[O.Option[ProviderFactory]](factoryByID),
), handleMissingProvider), ), handleMissingProvider),
T.Tupled2(O.MonadGetOrElse[ProviderFactory]), T.Tupled2(O.MonadGetOrElse[ProviderFactory]),
IG.Ap[ProviderFactory](injFct), IG.Ap[ProviderFactory](injFct),

View File

@@ -41,7 +41,7 @@ func eraseTuple[A, R any](f func(A) IOE.IOEither[error, R]) func(E.Either[error,
} }
func eraseProviderFactory0[R any](f IOE.IOEither[error, R]) func(params ...any) IOE.IOEither[error, any] { func eraseProviderFactory0[R any](f IOE.IOEither[error, R]) func(params ...any) IOE.IOEither[error, any] {
return func(params ...any) IOE.IOEither[error, any] { return func(_ ...any) IOE.IOEither[error, any] {
return F.Pipe1( return F.Pipe1(
f, f,
IOE.Map[error](F.ToAny[R]), IOE.Map[error](F.ToAny[R]),

View File

@@ -65,34 +65,38 @@ type MultiInjectionToken[T any] interface {
} }
// makeID creates a generator of unique string IDs // makeID creates a generator of unique string IDs
func makeId() IO.IO[string] { func makeID() IO.IO[string] {
var count atomic.Int64 var count atomic.Int64
return IO.MakeIO(func() string { return IO.MakeIO(func() string {
return strconv.FormatInt(count.Add(1), 16) return strconv.FormatInt(count.Add(1), 16)
}) })
} }
// genId is the common generator of unique string IDs // genID is the common generator of unique string IDs
var genId = makeId() var genID = makeID()
type token[T any] struct { type tokenBase struct {
name string name string
id string id string
flag int flag int
toType func(val any) E.Either[error, T]
providerFactory O.Option[DIE.ProviderFactory] providerFactory O.Option[DIE.ProviderFactory]
} }
type token[T any] struct {
base *tokenBase
toType func(val any) E.Either[error, T]
}
func (t *token[T]) Id() string { func (t *token[T]) Id() string {
return t.id return t.base.id
} }
func (t *token[T]) Flag() int { func (t *token[T]) Flag() int {
return t.flag return t.base.flag
} }
func (t *token[T]) String() string { func (t *token[T]) String() string {
return t.name return t.base.name
} }
func (t *token[T]) Unerase(val any) E.Either[error, T] { func (t *token[T]) Unerase(val any) E.Either[error, T] {
@@ -100,11 +104,14 @@ func (t *token[T]) Unerase(val any) E.Either[error, T] {
} }
func (t *token[T]) ProviderFactory() O.Option[DIE.ProviderFactory] { func (t *token[T]) ProviderFactory() O.Option[DIE.ProviderFactory] {
return t.providerFactory return t.base.providerFactory
}
func makeTokenBase(name string, id string, typ int, providerFactory O.Option[DIE.ProviderFactory]) *tokenBase {
return &tokenBase{name, id, typ, providerFactory}
} }
func makeToken[T any](name string, id string, typ int, unerase func(val any) E.Either[error, T], providerFactory O.Option[DIE.ProviderFactory]) Dependency[T] { func makeToken[T any](name string, id string, typ int, unerase func(val any) E.Either[error, T], providerFactory O.Option[DIE.ProviderFactory]) Dependency[T] {
return &token[T]{name, id, typ, unerase, providerFactory} return &token[T]{makeTokenBase(name, id, typ, providerFactory), unerase}
} }
type injectionToken[T any] struct { type injectionToken[T any] struct {
@@ -136,7 +143,7 @@ func (i *injectionToken[T]) IOOption() Dependency[IOO.IOOption[T]] {
} }
func (i *injectionToken[T]) ProviderFactory() O.Option[DIE.ProviderFactory] { func (i *injectionToken[T]) ProviderFactory() O.Option[DIE.ProviderFactory] {
return i.providerFactory return i.base.providerFactory
} }
func (m *multiInjectionToken[T]) Container() InjectionToken[[]T] { func (m *multiInjectionToken[T]) Container() InjectionToken[[]T] {
@@ -149,10 +156,10 @@ func (m *multiInjectionToken[T]) Item() InjectionToken[T] {
// makeToken create a unique [InjectionToken] for a specific type // makeToken create a unique [InjectionToken] for a specific type
func makeInjectionToken[T any](name string, providerFactory O.Option[DIE.ProviderFactory]) InjectionToken[T] { func makeInjectionToken[T any](name string, providerFactory O.Option[DIE.ProviderFactory]) InjectionToken[T] {
id := genId() id := genID()
toIdentity := toType[T]() toIdentity := toType[T]()
return &injectionToken[T]{ return &injectionToken[T]{
token[T]{name, id, DIE.Identity, toIdentity, providerFactory}, token[T]{makeTokenBase(name, id, DIE.Identity, providerFactory), toIdentity},
makeToken[O.Option[T]](fmt.Sprintf("Option[%s]", name), id, DIE.Option, toOptionType(toIdentity), providerFactory), makeToken[O.Option[T]](fmt.Sprintf("Option[%s]", name), id, DIE.Option, toOptionType(toIdentity), providerFactory),
makeToken[IOE.IOEither[error, T]](fmt.Sprintf("IOEither[%s]", name), id, DIE.IOEither, toIOEitherType(toIdentity), providerFactory), makeToken[IOE.IOEither[error, T]](fmt.Sprintf("IOEither[%s]", name), id, DIE.IOEither, toIOEitherType(toIdentity), providerFactory),
makeToken[IOO.IOOption[T]](fmt.Sprintf("IOOption[%s]", name), id, DIE.IOOption, toIOOptionType(toIdentity), providerFactory), makeToken[IOO.IOOption[T]](fmt.Sprintf("IOOption[%s]", name), id, DIE.IOOption, toIOOptionType(toIdentity), providerFactory),
@@ -171,7 +178,7 @@ func MakeTokenWithDefault[T any](name string, providerFactory DIE.ProviderFactor
// MakeMultiToken creates a [MultiInjectionToken] // MakeMultiToken creates a [MultiInjectionToken]
func MakeMultiToken[T any](name string) MultiInjectionToken[T] { func MakeMultiToken[T any](name string) MultiInjectionToken[T] {
id := genId() id := genID()
toItem := toType[T]() toItem := toType[T]()
toContainer := toArrayType(toItem) toContainer := toArrayType(toItem)
containerName := fmt.Sprintf("Container[%s]", name) containerName := fmt.Sprintf("Container[%s]", name)
@@ -180,14 +187,14 @@ func MakeMultiToken[T any](name string) MultiInjectionToken[T] {
providerFactory := O.None[DIE.ProviderFactory]() providerFactory := O.None[DIE.ProviderFactory]()
// container // container
container := &injectionToken[[]T]{ container := &injectionToken[[]T]{
token[[]T]{containerName, id, DIE.Multi | DIE.Identity, toContainer, providerFactory}, token[[]T]{makeTokenBase(containerName, id, DIE.Multi|DIE.Identity, providerFactory), toContainer},
makeToken[O.Option[[]T]](fmt.Sprintf("Option[%s]", containerName), id, DIE.Multi|DIE.Option, toOptionType(toContainer), providerFactory), makeToken[O.Option[[]T]](fmt.Sprintf("Option[%s]", containerName), id, DIE.Multi|DIE.Option, toOptionType(toContainer), providerFactory),
makeToken[IOE.IOEither[error, []T]](fmt.Sprintf("IOEither[%s]", containerName), id, DIE.Multi|DIE.IOEither, toIOEitherType(toContainer), providerFactory), makeToken[IOE.IOEither[error, []T]](fmt.Sprintf("IOEither[%s]", containerName), id, DIE.Multi|DIE.IOEither, toIOEitherType(toContainer), providerFactory),
makeToken[IOO.IOOption[[]T]](fmt.Sprintf("IOOption[%s]", containerName), id, DIE.Multi|DIE.IOOption, toIOOptionType(toContainer), providerFactory), makeToken[IOO.IOOption[[]T]](fmt.Sprintf("IOOption[%s]", containerName), id, DIE.Multi|DIE.IOOption, toIOOptionType(toContainer), providerFactory),
} }
// item // item
item := &injectionToken[T]{ item := &injectionToken[T]{
token[T]{itemName, id, DIE.Item | DIE.Identity, toItem, providerFactory}, token[T]{makeTokenBase(itemName, id, DIE.Item|DIE.Identity, providerFactory), toItem},
makeToken[O.Option[T]](fmt.Sprintf("Option[%s]", itemName), id, DIE.Item|DIE.Option, toOptionType(toItem), providerFactory), makeToken[O.Option[T]](fmt.Sprintf("Option[%s]", itemName), id, DIE.Item|DIE.Option, toOptionType(toItem), providerFactory),
makeToken[IOE.IOEither[error, T]](fmt.Sprintf("IOEither[%s]", itemName), id, DIE.Item|DIE.IOEither, toIOEitherType(toItem), providerFactory), makeToken[IOE.IOEither[error, T]](fmt.Sprintf("IOEither[%s]", itemName), id, DIE.Item|DIE.IOEither, toIOEitherType(toItem), providerFactory),
makeToken[IOO.IOOption[T]](fmt.Sprintf("IOOption[%s]", itemName), id, DIE.Item|DIE.IOOption, toIOOptionType(toItem), providerFactory), makeToken[IOO.IOOption[T]](fmt.Sprintf("IOOption[%s]", itemName), id, DIE.Item|DIE.IOOption, toIOOptionType(toItem), providerFactory),

View File

@@ -25,6 +25,13 @@ import (
O "github.com/IBM/fp-go/option" O "github.com/IBM/fp-go/option"
) )
var (
toOptionAny = toType[O.Option[any]]()
toIOEitherAny = toType[IOE.IOEither[error, any]]()
toIOOptionAny = toType[IOO.IOOption[any]]()
toArrayAny = toType[[]any]()
)
// asDependency converts a generic type to a [DIE.Dependency] // asDependency converts a generic type to a [DIE.Dependency]
func asDependency[T DIE.Dependency](t T) DIE.Dependency { func asDependency[T DIE.Dependency](t T) DIE.Dependency {
return t return t
@@ -38,7 +45,7 @@ func toType[T any]() func(t any) E.Either[error, T] {
// toOptionType converts an any to an Option[any] and then to an Option[T] // toOptionType converts an any to an Option[any] and then to an Option[T]
func toOptionType[T any](item func(any) E.Either[error, T]) func(t any) E.Either[error, O.Option[T]] { func toOptionType[T any](item func(any) E.Either[error, T]) func(t any) E.Either[error, O.Option[T]] {
return F.Flow2( return F.Flow2(
toType[O.Option[any]](), toOptionAny,
E.Chain(O.Fold( E.Chain(O.Fold(
F.Nullary2(O.None[T], E.Of[error, O.Option[T]]), F.Nullary2(O.None[T], E.Of[error, O.Option[T]]),
F.Flow2( F.Flow2(
@@ -52,7 +59,7 @@ func toOptionType[T any](item func(any) E.Either[error, T]) func(t any) E.Either
// toIOEitherType converts an any to an IOEither[error, any] and then to an IOEither[error, T] // toIOEitherType converts an any to an IOEither[error, any] and then to an IOEither[error, T]
func toIOEitherType[T any](item func(any) E.Either[error, T]) func(t any) E.Either[error, IOE.IOEither[error, T]] { func toIOEitherType[T any](item func(any) E.Either[error, T]) func(t any) E.Either[error, IOE.IOEither[error, T]] {
return F.Flow2( return F.Flow2(
toType[IOE.IOEither[error, any]](), toIOEitherAny,
E.Map[error](IOE.ChainEitherK(item)), E.Map[error](IOE.ChainEitherK(item)),
) )
} }
@@ -60,7 +67,7 @@ func toIOEitherType[T any](item func(any) E.Either[error, T]) func(t any) E.Eith
// toIOOptionType converts an any to an IOOption[any] and then to an IOOption[T] // toIOOptionType converts an any to an IOOption[any] and then to an IOOption[T]
func toIOOptionType[T any](item func(any) E.Either[error, T]) func(t any) E.Either[error, IOO.IOOption[T]] { func toIOOptionType[T any](item func(any) E.Either[error, T]) func(t any) E.Either[error, IOO.IOOption[T]] {
return F.Flow2( return F.Flow2(
toType[IOO.IOOption[any]](), toIOOptionAny,
E.Map[error](IOO.ChainOptionK(F.Flow2( E.Map[error](IOO.ChainOptionK(F.Flow2(
item, item,
E.ToOption[error, T], E.ToOption[error, T],
@@ -71,7 +78,7 @@ func toIOOptionType[T any](item func(any) E.Either[error, T]) func(t any) E.Eith
// toArrayType converts an any to a []T // toArrayType converts an any to a []T
func toArrayType[T any](item func(any) E.Either[error, T]) func(t any) E.Either[error, []T] { func toArrayType[T any](item func(any) E.Either[error, T]) func(t any) E.Either[error, []T] {
return F.Flow2( return F.Flow2(
toType[[]any](), toArrayAny,
E.Chain(E.TraverseArray(item)), E.Chain(E.TraverseArray(item)),
) )
} }

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

@@ -20,30 +20,45 @@ import (
) )
type ( type (
// Either defines a data structure that logically holds either an E or an A. The flag discriminates the cases either struct {
Either[E, A any] struct {
isLeft bool isLeft bool
left E value any
right A
} }
// Either defines a data structure that logically holds either an E or an A. The flag discriminates the cases
Either[E, A any] either
) )
// String prints some debug info for the object // String prints some debug info for the object
func (s Either[E, A]) String() string { //
// go:noinline
func eitherString(s *either) string {
if s.isLeft { if s.isLeft {
return fmt.Sprintf("Left[%T, %T](%v)", s.left, s.right, s.left) return fmt.Sprintf("Left[%T](%v)", s.value, s.value)
} }
return fmt.Sprintf("Right[%T, %T](%v)", s.left, s.right, s.right) return fmt.Sprintf("Right[%T](%v)", s.value, s.value)
}
// Format prints some debug info for the object
//
// go:noinline
func eitherFormat(e *either, f fmt.State, c rune) {
switch c {
case 's':
fmt.Fprint(f, eitherString(e))
default:
fmt.Fprint(f, eitherString(e))
}
}
// String prints some debug info for the object
func (s Either[E, A]) String() string {
return eitherString((*either)(&s))
} }
// Format prints some debug info for the object // Format prints some debug info for the object
func (s Either[E, A]) Format(f fmt.State, c rune) { func (s Either[E, A]) Format(f fmt.State, c rune) {
switch c { eitherFormat((*either)(&s), f, c)
case 's':
fmt.Fprint(f, s.String())
default:
fmt.Fprint(f, s.String())
}
} }
// IsLeft tests if the [Either] is a left value. Rather use [Fold] if you need to access the values. Inverse is [IsRight]. // IsLeft tests if the [Either] is a left value. Rather use [Fold] if you need to access the values. Inverse is [IsRight].
@@ -58,23 +73,29 @@ func IsRight[E, A any](val Either[E, A]) bool {
// Left creates a new instance of an [Either] representing the left value. // Left creates a new instance of an [Either] representing the left value.
func Left[A, E any](value E) Either[E, A] { func Left[A, E any](value E) Either[E, A] {
return Either[E, A]{isLeft: true, left: value} return Either[E, A]{true, value}
} }
// Right creates a new instance of an [Either] representing the right value. // Right creates a new instance of an [Either] representing the right value.
func Right[E, A any](value A) Either[E, A] { func Right[E, A any](value A) Either[E, A] {
return Either[E, A]{isLeft: false, right: value} return Either[E, A]{false, value}
} }
// MonadFold extracts the values from an [Either] by invoking the [onLeft] callback or the [onRight] callback depending on the case // MonadFold extracts the values from an [Either] by invoking the [onLeft] callback or the [onRight] callback depending on the case
func MonadFold[E, A, B any](ma Either[E, A], onLeft func(e E) B, onRight func(a A) B) B { func MonadFold[E, A, B any](ma Either[E, A], onLeft func(e E) B, onRight func(a A) B) B {
if ma.isLeft { if ma.isLeft {
return onLeft(ma.left) return onLeft(ma.value.(E))
} }
return onRight(ma.right) return onRight(ma.value.(A))
} }
// Unwrap converts an [Either] into the idiomatic tuple // Unwrap converts an [Either] into the idiomatic tuple
func Unwrap[E, A any](ma Either[E, A]) (A, E) { func Unwrap[E, A any](ma Either[E, A]) (A, E) {
return ma.right, ma.left if ma.isLeft {
var a A
return a, ma.value.(E)
} else {
var e E
return ma.value.(A), e
}
} }

View File

@@ -22,6 +22,7 @@ package either
import ( import (
E "github.com/IBM/fp-go/errors" E "github.com/IBM/fp-go/errors"
F "github.com/IBM/fp-go/function" F "github.com/IBM/fp-go/function"
C "github.com/IBM/fp-go/internal/chain"
FC "github.com/IBM/fp-go/internal/functor" FC "github.com/IBM/fp-go/internal/functor"
L "github.com/IBM/fp-go/lazy" L "github.com/IBM/fp-go/lazy"
O "github.com/IBM/fp-go/option" O "github.com/IBM/fp-go/option"
@@ -64,19 +65,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] { 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] { 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, B]), Right[B, 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] { 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])) 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] { // MapLeft applies a mapping function to the error channel
return F.Bind2nd(MonadMapLeft[E, A, B], f) 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] { func MonadChain[E, A, B any](fa Either[E, A], f func(a A) Either[E, B]) Either[E, B] {
@@ -84,12 +86,15 @@ func MonadChain[E, A, B any](fa Either[E, A], f func(a A) Either[E, B]) Either[E
} }
func MonadChainFirst[E, A, B any](ma Either[E, A], f func(a A) Either[E, B]) Either[E, A] { func MonadChainFirst[E, A, B any](ma Either[E, A], f func(a A) Either[E, B]) Either[E, A] {
return MonadChain(ma, func(a A) Either[E, A] { return C.MonadChainFirst(
return MonadMap(f(a), F.Constant1[B](a)) MonadChain[E, A, A],
}) MonadMap[E, B, A],
ma,
f,
)
} }
func MonadChainTo[A, E, B any](ma Either[E, A], mb Either[E, B]) Either[E, B] { func MonadChainTo[A, E, B any](_ Either[E, A], mb Either[E, B]) Either[E, B] {
return mb return mb
} }
@@ -105,15 +110,19 @@ 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] { 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] { 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] { func ChainFirst[E, A, B any](f func(a A) Either[E, B]) func(Either[E, A]) Either[E, A] {
return F.Bind2nd(MonadChainFirst[E, A, B], f) return C.ChainFirst(
Chain[E, A, A],
Map[E, B, A],
f,
)
} }
func Flatten[E, A any](mma Either[E, Either[E, A]]) Either[E, A] { func Flatten[E, A any](mma Either[E, Either[E, A]]) Either[E, A] {
@@ -250,7 +259,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] { 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] { func MonadAlt[E, A any](fa Either[E, A], that L.Lazy[Either[E, A]]) Either[E, A] {

View File

@@ -17,6 +17,7 @@ package either
import ( import (
"errors" "errors"
"fmt"
"testing" "testing"
F "github.com/IBM/fp-go/function" F "github.com/IBM/fp-go/function"
@@ -26,12 +27,6 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
func TestDefault(t *testing.T) {
var e Either[error, string]
assert.Equal(t, Of[error](""), e)
}
func TestIsLeft(t *testing.T) { func TestIsLeft(t *testing.T) {
err := errors.New("Some error") err := errors.New("Some error")
withError := Left[string](err) withError := Left[string](err)
@@ -115,3 +110,13 @@ func TestFromOption(t *testing.T) {
assert.Equal(t, Left[int]("none"), FromOption[int](F.Constant("none"))(O.None[int]())) assert.Equal(t, Left[int]("none"), FromOption[int](F.Constant("none"))(O.None[int]()))
assert.Equal(t, Right[string](1), FromOption[int](F.Constant("none"))(O.Some(1))) assert.Equal(t, Right[string](1), FromOption[int](F.Constant("none"))(O.Some(1)))
} }
func TestStringer(t *testing.T) {
e := Of[error]("foo")
exp := "Right[string](foo)"
assert.Equal(t, exp, e.String())
var s fmt.Stringer = e
assert.Equal(t, exp, s.String())
}

View File

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

View File

@@ -48,11 +48,11 @@ func ExampleEither_creation() {
fmt.Println(rightFromPred) fmt.Println(rightFromPred)
// Output: // Output:
// Left[*errors.errorString, string](some error) // Left[*errors.errorString](some error)
// Right[<nil>, string](value) // Right[string](value)
// Left[*errors.errorString, *string](value was nil) // Left[*errors.errorString](value was nil)
// true // true
// Left[*errors.errorString, int](3 is an odd number) // Left[*errors.errorString](3 is an odd number)
// Right[<nil>, int](4) // Right[int](4)
} }

View File

@@ -53,8 +53,8 @@ func ExampleEither_extraction() {
fmt.Println(doubleFromRightBis) fmt.Println(doubleFromRightBis)
// Output: // Output:
// Left[*errors.errorString, int](Division by Zero!) // Left[*errors.errorString](Division by Zero!)
// Right[<nil>, int](10) // Right[int](10)
// 0 // 0
// 10 // 10
// 0 // 0

43
either/monad.go Normal file
View File

@@ -0,0 +1,43 @@
// Copyright (c) 2024 IBM Corp.
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package either
import (
"github.com/IBM/fp-go/internal/monad"
)
type eitherMonad[E, A, B any] struct{}
func (o *eitherMonad[E, A, B]) Of(a A) Either[E, A] {
return Of[E, A](a)
}
func (o *eitherMonad[E, A, B]) Map(f func(A) B) func(Either[E, A]) Either[E, B] {
return Map[E, A, B](f)
}
func (o *eitherMonad[E, A, B]) Chain(f func(A) Either[E, B]) func(Either[E, A]) Either[E, B] {
return Chain[E, A, B](f)
}
func (o *eitherMonad[E, A, B]) Ap(fa Either[E, A]) func(Either[E, func(A) B]) Either[E, B] {
return Ap[B, E, A](fa)
}
// Monad implements the monadic operations for [Either]
func Monad[E, A, B any]() monad.Monad[A, B, Either[E, A], Either[E, B], Either[E, func(A) B]] {
return &eitherMonad[E, A, B]{}
}

View File

@@ -19,38 +19,17 @@ import (
F "github.com/IBM/fp-go/function" F "github.com/IBM/fp-go/function"
) )
/*
*
We need to pass the members of the applicative explicitly, because golang does neither support higher kinded types nor template methods on structs or interfaces
HKTRB = HKT<Either[B]>
HKTA = HKT<A>
HKTB = HKT<B>
*/
func traverse[E, A, B, HKTB, HKTRB any](
mof func(Either[E, B]) HKTRB,
mmap func(func(B) Either[E, B]) func(HKTB) HKTRB,
) func(Either[E, A], func(A) HKTB) HKTRB {
left := F.Flow2(Left[B, E], mof)
right := mmap(Right[E, B])
return func(ta Either[E, A], f func(A) HKTB) HKTRB {
return MonadFold(ta,
left,
F.Flow2(f, right),
)
}
}
// Traverse converts an [Either] of some higher kinded type into the higher kinded type of an [Either] // Traverse converts an [Either] of some higher kinded type into the higher kinded type of an [Either]
func Traverse[A, E, B, HKTB, HKTRB any]( func Traverse[A, E, B, HKTB, HKTRB any](
mof func(Either[E, B]) HKTRB, mof func(Either[E, B]) HKTRB,
mmap func(func(B) Either[E, B]) func(HKTB) HKTRB, mmap func(func(B) Either[E, B]) func(HKTB) HKTRB,
) func(func(A) HKTB) func(Either[E, A]) HKTRB { ) func(func(A) HKTB) func(Either[E, A]) HKTRB {
delegate := traverse[E, A, B](mof, mmap)
left := F.Flow2(Left[B, E], mof)
right := mmap(Right[E, B])
return func(f func(A) HKTB) func(Either[E, A]) HKTRB { return func(f func(A) HKTB) func(Either[E, A]) HKTRB {
return F.Bind2nd(delegate, f) return Fold(left, F.Flow2(f, right))
} }
} }

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 { func Identity[ENDO ~func(A) A, A any]() ENDO {
return Of[ENDO](F.Identity[A]) 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) 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] // Identity returns the identity [Endomorphism]
func Identity[A any]() Endomorphism[A] { func Identity[A any]() Endomorphism[A] {
return G.Identity[Endomorphism[A]]() return G.Identity[Endomorphism[A]]()

View File

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

View File

@@ -16,18 +16,18 @@
package exec package exec
import ( import (
T "github.com/IBM/fp-go/tuple" P "github.com/IBM/fp-go/pair"
) )
type ( type (
// CommandOutput represents the output of executing a command. The first field in the [Tuple2] is // CommandOutput represents the output of executing a command. The first field in the [Tuple2] is
// stdout, the second one is stderr. Use [StdOut] and [StdErr] to access these fields // stdout, the second one is stderr. Use [StdOut] and [StdErr] to access these fields
CommandOutput = T.Tuple2[[]byte, []byte] CommandOutput = P.Pair[[]byte, []byte]
) )
var ( var (
// StdOut returns the field of a [CommandOutput] representing `stdout` // StdOut returns the field of a [CommandOutput] representing `stdout`
StdOut = T.First[[]byte, []byte] StdOut = P.Head[[]byte, []byte]
// StdErr returns the field of a [CommandOutput] representing `stderr` // StdErr returns the field of a [CommandOutput] representing `stderr`
StdErr = T.Second[[]byte, []byte] StdErr = P.Tail[[]byte, []byte]
) )

View File

@@ -1,6 +1,6 @@
// Code generated by go generate; DO NOT EDIT. // Code generated by go generate; DO NOT EDIT.
// This file was generated by robots at // 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 package function

View File

@@ -25,13 +25,13 @@ func Memoize[K comparable, T any](f func(K) T) func(K) T {
} }
// ContramapMemoize converts a unary function into a unary function that caches the value depending on the parameter // ContramapMemoize converts a unary function into a unary function that caches the value depending on the parameter
func ContramapMemoize[A any, K comparable, T any](kf func(A) K) func(func(A) T) func(A) T { func ContramapMemoize[T, A any, K comparable](kf func(A) K) func(func(A) T) func(A) T {
return G.ContramapMemoize[func(A) T](kf) return G.ContramapMemoize[func(A) T](kf)
} }
// CacheCallback converts a unary function into a unary function that caches the value depending on the parameter // CacheCallback converts a unary function into a unary function that caches the value depending on the parameter
func CacheCallback[ func CacheCallback[
A any, K comparable, T any](kf func(A) K, getOrCreate func(K, func() func() T) func() T) func(func(A) T) func(A) T { T, A any, K comparable](kf func(A) K, getOrCreate func(K, func() func() T) func() T) func(func(A) T) func(A) T {
return G.CacheCallback[func(func(A) T) func(A) T](kf, getOrCreate) return G.CacheCallback[func(func(A) T) func(A) T](kf, getOrCreate)
} }

View File

@@ -1,6 +1,6 @@
// Code generated by go generate; DO NOT EDIT. // Code generated by go generate; DO NOT EDIT.
// This file was generated by robots at // 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 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 // 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 // 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 { func Pipe1[F1 ~func(T0) T1, T0, T1 any](t0 T0, f1 F1) T1 {
t1 := f1(t0) return f1(t0)
return t1
} }
// 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 // 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 // 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 // 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 { func Pipe2[F1 ~func(T0) T1, F2 ~func(T1) T2, T0, T1, T2 any](t0 T0, f1 F1, f2 F2) T2 {
t1 := f1(t0) return f2(f1(t0))
t2 := f2(t1)
return t2
} }
// 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 // 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 // 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 // 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 { 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) return f3(f2(f1(t0)))
t2 := f2(t1)
t3 := f3(t2)
return t3
} }
// 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 // 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 // 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 // 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 { 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) return f4(f3(f2(f1(t0))))
t2 := f2(t1)
t3 := f3(t2)
t4 := f4(t3)
return t4
} }
// 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 // 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 // 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 // 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 { 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) return f5(f4(f3(f2(f1(t0)))))
t2 := f2(t1)
t3 := f3(t2)
t4 := f4(t3)
t5 := f5(t4)
return t5
} }
// 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 // 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 // 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 // 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 { 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) return f6(f5(f4(f3(f2(f1(t0))))))
t2 := f2(t1)
t3 := f3(t2)
t4 := f4(t3)
t5 := f5(t4)
t6 := f6(t5)
return t6
} }
// 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 // 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 // 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 // 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 { 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) return f7(f6(f5(f4(f3(f2(f1(t0)))))))
t2 := f2(t1)
t3 := f3(t2)
t4 := f4(t3)
t5 := f5(t4)
t6 := f6(t5)
t7 := f7(t6)
return t7
} }
// 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 // 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 // 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 // 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 { 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) return f8(f7(f6(f5(f4(f3(f2(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
} }
// 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 // 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 // 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 // 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 { 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) return f9(f8(f7(f6(f5(f4(f3(f2(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
} }
// 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 // 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 // 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 // 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 { 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) return f10(f9(f8(f7(f6(f5(f4(f3(f2(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
} }
// 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 // 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 // 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 // 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 { 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) return f11(f10(f9(f8(f7(f6(f5(f4(f3(f2(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
} }
// 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 // 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 // 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 // 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 { 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) return f12(f11(f10(f9(f8(f7(f6(f5(f4(f3(f2(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
} }
// 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 // 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 // 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 // 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 { 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) return f13(f12(f11(f10(f9(f8(f7(f6(f5(f4(f3(f2(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
} }
// 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 // 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 // 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 // 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 { 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) return f14(f13(f12(f11(f10(f9(f8(f7(f6(f5(f4(f3(f2(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
} }
// 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 // 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 // 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 // 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 { 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) return f15(f14(f13(f12(f11(f10(f9(f8(f7(f6(f5(f4(f3(f2(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
} }
// 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 // 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 // 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 // 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 { 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) return f16(f15(f14(f13(f12(f11(f10(f9(f8(f7(f6(f5(f4(f3(f2(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
} }
// 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 // 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 // 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 // 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 { 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) return f17(f16(f15(f14(f13(f12(f11(f10(f9(f8(f7(f6(f5(f4(f3(f2(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
} }
// 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 // 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 // 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 // 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 { 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) return f18(f17(f16(f15(f14(f13(f12(f11(f10(f9(f8(f7(f6(f5(f4(f3(f2(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
} }
// 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 // 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 // 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 // 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 { 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) return f19(f18(f17(f16(f15(f14(f13(f12(f11(f10(f9(f8(f7(f6(f5(f4(f3(f2(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
} }
// 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 // 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 // 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 // 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 { 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) return f20(f19(f18(f17(f16(f15(f14(f13(f12(f11(f10(f9(f8(f7(f6(f5(f4(f3(f2(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
} }
// 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 // 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

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

@@ -0,0 +1,387 @@
// 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"
"crypto/sha256"
"fmt"
"net/http"
"net/url"
A "github.com/IBM/fp-go/array"
B "github.com/IBM/fp-go/bytes"
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"
R "github.com/IBM/fp-go/record"
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
//
// Deprecated: use [URL] instead
Url = L.MakeLensRef((*Builder).GetURL, (*Builder).SetURL)
// 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 the URL
//
// Deprecated: use [WithURL] instead
WithUrl = URL.Set
// WithURL creates a [Endomorphism] for the URL
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)),
)
// bodyAsBytes returns a []byte with a fallback to the empty array
bodyAsBytes = O.Fold(B.Empty, E.Fold(F.Ignore1of1[error](B.Empty), F.Identity[[]byte]))
)
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
//
// Deprecated: use [GetTargetURL] instead
func (builder *Builder) GetTargetUrl() E.Either[error, string] {
return builder.GetTargetURL()
}
// 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),
)),
)
}
// Deprecated: use [GetURL] instead
func (builder *Builder) GetUrl() string {
return builder.url
}
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
}
// Deprecated: use [SetURL] instead
func (builder *Builder) SetUrl(url string) *Builder {
builder.url = url
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)
}
// GetHash returns a hash value for the builder that can be used as a cache key
func (builder *Builder) GetHash() string {
return MakeHash(builder)
}
// 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
//
// Deprecated: use [WithJSON] instead
func WithJson[T any](data T) Endomorphism {
return WithJSON[T](data)
}
// 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)
}
func hashWriteValue(buf *bytes.Buffer, value string) *bytes.Buffer {
buf.WriteString(value)
return buf
}
func hashWriteQuery(name string, buf *bytes.Buffer, values []string) *bytes.Buffer {
buf.WriteString(name)
return A.Reduce(hashWriteValue, buf)(values)
}
func makeBytes(b *Builder) []byte {
var buf bytes.Buffer
buf.WriteString(b.GetMethod())
buf.WriteString(b.GetURL())
b.GetHeaders().Write(&buf) // #nosec: G104
R.ReduceOrdWithIndex[[]string, *bytes.Buffer](S.Ord)(hashWriteQuery, &buf)(b.GetQuery())
buf.Write(bodyAsBytes(b.GetBody()))
return buf.Bytes()
}
// MakeHash converts a [Builder] into a hash string, convenient to use as a cache key
func MakeHash(b *Builder) string {
return fmt.Sprintf("%x", sha256.Sum256(makeBytes(b)))
}

View File

@@ -0,0 +1,93 @@
// 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 (
"fmt"
"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))
}
func TestHash(t *testing.T) {
b1 := F.Pipe4(
Default,
WithContentType(C.JSON),
WithHeader(H.Accept)(C.JSON),
WithURL("http://www.example.com"),
WithJSON(map[string]string{"a": "b"}),
)
b2 := F.Pipe4(
Default,
WithURL("http://www.example.com"),
WithHeader(H.Accept)(C.JSON),
WithContentType(C.JSON),
WithJSON(map[string]string{"a": "b"}),
)
assert.Equal(t, MakeHash(b1), MakeHash(b2))
assert.NotEqual(t, MakeHash(Default), MakeHash(b2))
fmt.Println(MakeHash(b1))
}

View File

@@ -17,6 +17,7 @@ package content
const ( const (
TextPlain = "text/plain" TextPlain = "text/plain"
Json = "application/json" JSON = "application/json"
Json = JSON // Deprecated: use [JSON] instead
FormEncoded = "application/x-www-form-urlencoded" FormEncoded = "application/x-www-form-urlencoded"
) )

View File

@@ -39,7 +39,7 @@ var (
noField = O.None[string]() 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]() Monoid = ENDO.Monoid[url.Values]()
// ValuesMonoid is a [M.Monoid] to concatenate [url.Values] maps // ValuesMonoid is a [M.Monoid] to concatenate [url.Values] maps

View File

@@ -18,15 +18,15 @@ package http
import ( import (
H "net/http" H "net/http"
T "github.com/IBM/fp-go/tuple" P "github.com/IBM/fp-go/pair"
) )
type ( type (
// FullResponse represents a full http response, including headers and body // FullResponse represents a full http response, including headers and body
FullResponse = T.Tuple2[*H.Response, []byte] FullResponse = P.Pair[*H.Response, []byte]
) )
var ( var (
Response = T.First[*H.Response, []byte] Response = P.Head[*H.Response, []byte]
Body = T.Second[*H.Response, []byte] Body = P.Tail[*H.Response, []byte]
) )

View File

@@ -20,6 +20,7 @@ import (
"io" "io"
"mime" "mime"
H "net/http" H "net/http"
"net/url"
"regexp" "regexp"
A "github.com/IBM/fp-go/array" A "github.com/IBM/fp-go/array"
@@ -27,39 +28,48 @@ import (
"github.com/IBM/fp-go/errors" "github.com/IBM/fp-go/errors"
F "github.com/IBM/fp-go/function" F "github.com/IBM/fp-go/function"
O "github.com/IBM/fp-go/option" O "github.com/IBM/fp-go/option"
P "github.com/IBM/fp-go/pair"
R "github.com/IBM/fp-go/record/generic" R "github.com/IBM/fp-go/record/generic"
T "github.com/IBM/fp-go/tuple"
) )
type ( type (
ParsedMediaType = T.Tuple2[string, map[string]string] ParsedMediaType = P.Pair[string, map[string]string]
HttpError struct {
statusCode int
headers H.Header
body []byte
url *url.URL
}
) )
var ( var (
// mime type to check if a media type matches // mime type to check if a media type matches
reJsonMimeType = regexp.MustCompile(`application/(?:\w+\+)?json`) isJSONMimeType = regexp.MustCompile(`application/(?:\w+\+)?json`).MatchString
// ValidateResponse validates an HTTP response and returns an [E.Either] if the response is not a success // ValidateResponse validates an HTTP response and returns an [E.Either] if the response is not a success
ValidateResponse = E.FromPredicate(isValidStatus, StatusCodeError) ValidateResponse = E.FromPredicate(isValidStatus, StatusCodeError)
// alidateJsonContentTypeString parses a content type a validates that it is valid JSON // alidateJsonContentTypeString parses a content type a validates that it is valid JSON
validateJsonContentTypeString = F.Flow2( validateJSONContentTypeString = F.Flow2(
ParseMediaType, ParseMediaType,
E.ChainFirst(F.Flow2( E.ChainFirst(F.Flow2(
T.First[string, map[string]string], P.Head[string, map[string]string],
E.FromPredicate(reJsonMimeType.MatchString, func(mimeType string) error { E.FromPredicate(isJSONMimeType, errors.OnSome[string]("mimetype [%s] is not a valid JSON content type")),
return fmt.Errorf("mimetype [%s] is not a valid JSON content type", mimeType)
}),
)), )),
) )
// ValidateJsonResponse checks if an HTTP response is a valid JSON response // ValidateJSONResponse checks if an HTTP response is a valid JSON response
ValidateJsonResponse = F.Flow2( ValidateJSONResponse = F.Flow2(
E.Of[error, *H.Response], E.Of[error, *H.Response],
E.ChainFirst(F.Flow5( E.ChainFirst(F.Flow5(
GetHeader, GetHeader,
R.Lookup[H.Header](HeaderContentType), R.Lookup[H.Header](HeaderContentType),
O.Chain(A.First[string]), O.Chain(A.First[string]),
E.FromOption[string](errors.OnNone("unable to access the [%s] header", HeaderContentType)), E.FromOption[string](errors.OnNone("unable to access the [%s] header", HeaderContentType)),
E.ChainFirst(validateJsonContentTypeString), E.ChainFirst(validateJSONContentTypeString),
))) )))
// ValidateJsonResponse checks if an HTTP response is a valid JSON response
//
// Deprecated: use [ValidateJSONResponse] instead
ValidateJsonResponse = ValidateJSONResponse
) )
const ( const (
@@ -69,7 +79,32 @@ const (
// ParseMediaType parses a media type into a tuple // ParseMediaType parses a media type into a tuple
func ParseMediaType(mediaType string) E.Either[error, ParsedMediaType] { func ParseMediaType(mediaType string) E.Either[error, ParsedMediaType] {
m, p, err := mime.ParseMediaType(mediaType) m, p, err := mime.ParseMediaType(mediaType)
return E.TryCatchError(T.MakeTuple2(m, p), err) return E.TryCatchError(P.MakePair(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 { func GetHeader(resp *H.Response) H.Header {
@@ -84,6 +119,13 @@ func isValidStatus(resp *H.Response) bool {
return resp.StatusCode >= H.StatusOK && resp.StatusCode < H.StatusMultipleChoices 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 { 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}
} }

View File

@@ -39,7 +39,7 @@ func Error[A any](t *testing.T) func(E.Either[error, A]) bool {
func TestValidateJsonContentTypeString(t *testing.T) { func TestValidateJsonContentTypeString(t *testing.T) {
res := F.Pipe1( res := F.Pipe1(
validateJsonContentTypeString(C.Json), validateJSONContentTypeString(C.JSON),
NoError[ParsedMediaType](t), NoError[ParsedMediaType](t),
) )
@@ -49,7 +49,7 @@ func TestValidateJsonContentTypeString(t *testing.T) {
func TestValidateInvalidJsonContentTypeString(t *testing.T) { func TestValidateInvalidJsonContentTypeString(t *testing.T) {
res := F.Pipe1( res := F.Pipe1(
validateJsonContentTypeString("application/xml"), validateJSONContentTypeString("application/xml"),
Error[ParsedMediaType](t), Error[ParsedMediaType](t),
) )

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 { 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 { 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 { 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

@@ -36,7 +36,7 @@ func Map[A, B any](f func(A) B) func(A) B {
return G.Map(f) return G.Map(f)
} }
func MonadMapTo[A, B any](fa A, b B) B { func MonadMapTo[A, B any](_ A, b B) B {
return b return b
} }

43
identity/monad.go Normal file
View File

@@ -0,0 +1,43 @@
// Copyright (c) 2024 IBM Corp.
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package identity
import (
"github.com/IBM/fp-go/internal/monad"
)
type identityMonad[A, B any] struct{}
func (o *identityMonad[A, B]) Of(a A) A {
return Of[A](a)
}
func (o *identityMonad[A, B]) Map(f func(A) B) func(A) B {
return Map[A, B](f)
}
func (o *identityMonad[A, B]) Chain(f func(A) B) func(A) B {
return Chain[A, B](f)
}
func (o *identityMonad[A, B]) Ap(fa A) func(func(A) B) B {
return Ap[B, A](fa)
}
// Monad implements the monadic operations for [Option]
func Monad[A, B any]() monad.Monad[A, B, A, B, func(A) B] {
return &identityMonad[A, B]{}
}

View File

@@ -20,13 +20,19 @@ import (
E "github.com/IBM/fp-go/eq" E "github.com/IBM/fp-go/eq"
F "github.com/IBM/fp-go/function" F "github.com/IBM/fp-go/function"
"github.com/IBM/fp-go/internal/applicative"
"github.com/IBM/fp-go/internal/apply"
L "github.com/IBM/fp-go/internal/apply/testing" L "github.com/IBM/fp-go/internal/apply/testing"
"github.com/IBM/fp-go/internal/functor"
"github.com/IBM/fp-go/internal/pointed"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
// Applicative identity law // Applicative identity law
// //
// A.ap(A.of(a => a), fa) <-> fa // A.ap(A.of(a => a), fa) <-> fa
//
// Deprecated: use [ApplicativeAssertIdentity]
func AssertIdentity[HKTA, HKTAA, A any](t *testing.T, func AssertIdentity[HKTA, HKTAA, A any](t *testing.T,
eq E.Eq[HKTA], eq E.Eq[HKTA],
@@ -34,6 +40,9 @@ func AssertIdentity[HKTA, HKTAA, A any](t *testing.T,
fap func(HKTAA, HKTA) HKTA, fap func(HKTAA, HKTA) HKTA,
) func(fa HKTA) bool { ) func(fa HKTA) bool {
// mark as test helper
t.Helper()
return func(fa HKTA) bool { return func(fa HKTA) bool {
left := fap(fof(F.Identity[A]), fa) left := fap(fof(F.Identity[A]), fa)
@@ -43,9 +52,33 @@ func AssertIdentity[HKTA, HKTAA, A any](t *testing.T,
} }
} }
// Applicative identity law
//
// A.ap(A.of(a => a), fa) <-> fa
func ApplicativeAssertIdentity[HKTA, HKTFAA, A any](t *testing.T,
eq E.Eq[HKTA],
ap applicative.Applicative[A, A, HKTA, HKTA, HKTFAA],
paa pointed.Pointed[func(A) A, HKTFAA],
) func(fa HKTA) bool {
// mark as test helper
t.Helper()
return func(fa HKTA) bool {
left := ap.Ap(fa)(paa.Of(F.Identity[A]))
right := fa
return assert.True(t, eq.Equals(left, right), "Applicative identity")
}
}
// Applicative homomorphism law // Applicative homomorphism law
// //
// A.ap(A.of(ab), A.of(a)) <-> A.of(ab(a)) // A.ap(A.of(ab), A.of(a)) <-> A.of(ab(a))
//
// Deprecated: use [ApplicativeAssertHomomorphism]
func AssertHomomorphism[HKTA, HKTB, HKTAB, A, B any](t *testing.T, func AssertHomomorphism[HKTA, HKTB, HKTAB, A, B any](t *testing.T,
eq E.Eq[HKTB], eq E.Eq[HKTB],
@@ -57,6 +90,9 @@ func AssertHomomorphism[HKTA, HKTB, HKTAB, A, B any](t *testing.T,
ab func(A) B, ab func(A) B,
) func(a A) bool { ) func(a A) bool {
// mark as test helper
t.Helper()
return func(a A) bool { return func(a A) bool {
left := fap(fofab(ab), fofa(a)) left := fap(fofab(ab), fofa(a))
@@ -66,14 +102,39 @@ func AssertHomomorphism[HKTA, HKTB, HKTAB, A, B any](t *testing.T,
} }
} }
// Applicative homomorphism law
//
// A.ap(A.of(ab), A.of(a)) <-> A.of(ab(a))
func ApplicativeAssertHomomorphism[HKTA, HKTB, HKTFAB, A, B any](t *testing.T,
eq E.Eq[HKTB],
apab applicative.Applicative[A, B, HKTA, HKTB, HKTFAB],
pb pointed.Pointed[B, HKTB],
pfab pointed.Pointed[func(A) B, HKTFAB],
ab func(A) B,
) func(a A) bool {
// mark as test helper
t.Helper()
return func(a A) bool {
left := apab.Ap(apab.Of(a))(pfab.Of(ab))
right := pb.Of(ab(a))
return assert.True(t, eq.Equals(left, right), "Applicative homomorphism")
}
}
// Applicative interchange law // Applicative interchange law
// //
// A.ap(fab, A.of(a)) <-> A.ap(A.of(ab => ab(a)), fab) // A.ap(fab, A.of(a)) <-> A.ap(A.of(ab => ab(a)), fab)
//
// Deprecated: use [ApplicativeAssertInterchange]
func AssertInterchange[HKTA, HKTB, HKTAB, HKTABB, A, B any](t *testing.T, func AssertInterchange[HKTA, HKTB, HKTAB, HKTABB, A, B any](t *testing.T,
eq E.Eq[HKTB], eq E.Eq[HKTB],
fofa func(A) HKTA, fofa func(A) HKTA,
fofb func(B) HKTB,
fofab func(func(A) B) HKTAB, fofab func(func(A) B) HKTAB,
fofabb func(func(func(A) B) B) HKTABB, fofabb func(func(func(A) B) B) HKTABB,
@@ -82,6 +143,9 @@ func AssertInterchange[HKTA, HKTB, HKTAB, HKTABB, A, B any](t *testing.T,
ab func(A) B, ab func(A) B,
) func(a A) bool { ) func(a A) bool {
// mark as test helper
t.Helper()
return func(a A) bool { return func(a A) bool {
fab := fofab(ab) fab := fofab(ab)
@@ -95,7 +159,38 @@ func AssertInterchange[HKTA, HKTB, HKTAB, HKTABB, A, B any](t *testing.T,
} }
} }
// Applicative interchange law
//
// A.ap(fab, A.of(a)) <-> A.ap(A.of(ab => ab(a)), fab)
func ApplicativeAssertInterchange[HKTA, HKTB, HKTFAB, HKTABB, A, B any](t *testing.T,
eq E.Eq[HKTB],
apab applicative.Applicative[A, B, HKTA, HKTB, HKTFAB],
apabb applicative.Applicative[func(A) B, B, HKTFAB, HKTB, HKTABB],
pabb pointed.Pointed[func(func(A) B) B, HKTABB],
ab func(A) B,
) func(a A) bool {
// mark as test helper
t.Helper()
return func(a A) bool {
fab := apabb.Of(ab)
left := apab.Ap(apab.Of(a))(fab)
right := apabb.Ap(fab)(pabb.Of(func(ab func(A) B) B {
return ab(a)
}))
return assert.True(t, eq.Equals(left, right), "Applicative homomorphism")
}
}
// AssertLaws asserts the apply laws `identity`, `composition`, `associative composition`, 'applicative identity', 'homomorphism', 'interchange' // AssertLaws asserts the apply laws `identity`, `composition`, `associative composition`, 'applicative identity', 'homomorphism', 'interchange'
//
// Deprecated: use [ApplicativeAssertLaws] instead
func AssertLaws[HKTA, HKTB, HKTC, HKTAA, HKTAB, HKTBC, HKTAC, HKTABB, HKTABAC, A, B, C any](t *testing.T, func AssertLaws[HKTA, HKTB, HKTC, HKTAA, HKTAB, HKTBC, HKTAC, HKTABB, HKTABAC, A, B, C any](t *testing.T,
eqa E.Eq[HKTA], eqa E.Eq[HKTA],
eqb E.Eq[HKTB], eqb E.Eq[HKTB],
@@ -127,15 +222,62 @@ func AssertLaws[HKTA, HKTB, HKTC, HKTAA, HKTAB, HKTBC, HKTAC, HKTABB, HKTABAC, A
ab func(A) B, ab func(A) B,
bc func(B) C, bc func(B) C,
) func(a A) bool { ) func(a A) bool {
// mark as test helper
t.Helper()
// apply laws // apply laws
apply := L.AssertLaws(t, eqa, eqc, fofab, fofbc, faa, fab, fac, fbc, fmap, fapab, fapbc, fapac, fapabac, ab, bc) apply := L.AssertLaws(t, eqa, eqc, fofab, fofbc, faa, fab, fac, fbc, fmap, fapab, fapbc, fapac, fapabac, ab, bc)
// applicative laws // applicative laws
identity := AssertIdentity(t, eqa, fofaa, fapaa) identity := AssertIdentity(t, eqa, fofaa, fapaa)
homomorphism := AssertHomomorphism(t, eqb, fofa, fofb, fofab, fapab, ab) homomorphism := AssertHomomorphism(t, eqb, fofa, fofb, fofab, fapab, ab)
interchange := AssertInterchange(t, eqb, fofa, fofb, fofab, fofabb, fapab, fapabb, ab) interchange := AssertInterchange(t, eqb, fofa, fofab, fofabb, fapab, fapabb, ab)
return func(a A) bool { return func(a A) bool {
fa := fofa(a) fa := fofa(a)
return apply(fa) && identity(fa) && homomorphism(a) && interchange(a) return apply(fa) && identity(fa) && homomorphism(a) && interchange(a)
} }
} }
// ApplicativeAssertLaws asserts the apply laws `identity`, `composition`, `associative composition`, 'applicative identity', 'homomorphism', 'interchange'
func ApplicativeAssertLaws[HKTA, HKTB, HKTC, HKTAA, HKTAB, HKTBC, HKTAC, HKTABB, HKTABAC, A, B, C any](t *testing.T,
eqa E.Eq[HKTA],
eqb E.Eq[HKTB],
eqc E.Eq[HKTC],
fofb pointed.Pointed[B, HKTB],
fofaa pointed.Pointed[func(A) A, HKTAA],
fofbc pointed.Pointed[func(B) C, HKTBC],
fofabb pointed.Pointed[func(func(A) B) B, HKTABB],
faa functor.Functor[A, A, HKTA, HKTA],
fmap functor.Functor[func(B) C, func(func(A) B) func(A) C, HKTBC, HKTABAC],
fapaa applicative.Applicative[A, A, HKTA, HKTA, HKTAA],
fapab applicative.Applicative[A, B, HKTA, HKTB, HKTAB],
fapbc apply.Apply[B, C, HKTB, HKTC, HKTBC],
fapac apply.Apply[A, C, HKTA, HKTC, HKTAC],
fapabb applicative.Applicative[func(A) B, B, HKTAB, HKTB, HKTABB],
fapabac applicative.Applicative[func(A) B, func(A) C, HKTAB, HKTAC, HKTABAC],
ab func(A) B,
bc func(B) C,
) func(a A) bool {
// mark as test helper
t.Helper()
// apply laws
apply := L.ApplyAssertLaws(t, eqa, eqc, applicative.ToPointed(fapabac), fofbc, faa, fmap, applicative.ToApply(fapab), fapbc, fapac, applicative.ToApply(fapabac), ab, bc)
// applicative laws
identity := ApplicativeAssertIdentity(t, eqa, fapaa, fofaa)
homomorphism := ApplicativeAssertHomomorphism(t, eqb, fapab, fofb, applicative.ToPointed(fapabb), ab)
interchange := ApplicativeAssertInterchange(t, eqb, fapab, fapabb, fofabb, ab)
return func(a A) bool {
fa := fapaa.Of(a)
return apply(fa) && identity(fa) && homomorphism(a) && interchange(a)
}
}

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 applicative
import (
"github.com/IBM/fp-go/internal/apply"
"github.com/IBM/fp-go/internal/functor"
"github.com/IBM/fp-go/internal/pointed"
)
type Applicative[A, B, HKTA, HKTB, HKTFAB any] interface {
apply.Apply[A, B, HKTA, HKTB, HKTFAB]
pointed.Pointed[A, HKTA]
}
// ToFunctor converts from [Applicative] to [functor.Functor]
func ToFunctor[A, B, HKTA, HKTB, HKTFAB any](ap Applicative[A, B, HKTA, HKTB, HKTFAB]) functor.Functor[A, B, HKTA, HKTB] {
return ap
}
// ToApply converts from [Applicative] to [apply.Apply]
func ToApply[A, B, HKTA, HKTB, HKTFAB any](ap Applicative[A, B, HKTA, HKTB, HKTFAB]) apply.Apply[A, B, HKTA, HKTB, HKTFAB] {
return ap
}
// ToPointed converts from [Applicative] to [pointed.Pointed]
func ToPointed[A, B, HKTA, HKTB, HKTFAB any](ap Applicative[A, B, HKTA, HKTB, HKTFAB]) pointed.Pointed[A, HKTA] {
return ap
}

View File

@@ -19,13 +19,6 @@ import (
F "github.com/IBM/fp-go/function" 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]( func MonadAp[HKTGA, HKTGB, HKTGAB, HKTFGAB, HKTFGGAB, HKTFGA, HKTFGB any](
fap func(HKTFGGAB, HKTFGA) HKTFGB, fap func(HKTFGGAB, HKTFGA) HKTFGB,
fmap func(HKTFGAB, func(HKTGAB) func(HKTGA) HKTGB) HKTFGGAB, 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) 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>( // export function ap<F, G>(
// F: Apply<F>, // F: Apply<F>,
// G: Apply<G> // G: Apply<G>
@@ -107,3 +123,25 @@ func ApSecond[HKTGA, HKTGB, HKTGBB, A, B any](
return MonadApSecond(fap, fmap, first, second) return MonadApSecond(fap, fmap, first, second)
} }
} }
func MonadApS[S1, S2, B, HKTBGBS2, HKTS1, HKTS2, HKTB any](
fap func(HKTBGBS2, HKTB) HKTS2,
fmap func(HKTS1, func(S1) func(B) S2) HKTBGBS2,
fa HKTS1,
key func(B) func(S1) S2,
fb HKTB,
) HKTS2 {
return fap(fmap(fa, F.Flip(key)), fb)
}
func ApS[S1, S2, B, HKTBGBS2, HKTS1, HKTS2, HKTB any](
fap func(HKTB) func(HKTBGBS2) HKTS2,
fmap func(func(S1) func(B) S2) func(HKTS1) HKTBGBS2,
key func(B) func(S1) S2,
fb HKTB,
) func(HKTS1) HKTS2 {
return F.Flow2(
fmap(F.Flip(key)),
fap(fb),
)
}

View File

@@ -19,13 +19,18 @@ import (
"testing" "testing"
E "github.com/IBM/fp-go/eq" E "github.com/IBM/fp-go/eq"
"github.com/IBM/fp-go/internal/apply"
"github.com/IBM/fp-go/internal/functor"
FCT "github.com/IBM/fp-go/internal/functor/testing" FCT "github.com/IBM/fp-go/internal/functor/testing"
"github.com/IBM/fp-go/internal/pointed"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
// Apply associative composition law // Apply associative composition law
// //
// F.ap(F.ap(F.map(fbc, bc => ab => a => bc(ab(a))), fab), fa) <-> F.ap(fbc, F.ap(fab, fa)) // F.ap(F.ap(F.map(fbc, bc => ab => a => bc(ab(a))), fab), fa) <-> F.ap(fbc, F.ap(fab, fa))
//
// Deprecated: use [ApplyAssertAssociativeComposition] instead
func AssertAssociativeComposition[HKTA, HKTB, HKTC, HKTAB, HKTBC, HKTAC, HKTABAC, A, B, C any](t *testing.T, func AssertAssociativeComposition[HKTA, HKTB, HKTC, HKTAB, HKTBC, HKTAC, HKTABAC, A, B, C any](t *testing.T,
eq E.Eq[HKTC], eq E.Eq[HKTC],
@@ -43,6 +48,7 @@ func AssertAssociativeComposition[HKTA, HKTB, HKTC, HKTAB, HKTBC, HKTAC, HKTABAC
ab func(A) B, ab func(A) B,
bc func(B) C, bc func(B) C,
) func(fa HKTA) bool { ) func(fa HKTA) bool {
t.Helper()
return func(fa HKTA) bool { return func(fa HKTA) bool {
fab := fofab(ab) fab := fofab(ab)
@@ -62,7 +68,49 @@ func AssertAssociativeComposition[HKTA, HKTB, HKTC, HKTAB, HKTBC, HKTAC, HKTABAC
} }
} }
// Apply associative composition law
//
// F.ap(F.ap(F.map(fbc, bc => ab => a => bc(ab(a))), fab), fa) <-> F.ap(fbc, F.ap(fab, fa))
func ApplyAssertAssociativeComposition[HKTA, HKTB, HKTC, HKTAB, HKTBC, HKTAC, HKTABAC, A, B, C any](t *testing.T,
eq E.Eq[HKTC],
fofab pointed.Pointed[func(A) B, HKTAB],
fofbc pointed.Pointed[func(B) C, HKTBC],
fmap functor.Functor[func(B) C, func(func(A) B) func(A) C, HKTBC, HKTABAC],
fapab apply.Apply[A, B, HKTA, HKTB, HKTAB],
fapbc apply.Apply[B, C, HKTB, HKTC, HKTBC],
fapac apply.Apply[A, C, HKTA, HKTC, HKTAC],
fapabac apply.Apply[func(A) B, func(A) C, HKTAB, HKTAC, HKTABAC],
ab func(A) B,
bc func(B) C,
) func(fa HKTA) bool {
t.Helper()
return func(fa HKTA) bool {
fab := fofab.Of(ab)
fbc := fofbc.Of(bc)
left := fapac.Ap(fa)(fapabac.Ap(fab)(fmap.Map(func(bc func(B) C) func(func(A) B) func(A) C {
return func(ab func(A) B) func(A) C {
return func(a A) C {
return bc(ab(a))
}
}
})(fbc)))
right := fapbc.Ap(fapab.Ap(fa)(fab))(fbc)
return assert.True(t, eq.Equals(left, right), "Apply associative composition")
}
}
// AssertLaws asserts the apply laws `identity`, `composition` and `associative composition` // AssertLaws asserts the apply laws `identity`, `composition` and `associative composition`
//
// Deprecated: use [ApplyAssertLaws] instead
func AssertLaws[HKTA, HKTB, HKTC, HKTAB, HKTBC, HKTAC, HKTABAC, A, B, C any](t *testing.T, func AssertLaws[HKTA, HKTB, HKTC, HKTAB, HKTBC, HKTAC, HKTABAC, A, B, C any](t *testing.T,
eqa E.Eq[HKTA], eqa E.Eq[HKTA],
eqc E.Eq[HKTC], eqc E.Eq[HKTC],
@@ -86,6 +134,8 @@ func AssertLaws[HKTA, HKTB, HKTC, HKTAB, HKTBC, HKTAC, HKTABAC, A, B, C any](t *
ab func(A) B, ab func(A) B,
bc func(B) C, bc func(B) C,
) func(fa HKTA) bool { ) func(fa HKTA) bool {
// mark as test helper
t.Helper()
// functor laws // functor laws
functor := FCT.AssertLaws(t, eqa, eqc, faa, fab, fac, fbc, ab, bc) functor := FCT.AssertLaws(t, eqa, eqc, faa, fab, fac, fbc, ab, bc)
// associative composition laws // associative composition laws
@@ -95,3 +145,36 @@ func AssertLaws[HKTA, HKTB, HKTC, HKTAB, HKTBC, HKTAC, HKTABAC, A, B, C any](t *
return functor(fa) && composition(fa) return functor(fa) && composition(fa)
} }
} }
// ApplyAssertLaws asserts the apply laws `identity`, `composition` and `associative composition`
func ApplyAssertLaws[HKTA, HKTB, HKTC, HKTAB, HKTBC, HKTAC, HKTABAC, A, B, C any](t *testing.T,
eqa E.Eq[HKTA],
eqc E.Eq[HKTC],
fofab pointed.Pointed[func(A) B, HKTAB],
fofbc pointed.Pointed[func(B) C, HKTBC],
faa functor.Functor[A, A, HKTA, HKTA],
fmap functor.Functor[func(B) C, func(func(A) B) func(A) C, HKTBC, HKTABAC],
fapab apply.Apply[A, B, HKTA, HKTB, HKTAB],
fapbc apply.Apply[B, C, HKTB, HKTC, HKTBC],
fapac apply.Apply[A, C, HKTA, HKTC, HKTAC],
fapabac apply.Apply[func(A) B, func(A) C, HKTAB, HKTAC, HKTABAC],
ab func(A) B,
bc func(B) C,
) func(fa HKTA) bool {
// mark as test helper
t.Helper()
// functor laws
functor := FCT.FunctorAssertLaws(t, eqa, eqc, faa, apply.ToFunctor(fapab), apply.ToFunctor(fapac), apply.ToFunctor(fapbc), ab, bc)
// associative composition laws
composition := ApplyAssertAssociativeComposition(t, eqc, fofab, fofbc, fmap, fapab, fapbc, fapac, fapabac, ab, bc)
return func(fa HKTA) bool {
return functor(fa) && composition(fa)
}
}

30
internal/apply/types.go Normal file
View File

@@ -0,0 +1,30 @@
// 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 apply
import (
"github.com/IBM/fp-go/internal/functor"
)
type Apply[A, B, HKTA, HKTB, HKTFAB any] interface {
functor.Functor[A, B, HKTA, HKTB]
Ap(HKTA) func(HKTFAB) HKTB
}
// ToFunctor converts from [Apply] to [functor.Functor]
func ToFunctor[A, B, HKTA, HKTB, HKTFAB any](ap Apply[A, B, HKTA, HKTB, HKTFAB]) functor.Functor[A, B, HKTA, HKTB] {
return ap
}

View File

@@ -106,6 +106,12 @@ func MonadMap[GA ~[]A, GB ~[]B, A, B any](as GA, f func(a A) B) GB {
return bs return bs
} }
func Map[GA ~[]A, GB ~[]B, A, B any](f func(a A) B) func(GA) GB {
return func(as GA) GB {
return MonadMap[GA, GB](as, f)
}
}
func MonadMapWithIndex[GA ~[]A, GB ~[]B, A, B any](as GA, f func(idx int, a A) B) GB { func MonadMapWithIndex[GA ~[]A, GB ~[]B, A, B any](as GA, f func(idx int, a A) B) GB {
count := len(as) count := len(as)
bs := make(GB, count) bs := make(GB, count)

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] // HKTA=HKT[A]
// HKTB=HKT[B] // HKTB=HKT[B]
func ChainFirst[A, B, HKTA, HKTB any]( func ChainFirst[A, B, HKTA, HKTB any](
mchain func(HKTA, func(A) HKTA) HKTA, mchain func(func(A) HKTA) func(HKTA) HKTA,
mmap func(HKTB, func(B) A) HKTA, mmap func(func(B) A) func(HKTB) HKTA,
f func(A) HKTB) func(HKTA) HKTA { f func(A) HKTB) func(HKTA) HKTA {
return F.Bind2nd(mchain, func(a A) HKTA { return mchain(func(a A) HKTA {
return mmap(f(a), F.Constant1[B](a)) return mmap(F.Constant1[B](a))(f(a))
}) })
} }
func Chain[A, B, HKTA, HKTB any]( func Chain[A, B, HKTA, HKTB any](
mchain func(HKTA, func(A) HKTB) HKTB, mchain func(func(A) HKTB) func(HKTA) HKTB,
f func(A) HKTB, f func(A) HKTB,
) func(HKTA) HKTB { ) func(HKTA) HKTB {
return func(first HKTA) HKTB { return mchain(f)
return MonadChain[A, B](mchain, first, f) }
}
func MonadBind[S1, S2, B, HKTS1, HKTS2, HKTB any](
mchain func(HKTS1, func(S1) HKTS2) HKTS2,
mmap func(HKTB, func(B) S2) HKTS2,
first HKTS1,
key func(B) func(S1) S2,
f func(S1) HKTB,
) HKTS2 {
return mchain(first, func(s1 S1) HKTS2 {
return mmap(f(s1), func(b B) S2 {
return key(b)(s1)
})
})
}
func Bind[S1, S2, B, HKTS1, HKTS2, HKTB any](
mchain func(func(S1) HKTS2) func(HKTS1) HKTS2,
mmap func(func(B) S2) func(HKTB) HKTS2,
key func(B) func(S1) S2,
f func(S1) HKTB,
) func(HKTS1) HKTS2 {
mapb := F.Flow2(
F.Flip(key),
mmap,
)
return mchain(func(s1 S1) HKTS2 {
return F.Pipe2(
s1,
f,
F.Pipe1(
s1,
mapb,
),
)
})
}
func BindTo[S1, B, HKTS1, HKTB any](
mmap func(func(B) S1) func(HKTB) HKTS1,
key func(B) S1,
) func(fa HKTB) HKTS1 {
return mmap(key)
}
func MonadBindTo[S1, B, HKTS1, HKTB any](
mmap func(HKTB, func(B) S1) HKTS1,
first HKTB,
key func(B) S1,
) HKTS1 {
return mmap(first, key)
} }

View File

@@ -20,17 +20,22 @@ import (
E "github.com/IBM/fp-go/eq" E "github.com/IBM/fp-go/eq"
F "github.com/IBM/fp-go/function" F "github.com/IBM/fp-go/function"
"github.com/IBM/fp-go/internal/apply"
L "github.com/IBM/fp-go/internal/apply/testing" L "github.com/IBM/fp-go/internal/apply/testing"
"github.com/IBM/fp-go/internal/chain"
"github.com/IBM/fp-go/internal/functor"
"github.com/IBM/fp-go/internal/pointed"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
// Chain associativity law // Chain associativity law
// //
// F.chain(F.chain(fa, afb), bfc) <-> F.chain(fa, a => F.chain(afb(a), bfc)) // F.chain(F.chain(fa, afb), bfc) <-> F.chain(fa, a => F.chain(afb(a), bfc))
//
// Deprecated: use [ChainAssertAssociativity] instead
func AssertAssociativity[HKTA, HKTB, HKTC, A, B, C any](t *testing.T, func AssertAssociativity[HKTA, HKTB, HKTC, A, B, C any](t *testing.T,
eq E.Eq[HKTC], eq E.Eq[HKTC],
fofa func(A) HKTA,
fofb func(B) HKTB, fofb func(B) HKTB,
fofc func(C) HKTC, fofc func(C) HKTC,
@@ -56,12 +61,44 @@ func AssertAssociativity[HKTA, HKTB, HKTC, A, B, C any](t *testing.T,
} }
} }
// Chain associativity law
//
// F.chain(F.chain(fa, afb), bfc) <-> F.chain(fa, a => F.chain(afb(a), bfc))
func ChainAssertAssociativity[HKTA, HKTB, HKTC, HKTAB, HKTAC, HKTBC, A, B, C any](t *testing.T,
eq E.Eq[HKTC],
fofb pointed.Pointed[B, HKTB],
fofc pointed.Pointed[C, HKTC],
chainab chain.Chainable[A, B, HKTA, HKTB, HKTAB],
chainac chain.Chainable[A, C, HKTA, HKTC, HKTAC],
chainbc chain.Chainable[B, C, HKTB, HKTC, HKTBC],
ab func(A) B,
bc func(B) C,
) func(fa HKTA) bool {
return func(fa HKTA) bool {
afb := F.Flow2(ab, fofb.Of)
bfc := F.Flow2(bc, fofc.Of)
left := chainbc.Chain(bfc)(chainab.Chain(afb)(fa))
right := chainac.Chain(func(a A) HKTC {
return chainbc.Chain(bfc)(afb(a))
})(fa)
return assert.True(t, eq.Equals(left, right), "Chain associativity")
}
}
// AssertLaws asserts the apply laws `identity`, `composition`, `associative composition` and `associativity` // AssertLaws asserts the apply laws `identity`, `composition`, `associative composition` and `associativity`
//
// Deprecated: use [ChainAssertLaws] instead
func AssertLaws[HKTA, HKTB, HKTC, HKTAB, HKTBC, HKTAC, HKTABAC, A, B, C any](t *testing.T, func AssertLaws[HKTA, HKTB, HKTC, HKTAB, HKTBC, HKTAC, HKTABAC, A, B, C any](t *testing.T,
eqa E.Eq[HKTA], eqa E.Eq[HKTA],
eqc E.Eq[HKTC], eqc E.Eq[HKTC],
fofa func(A) HKTA,
fofb func(B) HKTB, fofb func(B) HKTB,
fofc func(C) HKTC, fofc func(C) HKTC,
@@ -91,7 +128,41 @@ func AssertLaws[HKTA, HKTB, HKTC, HKTAB, HKTBC, HKTAC, HKTABAC, A, B, C any](t *
// apply laws // apply laws
apply := L.AssertLaws(t, eqa, eqc, fofab, fofbc, faa, fab, fac, fbc, fmap, fapab, fapbc, fapac, fapabac, ab, bc) apply := L.AssertLaws(t, eqa, eqc, fofab, fofbc, faa, fab, fac, fbc, fmap, fapab, fapbc, fapac, fapabac, ab, bc)
// chain laws // chain laws
associativity := AssertAssociativity(t, eqc, fofa, fofb, fofc, chainab, chainac, chainbc, ab, bc) associativity := AssertAssociativity(t, eqc, fofb, fofc, chainab, chainac, chainbc, ab, bc)
return func(fa HKTA) bool {
return apply(fa) && associativity(fa)
}
}
// ChainAssertLaws asserts the apply laws `identity`, `composition`, `associative composition` and `associativity`
func ChainAssertLaws[HKTA, HKTB, HKTC, HKTAB, HKTBC, HKTAC, HKTABAC, A, B, C any](t *testing.T,
eqa E.Eq[HKTA],
eqc E.Eq[HKTC],
fofb pointed.Pointed[B, HKTB],
fofc pointed.Pointed[C, HKTC],
fofab pointed.Pointed[func(A) B, HKTAB],
fofbc pointed.Pointed[func(B) C, HKTBC],
faa functor.Functor[A, A, HKTA, HKTA],
fmap functor.Functor[func(B) C, func(func(A) B) func(A) C, HKTBC, HKTABAC],
chainab chain.Chainable[A, B, HKTA, HKTB, HKTAB],
chainac chain.Chainable[A, C, HKTA, HKTC, HKTAC],
chainbc chain.Chainable[B, C, HKTB, HKTC, HKTBC],
fapabac apply.Apply[func(A) B, func(A) C, HKTAB, HKTAC, HKTABAC],
ab func(A) B,
bc func(B) C,
) func(fa HKTA) bool {
// apply laws
apply := L.ApplyAssertLaws(t, eqa, eqc, fofab, fofbc, faa, fmap, chain.ToApply(chainab), chain.ToApply(chainbc), chain.ToApply(chainac), fapabac, ab, bc)
// chain laws
associativity := ChainAssertAssociativity(t, eqc, fofb, fofc, chainab, chainac, chainbc, ab, bc)
return func(fa HKTA) bool { return func(fa HKTA) bool {
return apply(fa) && associativity(fa) return apply(fa) && associativity(fa)

36
internal/chain/types.go Normal file
View File

@@ -0,0 +1,36 @@
// Copyright (c) 2024 IBM Corp.
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package chain
import (
"github.com/IBM/fp-go/internal/apply"
"github.com/IBM/fp-go/internal/functor"
)
type Chainable[A, B, HKTA, HKTB, HKTFAB any] interface {
apply.Apply[A, B, HKTA, HKTB, HKTFAB]
Chain(func(A) HKTB) func(HKTA) HKTB
}
// ToFunctor converts from [Chainable] to [functor.Functor]
func ToFunctor[A, B, HKTA, HKTB, HKTFAB any](ap Chainable[A, B, HKTA, HKTB, HKTFAB]) functor.Functor[A, B, HKTA, HKTB] {
return ap
}
// ToApply converts from [Chainable] to [functor.Functor]
func ToApply[A, B, HKTA, HKTB, HKTFAB any](ap Chainable[A, B, HKTA, HKTB, HKTFAB]) apply.Apply[A, B, HKTA, HKTB, HKTFAB] {
return ap
}

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) 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>> // HKTFA = HKT<F, Either<E, A>>
// HKTFB = HKT<F, Either<E, B>> // 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 { 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>> // HKTFA = HKT<F, Either<E, A>>
// HKTFB = HKT<F, Either<E, B>> // 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] // HKTGA = Either[E, A]
// HKTGB = Either[E, B] // HKTGB = Either[E, B]
return F.Bind2nd(fmap, ET.BiMap(f, g)) return fmap(ET.BiMap(f, g))
} }
// HKTFA = HKT<F, Either<E, A>> // 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)) 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]( func MonadAp[E, A, B, HKTFAB, HKTFGAB, HKTFA, HKTFB any](
fap func(HKTFGAB, HKTFA) HKTFB, 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, fmap func(HKTFAB, func(ET.Either[E, func(A) B]) func(ET.Either[E, A]) ET.Either[E, B]) HKTFGAB,
fab HKTFAB, fab HKTFAB,
fa HKTFA) HKTFB { 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) 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 { 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) return F.Pipe2(a, ET.Right[E, A], fof)
} }
@@ -142,3 +159,7 @@ func OrLeft[E1, E2, A, HKTE1A, HKTE2, HKTE2A any](
func MonadMapLeft[E, A, B, HKTFA, HKTFB any](fmap func(HKTFA, func(ET.Either[E, A]) ET.Either[B, A]) HKTFB, fa HKTFA, f func(E) B) HKTFB { func MonadMapLeft[E, A, B, HKTFA, HKTFB any](fmap func(HKTFA, func(ET.Either[E, A]) ET.Either[B, A]) HKTFB, fa HKTFA, f func(E) B) HKTFB {
return FC.MonadMap(fmap, ET.MonadMapLeft[E, A, B], fa, f) return FC.MonadMap(fmap, ET.MonadMapLeft[E, A, B], fa, f)
} }
func MapLeft[E, A, B, HKTFA, HKTFB any](fmap func(func(ET.Either[E, A]) ET.Either[B, A]) func(HKTFA) HKTFB, f func(E) B) func(HKTFA) HKTFB {
return FC.Map(fmap, ET.MapLeft[A, E, B], f)
}

View File

@@ -23,7 +23,7 @@ import (
EX "github.com/IBM/fp-go/exec" EX "github.com/IBM/fp-go/exec"
T "github.com/IBM/fp-go/tuple" P "github.com/IBM/fp-go/pair"
) )
func Exec(ctx context.Context, name string, args []string, in []byte) (EX.CommandOutput, error) { func Exec(ctx context.Context, name string, args []string, in []byte) (EX.CommandOutput, error) {
@@ -42,5 +42,5 @@ func Exec(ctx context.Context, name string, args []string, in []byte) (EX.Comman
err = fmt.Errorf("command execution of [%s][%s] failed, stdout [%s], stderr [%s], cause [%w]", name, args, stdOut.String(), stdErr.String(), err) err = fmt.Errorf("command execution of [%s][%s] failed, stdout [%s], stderr [%s], cause [%w]", name, args, stdOut.String(), stdErr.String(), err)
} }
// return the outputs // return the outputs
return T.MakeTuple2(stdOut.Bytes(), stdErr.Bytes()), err return P.MakePair(stdOut.Bytes(), stdErr.Bytes()), err
} }

View File

@@ -0,0 +1,26 @@
// Copyright (c) 2024 IBM Corp.
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package foldable
import (
M "github.com/IBM/fp-go/monoid"
)
type Foldable[A, B, HKTA any] interface {
Reduce(func(B, A) B, B) func(HKTA) B
ReduceRight(func(B, A) B, B) func(HKTA) B
FoldMap(m M.Monoid[B]) func(func(A) B) func(HKTA) B
}

View File

@@ -60,6 +60,13 @@ func MonadChainEitherK[A, E, B, HKTEA, HKTEB any](
return mchain(ma, F.Flow2(f, fromEither)) 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]( func ChainOptionK[A, E, B, HKTEA, HKTEB any](
mchain func(HKTEA, func(A) HKTEB) HKTEB, mchain func(HKTEA, func(A) HKTEB) HKTEB,
fromEither func(ET.Either[E, B]) 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]( func ChainFirstEitherK[A, E, B, HKTEA, HKTEB any](
mchain func(HKTEA, func(A) HKTEA) HKTEA, mchain func(func(A) HKTEA) func(HKTEA) HKTEA,
mmap func(HKTEB, func(B) A) HKTEA, mmap func(func(B) A) func(HKTEB) HKTEA,
fromEither func(ET.Either[E, B]) HKTEB, fromEither func(ET.Either[E, B]) HKTEB,
f func(A) ET.Either[E, B]) func(HKTEA) HKTEA { f func(A) ET.Either[E, B]) func(HKTEA) HKTEA {
return C.ChainFirst(mchain, mmap, F.Flow2(f, fromEither)) return C.ChainFirst(mchain, mmap, F.Flow2(f, fromEither))

View File

@@ -0,0 +1,24 @@
// Copyright (c) 2024 IBM Corp.
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package fromeither
import (
ET "github.com/IBM/fp-go/either"
)
type FromEither[E, A, HKTA any] interface {
FromEither(ET.Either[E, A]) HKTA
}

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]( func ChainFirstIOK[A, B, HKTA, HKTB any, GIOB ~func() B](
mchain func(HKTA, func(A) HKTA) HKTA, mchain func(func(A) HKTA) func(HKTA) HKTA,
mmap func(HKTB, func(B) A) HKTA, mmap func(func(B) A) func(HKTB) HKTA,
fromio func(GIOB) HKTB, fromio func(GIOB) HKTB,
f func(A) GIOB) func(HKTA) HKTA { f func(A) GIOB) func(HKTA) HKTA {
// chain // 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]( func ChainIOK[GR ~func() B, A, B, HKTA, HKTB any](
mchain func(HKTA, func(A) HKTB) HKTB, mchain func(func(A) HKTB) func(HKTA) HKTB,
fromio func(GR) HKTB, fromio func(GR) HKTB,
f func(A) GR) func(HKTA) HKTB { f func(A) GR) func(HKTA) HKTB {
// chain // chain

20
internal/fromio/types.go Normal file
View File

@@ -0,0 +1,20 @@
// Copyright (c) 2024 IBM Corp.
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package fromio
type FromIO[A, GA ~func() A, HKTA any] interface {
FromIO(GA) HKTA
}

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

View File

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

View File

@@ -21,6 +21,46 @@ import (
// HKTFGA = HKT[F, HKT[G, A]] // HKTFGA = HKT[F, HKT[G, A]]
// HKTFGB = HKT[F, HKT[G, B]] // HKTFGB = HKT[F, HKT[G, B]]
func MonadMap[A, B, HKTGA, HKTGB, HKTFGA, HKTFGB any](fmap func(HKTFGA, func(HKTGA) HKTGB) HKTFGB, gmap func(HKTGA, func(A) B) HKTGB, fa HKTFGA, f func(A) B) HKTFGB { func MonadMap[A, B, HKTGA, HKTGB, HKTFGA, HKTFGB any](
fmap func(HKTFGA, func(HKTGA) HKTGB) HKTFGB,
gmap func(HKTGA, func(A) B) HKTGB,
fa HKTFGA,
f func(A) B) HKTFGB {
return fmap(fa, F.Bind2nd(gmap, f)) return fmap(fa, F.Bind2nd(gmap, f))
} }
func Map[A, B, HKTGA, HKTGB, HKTFGA, HKTFGB any](
fmap func(func(HKTGA) HKTGB) func(HKTFGA) HKTFGB,
gmap func(func(A) B) func(HKTGA) HKTGB,
f func(A) B) func(HKTFGA) HKTFGB {
return 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

@@ -20,21 +20,44 @@ import (
E "github.com/IBM/fp-go/eq" E "github.com/IBM/fp-go/eq"
F "github.com/IBM/fp-go/function" F "github.com/IBM/fp-go/function"
"github.com/IBM/fp-go/internal/functor"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
// Functor identity law // Functor identity law
// //
// F.map(fa, a => a) <-> fa // F.map(fa, a => a) <-> fa
//
// Deprecated: use [FunctorAssertIdentity]
func AssertIdentity[HKTA, A any](t *testing.T, eq E.Eq[HKTA], fmap func(HKTA, func(A) A) HKTA) func(fa HKTA) bool { func AssertIdentity[HKTA, A any](t *testing.T, eq E.Eq[HKTA], fmap func(HKTA, func(A) A) HKTA) func(fa HKTA) bool {
t.Helper()
return func(fa HKTA) bool { return func(fa HKTA) bool {
return assert.True(t, eq.Equals(fa, fmap(fa, F.Identity[A])), "Functor identity law") return assert.True(t, eq.Equals(fa, fmap(fa, F.Identity[A])), "Functor identity law")
} }
} }
// Functor identity law
//
// F.map(fa, a => a) <-> fa
func FunctorAssertIdentity[HKTA, A any](
t *testing.T,
eq E.Eq[HKTA],
fca functor.Functor[A, A, HKTA, HKTA],
) func(fa HKTA) bool {
t.Helper()
return func(fa HKTA) bool {
return assert.True(t, eq.Equals(fa, fca.Map(F.Identity[A])(fa)), "Functor identity law")
}
}
// Functor composition law // Functor composition law
// //
// F.map(fa, a => bc(ab(a))) <-> F.map(F.map(fa, ab), bc) // F.map(fa, a => bc(ab(a))) <-> F.map(F.map(fa, ab), bc)
//
// Deprecated: use [FunctorAssertComposition] instead
func AssertComposition[HKTA, HKTB, HKTC, A, B, C any]( func AssertComposition[HKTA, HKTB, HKTC, A, B, C any](
t *testing.T, t *testing.T,
@@ -46,12 +69,36 @@ func AssertComposition[HKTA, HKTB, HKTC, A, B, C any](
ab func(A) B, ab func(A) B,
bc func(B) C, bc func(B) C,
) func(fa HKTA) bool { ) func(fa HKTA) bool {
t.Helper()
return func(fa HKTA) bool { return func(fa HKTA) bool {
return assert.True(t, eq.Equals(fac(fa, F.Flow2(ab, bc)), fbc(fab(fa, ab), bc)), "Functor composition law") return assert.True(t, eq.Equals(fac(fa, F.Flow2(ab, bc)), fbc(fab(fa, ab), bc)), "Functor composition law")
} }
} }
// Functor composition law
//
// F.map(fa, a => bc(ab(a))) <-> F.map(F.map(fa, ab), bc)
func FunctorAssertComposition[HKTA, HKTB, HKTC, A, B, C any](
t *testing.T,
eq E.Eq[HKTC],
fab functor.Functor[A, B, HKTA, HKTB],
fac functor.Functor[A, C, HKTA, HKTC],
fbc functor.Functor[B, C, HKTB, HKTC],
ab func(A) B,
bc func(B) C,
) func(fa HKTA) bool {
t.Helper()
return func(fa HKTA) bool {
return assert.True(t, eq.Equals(fac.Map(F.Flow2(ab, bc))(fa), fbc.Map(bc)(fab.Map(ab)(fa))), "Functor composition law")
}
}
// AssertLaws asserts the functor laws `identity` and `composition` // AssertLaws asserts the functor laws `identity` and `composition`
//
// Deprecated: use [FunctorAssertLaws] instead
func AssertLaws[HKTA, HKTB, HKTC, A, B, C any](t *testing.T, func AssertLaws[HKTA, HKTB, HKTC, A, B, C any](t *testing.T,
eqa E.Eq[HKTA], eqa E.Eq[HKTA],
eqc E.Eq[HKTC], eqc E.Eq[HKTC],
@@ -60,9 +107,11 @@ func AssertLaws[HKTA, HKTB, HKTC, A, B, C any](t *testing.T,
fab func(HKTA, func(A) B) HKTB, fab func(HKTA, func(A) B) HKTB,
fac func(HKTA, func(A) C) HKTC, fac func(HKTA, func(A) C) HKTC,
fbc func(HKTB, func(B) C) HKTC, fbc func(HKTB, func(B) C) HKTC,
ab func(A) B, ab func(A) B,
bc func(B) C, bc func(B) C,
) func(fa HKTA) bool { ) func(fa HKTA) bool {
t.Helper()
identity := AssertIdentity(t, eqa, faa) identity := AssertIdentity(t, eqa, faa)
composition := AssertComposition(t, eqc, fab, fac, fbc, ab, bc) composition := AssertComposition(t, eqc, fab, fac, fbc, ab, bc)
@@ -70,3 +119,25 @@ func AssertLaws[HKTA, HKTB, HKTC, A, B, C any](t *testing.T,
return identity(fa) && composition(fa) return identity(fa) && composition(fa)
} }
} }
// FunctorAssertLaws asserts the functor laws `identity` and `composition`
func FunctorAssertLaws[HKTA, HKTB, HKTC, A, B, C any](t *testing.T,
eqa E.Eq[HKTA],
eqc E.Eq[HKTC],
faa functor.Functor[A, A, HKTA, HKTA],
fab functor.Functor[A, B, HKTA, HKTB],
fac functor.Functor[A, C, HKTA, HKTC],
fbc functor.Functor[B, C, HKTB, HKTC],
ab func(A) B,
bc func(B) C,
) func(fa HKTA) bool {
t.Helper()
identity := FunctorAssertIdentity(t, eqa, faa)
composition := FunctorAssertComposition(t, eqc, fab, fac, fbc, ab, bc)
return func(fa HKTA) bool {
return identity(fa) && composition(fa)
}
}

20
internal/functor/types.go Normal file
View File

@@ -0,0 +1,20 @@
// 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 functor
type Functor[A, B, HKTA, HKTB any] interface {
Map(func(A) B) func(HKTA) HKTB
}

54
internal/monad/monad.go Normal file
View File

@@ -0,0 +1,54 @@
// Copyright (c) 2024 IBM Corp.
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package monad
import (
"github.com/IBM/fp-go/internal/applicative"
"github.com/IBM/fp-go/internal/apply"
"github.com/IBM/fp-go/internal/chain"
"github.com/IBM/fp-go/internal/functor"
"github.com/IBM/fp-go/internal/pointed"
)
type Monad[A, B, HKTA, HKTB, HKTFAB any] interface {
applicative.Applicative[A, B, HKTA, HKTB, HKTFAB]
chain.Chainable[A, B, HKTA, HKTB, HKTFAB]
}
// ToFunctor converts from [Monad] to [functor.Functor]
func ToFunctor[A, B, HKTA, HKTB, HKTFAB any](ap Monad[A, B, HKTA, HKTB, HKTFAB]) functor.Functor[A, B, HKTA, HKTB] {
return ap
}
// ToApply converts from [Monad] to [apply.Apply]
func ToApply[A, B, HKTA, HKTB, HKTFAB any](ap Monad[A, B, HKTA, HKTB, HKTFAB]) apply.Apply[A, B, HKTA, HKTB, HKTFAB] {
return ap
}
// ToPointed converts from [Monad] to [pointed.Pointed]
func ToPointed[A, B, HKTA, HKTB, HKTFAB any](ap Monad[A, B, HKTA, HKTB, HKTFAB]) pointed.Pointed[A, HKTA] {
return ap
}
// ToApplicative converts from [Monad] to [applicative.Applicative]
func ToApplicative[A, B, HKTA, HKTB, HKTFAB any](ap Monad[A, B, HKTA, HKTB, HKTFAB]) applicative.Applicative[A, B, HKTA, HKTB, HKTFAB] {
return ap
}
// ToChainable converts from [Monad] to [chain.Chainable]
func ToChainable[A, B, HKTA, HKTB, HKTFAB any](ap Monad[A, B, HKTA, HKTB, HKTFAB]) chain.Chainable[A, B, HKTA, HKTB, HKTFAB] {
return ap
}

View File

@@ -19,14 +19,21 @@ import (
"testing" "testing"
E "github.com/IBM/fp-go/eq" E "github.com/IBM/fp-go/eq"
"github.com/IBM/fp-go/internal/applicative"
LA "github.com/IBM/fp-go/internal/applicative/testing" LA "github.com/IBM/fp-go/internal/applicative/testing"
"github.com/IBM/fp-go/internal/chain"
LC "github.com/IBM/fp-go/internal/chain/testing" LC "github.com/IBM/fp-go/internal/chain/testing"
"github.com/IBM/fp-go/internal/functor"
"github.com/IBM/fp-go/internal/monad"
"github.com/IBM/fp-go/internal/pointed"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
// Apply monad left identity law // Apply monad left identity law
// //
// M.chain(M.of(a), f) <-> f(a) // M.chain(M.of(a), f) <-> f(a)
//
// Deprecated: use [MonadAssertLeftIdentity] instead
func AssertLeftIdentity[HKTA, HKTB, A, B any](t *testing.T, func AssertLeftIdentity[HKTA, HKTB, A, B any](t *testing.T,
eq E.Eq[HKTB], eq E.Eq[HKTB],
@@ -50,9 +57,36 @@ func AssertLeftIdentity[HKTA, HKTB, A, B any](t *testing.T,
} }
} }
// Apply monad left identity law
//
// M.chain(M.of(a), f) <-> f(a)
func MonadAssertLeftIdentity[HKTA, HKTB, HKTFAB, A, B any](t *testing.T,
eq E.Eq[HKTB],
fofb pointed.Pointed[B, HKTB],
ma monad.Monad[A, B, HKTA, HKTB, HKTFAB],
ab func(A) B,
) func(a A) bool {
return func(a A) bool {
f := func(a A) HKTB {
return fofb.Of(ab(a))
}
left := ma.Chain(f)(ma.Of(a))
right := f(a)
return assert.True(t, eq.Equals(left, right), "Monad left identity")
}
}
// Apply monad right identity law // Apply monad right identity law
// //
// M.chain(fa, M.of) <-> fa // M.chain(fa, M.of) <-> fa
//
// Deprecated: use [MonadAssertRightIdentity] instead
func AssertRightIdentity[HKTA, A any](t *testing.T, func AssertRightIdentity[HKTA, A any](t *testing.T,
eq E.Eq[HKTA], eq E.Eq[HKTA],
@@ -69,7 +103,27 @@ func AssertRightIdentity[HKTA, A any](t *testing.T,
} }
} }
// Apply monad right identity law
//
// M.chain(fa, M.of) <-> fa
func MonadAssertRightIdentity[HKTA, HKTAA, A any](t *testing.T,
eq E.Eq[HKTA],
ma monad.Monad[A, A, HKTA, HKTA, HKTAA],
) func(fa HKTA) bool {
return func(fa HKTA) bool {
left := ma.Chain(ma.Of)(fa)
right := fa
return assert.True(t, eq.Equals(left, right), "Monad right identity")
}
}
// AssertLaws asserts the apply laws `identity`, `composition`, `associative composition`, 'applicative identity', 'homomorphism', 'interchange', `associativity`, `left identity`, `right identity` // AssertLaws asserts the apply laws `identity`, `composition`, `associative composition`, 'applicative identity', 'homomorphism', 'interchange', `associativity`, `left identity`, `right identity`
//
// Deprecated: use [MonadAssertLaws] instead
func AssertLaws[HKTA, HKTB, HKTC, HKTAA, HKTAB, HKTBC, HKTAC, HKTABB, HKTABAC, A, B, C any](t *testing.T, func AssertLaws[HKTA, HKTB, HKTC, HKTAA, HKTAB, HKTBC, HKTAC, HKTABB, HKTABAC, A, B, C any](t *testing.T,
eqa E.Eq[HKTA], eqa E.Eq[HKTA],
eqb E.Eq[HKTB], eqb E.Eq[HKTB],
@@ -110,7 +164,7 @@ func AssertLaws[HKTA, HKTB, HKTC, HKTAA, HKTAB, HKTBC, HKTAC, HKTABB, HKTABAC, A
// applicative laws // applicative laws
applicative := LA.AssertLaws(t, eqa, eqb, eqc, fofa, fofb, fofaa, fofab, fofbc, fofabb, faa, fab, fac, fbc, fmap, fapaa, fapab, fapbc, fapac, fapabb, fapabac, ab, bc) applicative := LA.AssertLaws(t, eqa, eqb, eqc, fofa, fofb, fofaa, fofab, fofbc, fofabb, faa, fab, fac, fbc, fmap, fapaa, fapab, fapbc, fapac, fapabb, fapabac, ab, bc)
// chain laws // chain laws
chain := LC.AssertLaws(t, eqa, eqc, fofa, fofb, fofc, fofab, fofbc, faa, fab, fac, fbc, fmap, chainab, chainac, chainbc, fapab, fapbc, fapac, fapabac, ab, bc) chain := LC.AssertLaws(t, eqa, eqc, fofb, fofc, fofab, fofbc, faa, fab, fac, fbc, fmap, chainab, chainac, chainbc, fapab, fapbc, fapac, fapabac, ab, bc)
// monad laws // monad laws
leftIdentity := AssertLeftIdentity(t, eqb, fofa, fofb, chainab, ab) leftIdentity := AssertLeftIdentity(t, eqb, fofa, fofb, chainab, ab)
rightIdentity := AssertRightIdentity(t, eqa, fofa, chainaa) rightIdentity := AssertRightIdentity(t, eqa, fofa, chainaa)
@@ -120,3 +174,55 @@ func AssertLaws[HKTA, HKTB, HKTC, HKTAA, HKTAB, HKTBC, HKTAC, HKTABB, HKTABAC, A
return applicative(a) && chain(fa) && leftIdentity(a) && rightIdentity(fa) return applicative(a) && chain(fa) && leftIdentity(a) && rightIdentity(fa)
} }
} }
// MonadAssertLaws asserts the apply laws `identity`, `composition`, `associative composition`, 'applicative identity', 'homomorphism', 'interchange', `associativity`, `left identity`, `right identity`
func MonadAssertLaws[HKTA, HKTB, HKTC, HKTAA, HKTAB, HKTBC, HKTAC, HKTABB, HKTABAC, A, B, C any](t *testing.T,
eqa E.Eq[HKTA],
eqb E.Eq[HKTB],
eqc E.Eq[HKTC],
fofc pointed.Pointed[C, HKTC],
fofaa pointed.Pointed[func(A) A, HKTAA],
fofbc pointed.Pointed[func(B) C, HKTBC],
fofabb pointed.Pointed[func(func(A) B) B, HKTABB],
fmap functor.Functor[func(B) C, func(func(A) B) func(A) C, HKTBC, HKTABAC],
fapabb applicative.Applicative[func(A) B, B, HKTAB, HKTB, HKTABB],
fapabac applicative.Applicative[func(A) B, func(A) C, HKTAB, HKTAC, HKTABAC],
maa monad.Monad[A, A, HKTA, HKTA, HKTAA],
mab monad.Monad[A, B, HKTA, HKTB, HKTAB],
mac monad.Monad[A, C, HKTA, HKTC, HKTAC],
mbc monad.Monad[B, C, HKTB, HKTC, HKTBC],
ab func(A) B,
bc func(B) C,
) func(a A) bool {
// derivations
fofa := monad.ToPointed(maa)
fofb := monad.ToPointed(mbc)
fofab := applicative.ToPointed(fapabb)
fapaa := monad.ToApplicative(maa)
fapab := monad.ToApplicative(mab)
chainab := monad.ToChainable(mab)
chainac := monad.ToChainable(mac)
chainbc := monad.ToChainable(mbc)
fapbc := chain.ToApply(chainbc)
fapac := chain.ToApply(chainac)
faa := monad.ToFunctor(maa)
// applicative laws
apLaw := LA.ApplicativeAssertLaws(t, eqa, eqb, eqc, fofb, fofaa, fofbc, fofabb, faa, fmap, fapaa, fapab, fapbc, fapac, fapabb, fapabac, ab, bc)
// chain laws
chainLaw := LC.ChainAssertLaws(t, eqa, eqc, fofb, fofc, fofab, fofbc, faa, fmap, chainab, chainac, chainbc, applicative.ToApply(fapabac), ab, bc)
// monad laws
leftIdentity := MonadAssertLeftIdentity(t, eqb, fofb, mab, ab)
rightIdentity := MonadAssertRightIdentity(t, eqa, maa)
return func(a A) bool {
fa := fofa.Of(a)
return apLaw(a) && chainLaw(fa) && leftIdentity(a) && rightIdentity(fa)
}
}

View File

@@ -40,6 +40,12 @@ func MonadMap[A, B, HKTFA, HKTFB any](fmap func(HKTFA, func(O.Option[A]) O.Optio
return FC.MonadMap(fmap, O.MonadMap[A, B], fa, f) return FC.MonadMap(fmap, O.MonadMap[A, B], fa, f)
} }
func Map[A, B, HKTFA, HKTFB any](fmap func(func(O.Option[A]) O.Option[B]) func(HKTFA) HKTFB, f func(A) B) func(HKTFA) HKTFB {
// HKTGA = Either[E, A]
// HKTGB = Either[E, B]
return FC.Map(fmap, O.Map[A, B], f)
}
func MonadChain[A, B, HKTFA, HKTFB any]( func MonadChain[A, B, HKTFA, HKTFB any](
fchain func(HKTFA, func(O.Option[A]) HKTFB) HKTFB, fchain func(HKTFA, func(O.Option[A]) HKTFB) HKTFB,
fof func(O.Option[B]) HKTFB, fof func(O.Option[B]) HKTFB,
@@ -50,13 +56,11 @@ func MonadChain[A, B, HKTFA, HKTFB any](
} }
func Chain[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, fof func(O.Option[B]) HKTFB,
f func(A) HKTFB) func(ma HKTFA) HKTFB { f func(A) HKTFB) func(ma HKTFA) HKTFB {
// dispatch to the even more generic implementation // dispatch to the even more generic implementation
return func(ma HKTFA) HKTFB { return fchain(O.Fold(F.Nullary2(O.None[B], fof), f))
return MonadChain(fchain, fof, ma, f)
}
} }
func MonadAp[A, B, HKTFAB, HKTFGAB, HKTFA, HKTFB any]( func MonadAp[A, B, HKTFAB, HKTFGAB, HKTFA, HKTFB any](
@@ -64,12 +68,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, fmap func(HKTFAB, func(O.Option[func(A) B]) func(O.Option[A]) O.Option[B]) HKTFGAB,
fab HKTFAB, fab HKTFAB,
fa HKTFA) HKTFB { fa HKTFA) HKTFB {
// HKTGA = O.Option[A]
// HKTGB = O.Option[B]
// HKTGAB = O.Option[func(a A) B]
return apply.MonadAp(fap, fmap, O.MonadAp[B, A], fab, fa) return apply.MonadAp(fap, fmap, O.MonadAp[B, A], fab, fa)
} }
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 { 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)) return F.Bind2nd(mchain, O.Fold(onNone, onSome))
} }
@@ -89,6 +97,14 @@ func MonadChainOptionK[A, B, HKTA, HKTB any](
return MonadChain(fchain, fof, ma, FromOptionK(fof, f)) 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]( func MonadAlt[LAZY ~func() HKTFA, A, HKTFA any](
fof func(O.Option[A]) HKTFA, fof func(O.Option[A]) HKTFA,
fchain func(HKTFA, func(O.Option[A]) HKTFA) HKTFA, fchain func(HKTFA, func(O.Option[A]) HKTFA) HKTFA,
@@ -101,11 +117,9 @@ func MonadAlt[LAZY ~func() HKTFA, A, HKTFA any](
func Alt[LAZY ~func() HKTFA, A, HKTFA any]( func Alt[LAZY ~func() HKTFA, A, HKTFA any](
fof func(O.Option[A]) HKTFA, 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 { second LAZY) func(HKTFA) HKTFA {
return func(fa HKTFA) HKTFA { return fchain(O.Fold(second, F.Flow2(O.Of[A], fof)))
return MonadAlt(fof, fchain, fa, second)
}
} }

21
internal/pointed/types.go Normal file
View File

@@ -0,0 +1,21 @@
// 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 pointed
type Pointed[A, HKTA any] interface {
// Of lifts a value into its higher kinded type
Of(A) HKTA
}

View File

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

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

@@ -0,0 +1,59 @@
// Copyright (c) 2023 IBM Corp.
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package utils
import (
"fmt"
F "github.com/IBM/fp-go/function"
)
type (
Initial struct {
}
WithLastName struct {
Initial
LastName string
}
WithGivenName struct {
WithLastName
GivenName string
}
)
var (
Empty = Initial{}
SetLastName = F.Curry2(func(name string, s1 Initial) WithLastName {
return WithLastName{
Initial: s1,
LastName: name,
}
})
SetGivenName = F.Curry2(func(name string, s1 WithLastName) WithGivenName {
return WithGivenName{
WithLastName: s1,
GivenName: name,
}
})
)
func GetFullName(s WithGivenName) string {
return fmt.Sprintf("%s %s", s.GivenName, s.LastName)
}

66
io/bind.go Normal file
View File

@@ -0,0 +1,66 @@
// Copyright (c) 2023 IBM Corp.
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package io
import (
G "github.com/IBM/fp-go/io/generic"
)
// Bind creates an empty context of type [S] to be used with the [Bind] operation
func Do[S any](
empty S,
) IO[S] {
return G.Do[IO[S], S](empty)
}
// Bind attaches the result of a computation to a context [S1] to produce a context [S2]
func Bind[S1, S2, T any](
setter func(T) func(S1) S2,
f func(S1) IO[T],
) func(IO[S1]) IO[S2] {
return G.Bind[IO[S1], IO[S2], IO[T], S1, S2, T](setter, f)
}
// Let attaches the result of a computation to a context [S1] to produce a context [S2]
func Let[S1, S2, T any](
setter func(T) func(S1) S2,
f func(S1) T,
) func(IO[S1]) IO[S2] {
return G.Let[IO[S1], IO[S2], S1, S2, T](setter, f)
}
// LetTo attaches the a value to a context [S1] to produce a context [S2]
func LetTo[S1, S2, T any](
setter func(T) func(S1) S2,
b T,
) func(IO[S1]) IO[S2] {
return G.LetTo[IO[S1], IO[S2], S1, S2, T](setter, b)
}
// BindTo initializes a new state [S1] from a value [T]
func BindTo[S1, T any](
setter func(T) S1,
) func(IO[T]) IO[S1] {
return G.BindTo[IO[S1], IO[T], S1, T](setter)
}
// ApS attaches a value to a context [S1] to produce a context [S2] by considering the context and the value concurrently
func ApS[S1, S2, T any](
setter func(T) func(S1) S2,
fa IO[T],
) func(IO[S1]) IO[S2] {
return G.ApS[IO[S1], IO[S2], IO[T], S1, S2, T](setter, fa)
}

56
io/bind_test.go Normal file
View File

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

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

@@ -0,0 +1,89 @@
// Copyright (c) 2023 IBM Corp.
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package generic
import (
A "github.com/IBM/fp-go/internal/apply"
C "github.com/IBM/fp-go/internal/chain"
F "github.com/IBM/fp-go/internal/functor"
)
// Bind creates an empty context of type [S] to be used with the [Bind] operation
func Do[GS ~func() S, S any](
empty S,
) GS {
return Of[GS](empty)
}
// Bind attaches the result of a computation to a context [S1] to produce a context [S2]
func Bind[GS1 ~func() S1, GS2 ~func() S2, GT ~func() T, S1, S2, T any](
setter func(T) func(S1) S2,
f func(S1) GT,
) func(GS1) GS2 {
return C.Bind(
Chain[GS1, GS2, S1, S2],
Map[GT, GS2, T, S2],
setter,
f,
)
}
// Let attaches the result of a computation to a context [S1] to produce a context [S2]
func Let[GS1 ~func() S1, GS2 ~func() S2, S1, S2, T any](
key func(T) func(S1) S2,
f func(S1) T,
) func(GS1) GS2 {
return F.Let(
Map[GS1, GS2, S1, S2],
key,
f,
)
}
// LetTo attaches the a value to a context [S1] to produce a context [S2]
func LetTo[GS1 ~func() S1, GS2 ~func() S2, S1, S2, B any](
key func(B) func(S1) S2,
b B,
) func(GS1) GS2 {
return F.LetTo(
Map[GS1, GS2, S1, S2],
key,
b,
)
}
// BindTo initializes a new state [S1] from a value [T]
func BindTo[GS1 ~func() S1, GT ~func() T, S1, T any](
setter func(T) S1,
) func(GT) GS1 {
return C.BindTo(
Map[GT, GS1, T, S1],
setter,
)
}
// ApS attaches a value to a context [S1] to produce a context [S2] by considering the context and the value concurrently
func ApS[GS1 ~func() S1, GS2 ~func() S2, GT ~func() T, S1, S2, T any](
setter func(T) func(S1) S2,
fa GT,
) func(GS1) GS2 {
return A.ApS(
Ap[GS2, func() func(T) S2, GT, S2, T],
Map[GS1, func() func(T) S2, S1, func(T) S2],
setter,
fa,
)
}

View File

@@ -22,6 +22,12 @@ import (
C "github.com/IBM/fp-go/internal/chain" C "github.com/IBM/fp-go/internal/chain"
FC "github.com/IBM/fp-go/internal/functor" FC "github.com/IBM/fp-go/internal/functor"
L "github.com/IBM/fp-go/internal/lazy" L "github.com/IBM/fp-go/internal/lazy"
T "github.com/IBM/fp-go/tuple"
)
var (
// undefined represents an undefined value
undefined = struct{}{}
) )
// type IO[A any] = func() A // type IO[A any] = func() A
@@ -42,7 +48,7 @@ func FromIO[GA ~func() A, A any](a GA) GA {
func FromImpure[GA ~func() any, IMP ~func()](f IMP) GA { func FromImpure[GA ~func() any, IMP ~func()](f IMP) GA {
return MakeIO[GA](func() any { return MakeIO[GA](func() any {
f() f()
return nil return undefined
}) })
} }
@@ -52,7 +58,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 { func MonadMap[GA ~func() A, GB ~func() B, A, B any](fa GA, f func(A) B) GB {
return MakeIO[GB](func() B { return MakeIO[GB](func() B {
return F.Pipe1(fa(), f) return f(fa())
}) })
} }
@@ -65,13 +71,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 { 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. // 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 { func MonadChain[GA ~func() A, GB ~func() B, A, B any](fa GA, f func(A) GB) GB {
return MakeIO[GB](func() B { return MakeIO[GB](func() B {
return F.Pipe1(fa(), f)() return f(fa())()
}) })
} }
@@ -87,7 +93,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 // 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 { 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 // MonadChainFirst composes computations in sequence, using the return value of one computation to determine the next computation and
@@ -99,7 +105,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 // 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. // 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 { 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 { func ApSeq[GB ~func() B, GAB ~func() func(A) B, GA ~func() A, B, A any](ma GA) func(GAB) GB {
@@ -133,6 +143,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 // Now returns the current timestamp
func Now[GA ~func() time.Time]() GA { func Now[GA ~func() time.Time]() GA {
return MakeIO[GA](time.Now) return MakeIO[GA](time.Now)
@@ -150,5 +183,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 { 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))
})
} }

43
io/generic/monad.go Normal file
View File

@@ -0,0 +1,43 @@
// Copyright (c) 2024 IBM Corp.
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package generic
import (
"github.com/IBM/fp-go/internal/monad"
)
type ioMonad[A, B any, GA ~func() A, GB ~func() B, GAB ~func() func(A) B] struct{}
func (o *ioMonad[A, B, GA, GB, GAB]) Of(a A) GA {
return Of[GA, A](a)
}
func (o *ioMonad[A, B, GA, GB, GAB]) Map(f func(A) B) func(GA) GB {
return Map[GA, GB, A, B](f)
}
func (o *ioMonad[A, B, GA, GB, GAB]) Chain(f func(A) GB) func(GA) GB {
return Chain[GA, GB, A, B](f)
}
func (o *ioMonad[A, B, GA, GB, GAB]) Ap(fa GA) func(GAB) GB {
return Ap[GB, GAB, GA, B, A](fa)
}
// Monad implements the monadic operations for [Option]
func Monad[A, B any, GA ~func() A, GB ~func() B, GAB ~func() func(A) B]() monad.Monad[A, B, GA, GB, GAB] {
return &ioMonad[A, B, GA, GB, GAB]{}
}

View File

@@ -19,6 +19,7 @@ import (
"time" "time"
G "github.com/IBM/fp-go/io/generic" G "github.com/IBM/fp-go/io/generic"
T "github.com/IBM/fp-go/tuple"
) )
// IO represents a synchronous computation that cannot fail // 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) 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) 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

@@ -27,7 +27,7 @@ func TestLogger(t *testing.T) {
lio := l("out") lio := l("out")
assert.Equal(t, nil, lio(10)()) assert.NotPanics(t, func() { lio(10)() })
} }
func TestLogf(t *testing.T) { func TestLogf(t *testing.T) {
@@ -36,5 +36,5 @@ func TestLogf(t *testing.T) {
lio := l("Value is %d") lio := l("Value is %d")
assert.Equal(t, nil, lio(10)()) assert.NotPanics(t, func() { lio(10)() })
} }

26
io/monad.go Normal file
View File

@@ -0,0 +1,26 @@
// Copyright (c) 2024 IBM Corp.
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package io
import (
"github.com/IBM/fp-go/internal/monad"
G "github.com/IBM/fp-go/io/generic"
)
// Monad returns the monadic operations for [IO]
func Monad[A, B any]() monad.Monad[A, B, IO[A], IO[B], IO[func(A) B]] {
return G.Monad[A, B, IO[A], IO[B], IO[func(A) B]]()
}

View File

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

57
ioeither/bind_test.go Normal file
View File

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

View File

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

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