mirror of
https://github.com/IBM/fp-go.git
synced 2025-08-26 19:38:58 +02:00
Compare commits
80 Commits
cleue-add-
...
v1.0.128
Author | SHA1 | Date | |
---|---|---|---|
|
d0e4984b60 | ||
|
51ed1693a5 | ||
|
0afedbd7fe | ||
|
3f1bde219a | ||
|
6f91e91eb9 | ||
|
9f6b6d4968 | ||
|
79652d8474 | ||
|
a774d63e66 | ||
|
d86cf55a3d | ||
|
8150ae2a68 | ||
|
7daf65effc | ||
|
909f7c3bce | ||
|
5f0c644c6d | ||
|
9b3d9c6930 | ||
|
59381c1e50 | ||
|
358573cc20 | ||
|
e166806d1b | ||
|
02ec50c91d | ||
|
9e04974d0e | ||
|
2f99ca471a | ||
|
3e09a19d68 | ||
|
839ef47054 | ||
|
144b27233b | ||
|
668eb85aea | ||
|
b077fed094 | ||
|
7d3759619c | ||
|
c73467caf5 | ||
|
ca606767f3 | ||
|
fb82af9a69 | ||
|
57ad8c6466 | ||
|
5a9f405362 | ||
|
89c34254a9 | ||
|
a14feff1d6 | ||
|
f3642bad60 | ||
|
997627b318 | ||
|
1e1411c003 | ||
|
9ed9f8a171 | ||
|
e7428549e4 | ||
|
aef0048119 | ||
|
709d74b135 | ||
|
38c6541254 | ||
|
813b83b423 | ||
|
9139dedbbe | ||
|
5e15119653 | ||
|
986aa21055 | ||
|
3a4c46ec1e | ||
|
96c3ee20ff | ||
|
7af9acfd99 | ||
|
36eefbcd27 | ||
|
973138c822 | ||
|
12ef79184b | ||
|
5ac47440a1 | ||
|
3aa55c74d4 | ||
|
a6f55a199c | ||
|
2b500d15da | ||
|
599b8256b6 | ||
|
cf70b47984 | ||
|
7bceb856f8 | ||
|
49e89de783 | ||
|
a87de2f644 | ||
|
6d043d2752 | ||
|
1d02f21ff5 | ||
|
e82575fe08 | ||
|
5fcd0b1595 | ||
|
5caabf478c | ||
|
b7ec18c83e | ||
|
96686425fb | ||
|
1f675e08fa | ||
|
4d2f410c98 | ||
|
8f49c1328c | ||
|
2a1d5821db | ||
|
6abbdc5ee1 | ||
|
5fea9858a9 | ||
|
e6426c90c0 | ||
|
54ce59105e | ||
|
8bb006c741 | ||
|
b6efa35b03 | ||
|
35848900c0 | ||
|
3d54f99739 | ||
|
ffd9418cac |
12
.github/workflows/build.yml
vendored
12
.github/workflows/build.yml
vendored
@@ -17,8 +17,8 @@ on:
|
||||
env:
|
||||
# Currently no way to detect automatically
|
||||
DEFAULT_BRANCH: main
|
||||
GO_VERSION: 1.20.6 # renovate: datasource=golang-version depName=golang
|
||||
NODE_VERSION: 18
|
||||
GO_VERSION: 1.21.6 # renovate: datasource=golang-version depName=golang
|
||||
NODE_VERSION: 20
|
||||
DRY_RUN: true
|
||||
|
||||
jobs:
|
||||
@@ -26,14 +26,14 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [ '1.20.x', '1.21.x' ]
|
||||
go-version: [ '1.20.x', '1.21.x', '1.22.x']
|
||||
steps:
|
||||
# full checkout for semantic-release
|
||||
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Set up go ${{ matrix.go-version }}
|
||||
uses: actions/setup-go@v4
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
-
|
||||
@@ -60,12 +60,12 @@ jobs:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Node.js ${{ env.NODE_VERSION }}
|
||||
uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4.0.0
|
||||
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
|
||||
- name: Set up go ${{env.GO_VERSION}}
|
||||
uses: actions/setup-go@v4
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{env.GO_VERSION}}
|
||||
|
||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,3 +1,4 @@
|
||||
fp-go.exe
|
||||
main.exe
|
||||
build/
|
||||
build/
|
||||
.idea
|
@@ -17,6 +17,7 @@ package array
|
||||
|
||||
import (
|
||||
G "github.com/IBM/fp-go/array/generic"
|
||||
EM "github.com/IBM/fp-go/endomorphism"
|
||||
F "github.com/IBM/fp-go/function"
|
||||
"github.com/IBM/fp-go/internal/array"
|
||||
M "github.com/IBM/fp-go/monoid"
|
||||
@@ -57,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 {
|
||||
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 {
|
||||
@@ -89,16 +90,16 @@ func filterMapRef[A, B any](fa []A, pred func(a *A) bool, f func(a *A) B) []B {
|
||||
}
|
||||
|
||||
// Filter returns a new array with all elements from the original array that match a predicate
|
||||
func Filter[A any](pred func(A) bool) func([]A) []A {
|
||||
func Filter[A any](pred func(A) bool) EM.Endomorphism[[]A] {
|
||||
return G.Filter[[]A](pred)
|
||||
}
|
||||
|
||||
// FilterWithIndex returns a new array with all elements from the original array that match a predicate
|
||||
func FilterWithIndex[A any](pred func(int, A) bool) func([]A) []A {
|
||||
func FilterWithIndex[A any](pred func(int, A) bool) EM.Endomorphism[[]A] {
|
||||
return G.FilterWithIndex[[]A](pred)
|
||||
}
|
||||
|
||||
func FilterRef[A any](pred func(*A) bool) func([]A) []A {
|
||||
func FilterRef[A any](pred func(*A) bool) EM.Endomorphism[[]A] {
|
||||
return F.Bind2nd(filterRef[A], pred)
|
||||
}
|
||||
|
||||
@@ -227,7 +228,7 @@ func Last[A any](as []A) O.Option[A] {
|
||||
return G.Last(as)
|
||||
}
|
||||
|
||||
func PrependAll[A any](middle A) func([]A) []A {
|
||||
func PrependAll[A any](middle A) EM.Endomorphism[[]A] {
|
||||
return func(as []A) []A {
|
||||
count := len(as)
|
||||
dst := count * 2
|
||||
@@ -242,7 +243,7 @@ func PrependAll[A any](middle A) func([]A) []A {
|
||||
}
|
||||
}
|
||||
|
||||
func Intersperse[A any](middle A) func([]A) []A {
|
||||
func Intersperse[A any](middle A) EM.Endomorphism[[]A] {
|
||||
prepend := PrependAll(middle)
|
||||
return func(as []A) []A {
|
||||
if IsEmpty(as) {
|
||||
@@ -271,7 +272,7 @@ func Lookup[A any](idx int) func([]A) O.Option[A] {
|
||||
return G.Lookup[[]A](idx)
|
||||
}
|
||||
|
||||
func UpsertAt[A any](a A) func([]A) []A {
|
||||
func UpsertAt[A any](a A) EM.Endomorphism[[]A] {
|
||||
return G.UpsertAt[[]A](a)
|
||||
}
|
||||
|
||||
@@ -304,14 +305,20 @@ func ConstNil[A any]() []A {
|
||||
return array.ConstNil[[]A]()
|
||||
}
|
||||
|
||||
func SliceRight[A any](start int) func([]A) []A {
|
||||
func SliceRight[A any](start int) EM.Endomorphism[[]A] {
|
||||
return G.SliceRight[[]A](start)
|
||||
}
|
||||
|
||||
// Copy creates a shallow copy of the array
|
||||
func Copy[A any](b []A) []A {
|
||||
return G.Copy(b)
|
||||
}
|
||||
|
||||
// Clone creates a deep copy of the array using the provided endomorphism to clone the values
|
||||
func Clone[A any](f func(A) A) func(as []A) []A {
|
||||
return G.Clone[[]A](f)
|
||||
}
|
||||
|
||||
// FoldMap maps and folds an array. Map the Array passing each value to the iterating function. Then fold the results using the provided Monoid.
|
||||
func FoldMap[A, B any](m M.Monoid[B]) func(func(A) B) func([]A) B {
|
||||
return G.FoldMap[[]A](m)
|
||||
@@ -327,8 +334,8 @@ func Fold[A any](m M.Monoid[A]) func([]A) A {
|
||||
return G.Fold[[]A](m)
|
||||
}
|
||||
|
||||
func Push[A any](a A) func([]A) []A {
|
||||
return G.Push[[]A](a)
|
||||
func Push[A any](a A) EM.Endomorphism[[]A] {
|
||||
return G.Push[EM.Endomorphism[[]A]](a)
|
||||
}
|
||||
|
||||
func MonadFlap[B, A any](fab []func(A) B, a A) []B {
|
||||
@@ -338,3 +345,7 @@ func MonadFlap[B, A any](fab []func(A) B, a A) []B {
|
||||
func Flap[B, A any](a A) func([]func(A) B) []B {
|
||||
return G.Flap[func(A) B, []func(A) B, []B, A, B](a)
|
||||
}
|
||||
|
||||
func Prepend[A any](head A) EM.Endomorphism[[]A] {
|
||||
return G.Prepend[EM.Endomorphism[[]A]](head)
|
||||
}
|
||||
|
66
array/bind.go
Normal file
66
array/bind.go
Normal 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
56
array/bind_test.go
Normal 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"))
|
||||
}
|
@@ -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 {
|
||||
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 {
|
||||
@@ -304,6 +304,11 @@ func Copy[AS ~[]A, A any](b AS) AS {
|
||||
return buf
|
||||
}
|
||||
|
||||
func Clone[AS ~[]A, A any](f func(A) A) func(as AS) AS {
|
||||
// implementation assumes that map does not optimize for the empty array
|
||||
return Map[AS, AS](f)
|
||||
}
|
||||
|
||||
func FoldMap[AS ~[]A, A, B any](m M.Monoid[B]) func(func(A) B) func(AS) B {
|
||||
return func(f func(A) B) func(AS) B {
|
||||
return func(as AS) B {
|
||||
@@ -330,7 +335,7 @@ func Fold[AS ~[]A, A any](m M.Monoid[A]) func(AS) A {
|
||||
}
|
||||
}
|
||||
|
||||
func Push[GA ~[]A, A any](a A) func(GA) GA {
|
||||
func Push[ENDO ~func(GA) GA, GA ~[]A, A any](a A) ENDO {
|
||||
return F.Bind2nd(array.Push[GA, A], a)
|
||||
}
|
||||
|
||||
@@ -339,5 +344,9 @@ func MonadFlap[FAB ~func(A) B, GFAB ~[]FAB, GB ~[]B, A, B any](fab GFAB, a A) GB
|
||||
}
|
||||
|
||||
func Flap[FAB ~func(A) B, GFAB ~[]FAB, GB ~[]B, A, B any](a A) func(GFAB) GB {
|
||||
return F.Bind2nd(MonadFlap[FAB, GFAB, GB, A, B], a)
|
||||
return FC.Flap(Map[GFAB, GB], a)
|
||||
}
|
||||
|
||||
func Prepend[ENDO ~func(AS) AS, AS []A, A any](head A) ENDO {
|
||||
return array.Prepend[ENDO](head)
|
||||
}
|
||||
|
89
array/generic/bind.go
Normal file
89
array/generic/bind.go
Normal 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
43
array/generic/monad.go
Normal 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]{}
|
||||
}
|
32
array/generic/uniq.go
Normal file
32
array/generic/uniq.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package generic
|
||||
|
||||
import F "github.com/IBM/fp-go/function"
|
||||
|
||||
// StrictUniq converts an array of arbitrary items into an array or unique items
|
||||
// where uniqueness is determined by the built-in uniqueness constraint
|
||||
func StrictUniq[AS ~[]A, A comparable](as AS) AS {
|
||||
return Uniq[AS](F.Identity[A])(as)
|
||||
}
|
||||
|
||||
// uniquePredUnsafe returns a predicate on a map for uniqueness
|
||||
func uniquePredUnsafe[PRED ~func(A) K, A any, K comparable](f PRED) func(int, A) bool {
|
||||
lookup := make(map[K]bool)
|
||||
return func(_ int, a A) bool {
|
||||
k := f(a)
|
||||
_, has := lookup[k]
|
||||
if has {
|
||||
return false
|
||||
}
|
||||
lookup[k] = true
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// Uniq converts an array of arbitrary items into an array or unique items
|
||||
// where uniqueness is determined based on a key extractor function
|
||||
func Uniq[AS ~[]A, PRED ~func(A) K, A any, K comparable](f PRED) func(as AS) AS {
|
||||
return func(as AS) AS {
|
||||
// we need to create a new predicate for each iteration
|
||||
return filterWithIndex(as, uniquePredUnsafe(f))
|
||||
}
|
||||
}
|
26
array/monad.go
Normal file
26
array/monad.go
Normal 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]()
|
||||
}
|
@@ -24,10 +24,10 @@ import (
|
||||
func concat[T any](left, right []T) []T {
|
||||
// some performance checks
|
||||
ll := len(left)
|
||||
lr := len(right)
|
||||
if ll == 0 {
|
||||
return right
|
||||
}
|
||||
lr := len(right)
|
||||
if lr == 0 {
|
||||
return left
|
||||
}
|
||||
|
@@ -17,6 +17,7 @@ package nonempty
|
||||
|
||||
import (
|
||||
G "github.com/IBM/fp-go/array/generic"
|
||||
EM "github.com/IBM/fp-go/endomorphism"
|
||||
F "github.com/IBM/fp-go/function"
|
||||
"github.com/IBM/fp-go/internal/array"
|
||||
S "github.com/IBM/fp-go/semigroup"
|
||||
@@ -43,11 +44,11 @@ func From[A any](first A, data ...A) NonEmptyArray[A] {
|
||||
return buffer
|
||||
}
|
||||
|
||||
func IsEmpty[A any](as NonEmptyArray[A]) bool {
|
||||
func IsEmpty[A any](_ NonEmptyArray[A]) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func IsNonEmpty[A any](as NonEmptyArray[A]) bool {
|
||||
func IsNonEmpty[A any](_ NonEmptyArray[A]) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -65,6 +66,12 @@ func Reduce[A, B any](f func(B, A) B, initial B) func(NonEmptyArray[A]) B {
|
||||
}
|
||||
}
|
||||
|
||||
func ReduceRight[A, B any](f func(A, B) B, initial B) func(NonEmptyArray[A]) B {
|
||||
return func(as NonEmptyArray[A]) B {
|
||||
return array.ReduceRight(as, f, initial)
|
||||
}
|
||||
}
|
||||
|
||||
func Tail[A any](as NonEmptyArray[A]) []A {
|
||||
return as[1:]
|
||||
}
|
||||
@@ -122,3 +129,8 @@ func Fold[A any](s S.Semigroup[A]) func(NonEmptyArray[A]) A {
|
||||
return array.Reduce(Tail(as), s.Concat, Head(as))
|
||||
}
|
||||
}
|
||||
|
||||
// Prepend prepends a single value to an array
|
||||
func Prepend[A any](head A) EM.Endomorphism[NonEmptyArray[A]] {
|
||||
return array.Prepend[EM.Endomorphism[NonEmptyArray[A]]](head)
|
||||
}
|
||||
|
74
array/testing/laws.go
Normal file
74
array/testing/laws.go
Normal 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,
|
||||
)
|
||||
|
||||
}
|
47
array/testing/laws_test.go
Normal file
47
array/testing/laws_test.go
Normal 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))
|
||||
}
|
17
array/uniq.go
Normal file
17
array/uniq.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package array
|
||||
|
||||
import (
|
||||
G "github.com/IBM/fp-go/array/generic"
|
||||
)
|
||||
|
||||
// StrictUniq converts an array of arbitrary items into an array or unique items
|
||||
// where uniqueness is determined by the built-in uniqueness constraint
|
||||
func StrictUniq[A comparable](as []A) []A {
|
||||
return G.StrictUniq[[]A](as)
|
||||
}
|
||||
|
||||
// Uniq converts an array of arbitrary items into an array or unique items
|
||||
// where uniqueness is determined based on a key extractor function
|
||||
func Uniq[A any, K comparable](f func(A) K) func(as []A) []A {
|
||||
return G.Uniq[[]A](f)
|
||||
}
|
14
array/uniq_test.go
Normal file
14
array/uniq_test.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package array
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestUniq(t *testing.T) {
|
||||
data := From(1, 2, 3, 2, 4, 1)
|
||||
|
||||
uniq := StrictUniq(data)
|
||||
assert.Equal(t, From(1, 2, 3, 4), uniq)
|
||||
}
|
@@ -32,20 +32,20 @@ type bounded[T any] struct {
|
||||
b T
|
||||
}
|
||||
|
||||
func (self bounded[T]) Equals(x, y T) bool {
|
||||
return self.e(x, y)
|
||||
func (b bounded[T]) Equals(x, y T) bool {
|
||||
return b.e(x, y)
|
||||
}
|
||||
|
||||
func (self bounded[T]) Compare(x, y T) int {
|
||||
return self.c(x, y)
|
||||
func (b bounded[T]) Compare(x, y T) int {
|
||||
return b.c(x, y)
|
||||
}
|
||||
|
||||
func (self bounded[T]) Top() T {
|
||||
return self.t
|
||||
func (b bounded[T]) Top() T {
|
||||
return b.t
|
||||
}
|
||||
|
||||
func (self bounded[T]) Bottom() T {
|
||||
return self.b
|
||||
func (b bounded[T]) Bottom() T {
|
||||
return b.b
|
||||
}
|
||||
|
||||
// MakeBounded creates an instance of a bounded type
|
||||
|
@@ -15,6 +15,10 @@
|
||||
|
||||
package bytes
|
||||
|
||||
func Empty() []byte {
|
||||
return Monoid.Empty()
|
||||
}
|
||||
|
||||
func ToString(a []byte) string {
|
||||
return string(a)
|
||||
}
|
||||
|
@@ -34,5 +34,6 @@ func Commands() []*C.Command {
|
||||
IOEitherCommand(),
|
||||
IOCommand(),
|
||||
IOOptionCommand(),
|
||||
DICommand(),
|
||||
}
|
||||
}
|
||||
|
231
cli/di.go
Normal file
231
cli/di.go
Normal file
@@ -0,0 +1,231 @@
|
||||
// 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 cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
C "github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func generateMakeProvider(f *os.File, i int) {
|
||||
// non generic version
|
||||
fmt.Fprintf(f, "\n// MakeProvider%d creates a [DIE.Provider] for an [InjectionToken] from a function with %d dependencies\n", i, i)
|
||||
fmt.Fprintf(f, "func MakeProvider%d[", i)
|
||||
for j := 0; j < i; j++ {
|
||||
if j > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "T%d", j+1)
|
||||
}
|
||||
fmt.Fprintf(f, " any, R any](\n")
|
||||
fmt.Fprintf(f, " token InjectionToken[R],\n")
|
||||
for j := 0; j < i; j++ {
|
||||
fmt.Fprintf(f, " d%d Dependency[T%d],\n", j+1, j+1)
|
||||
}
|
||||
fmt.Fprintf(f, " f func(")
|
||||
for j := 0; j < i; j++ {
|
||||
if j > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "T%d", j+1)
|
||||
}
|
||||
fmt.Fprintf(f, ") IOE.IOEither[error, R],\n")
|
||||
fmt.Fprintf(f, ") DIE.Provider {\n")
|
||||
fmt.Fprint(f, " return DIE.MakeProvider(\n")
|
||||
fmt.Fprint(f, " token,\n")
|
||||
fmt.Fprintf(f, " MakeProviderFactory%d(\n", i)
|
||||
for j := 0; j < i; j++ {
|
||||
fmt.Fprintf(f, " d%d,\n", j+1)
|
||||
}
|
||||
fmt.Fprint(f, " f,\n")
|
||||
fmt.Fprint(f, " ))\n")
|
||||
fmt.Fprintf(f, "}\n")
|
||||
}
|
||||
|
||||
func generateMakeTokenWithDefault(f *os.File, i int) {
|
||||
// non generic version
|
||||
fmt.Fprintf(f, "\n// MakeTokenWithDefault%d creates an [InjectionToken] with a default implementation with %d dependencies\n", i, i)
|
||||
fmt.Fprintf(f, "func MakeTokenWithDefault%d[", i)
|
||||
for j := 0; j < i; j++ {
|
||||
if j > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "T%d", j+1)
|
||||
}
|
||||
fmt.Fprintf(f, " any, R any](\n")
|
||||
fmt.Fprintf(f, " name string,\n")
|
||||
for j := 0; j < i; j++ {
|
||||
fmt.Fprintf(f, " d%d Dependency[T%d],\n", j+1, j+1)
|
||||
}
|
||||
fmt.Fprintf(f, " f func(")
|
||||
for j := 0; j < i; j++ {
|
||||
if j > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "T%d", j+1)
|
||||
}
|
||||
fmt.Fprintf(f, ") IOE.IOEither[error, R],\n")
|
||||
fmt.Fprintf(f, ") InjectionToken[R] {\n")
|
||||
fmt.Fprintf(f, " return MakeTokenWithDefault[R](name, MakeProviderFactory%d(\n", i)
|
||||
for j := 0; j < i; j++ {
|
||||
fmt.Fprintf(f, " d%d,\n", j+1)
|
||||
}
|
||||
fmt.Fprint(f, " f,\n")
|
||||
fmt.Fprint(f, " ))\n")
|
||||
fmt.Fprintf(f, "}\n")
|
||||
}
|
||||
|
||||
func generateMakeProviderFactory(f *os.File, i int) {
|
||||
// non generic version
|
||||
fmt.Fprintf(f, "\n// MakeProviderFactory%d creates a [DIE.ProviderFactory] from a function with %d arguments and %d dependencies\n", i, i, i)
|
||||
fmt.Fprintf(f, "func MakeProviderFactory%d[", i)
|
||||
for j := 0; j < i; j++ {
|
||||
if j > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "T%d", j+1)
|
||||
}
|
||||
fmt.Fprintf(f, " any, R any](\n")
|
||||
for j := 0; j < i; j++ {
|
||||
fmt.Fprintf(f, " d%d Dependency[T%d],\n", j+1, j+1)
|
||||
}
|
||||
fmt.Fprintf(f, " f func(")
|
||||
for j := 0; j < i; j++ {
|
||||
if j > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "T%d", j+1)
|
||||
}
|
||||
fmt.Fprintf(f, ") IOE.IOEither[error, R],\n")
|
||||
fmt.Fprintf(f, ") DIE.ProviderFactory {\n")
|
||||
fmt.Fprint(f, " return DIE.MakeProviderFactory(\n")
|
||||
fmt.Fprint(f, " A.From[DIE.Dependency](\n")
|
||||
for j := 0; j < i; j++ {
|
||||
fmt.Fprintf(f, " d%d,\n", j+1)
|
||||
}
|
||||
fmt.Fprint(f, " ),\n")
|
||||
fmt.Fprintf(f, " eraseProviderFactory%d(\n", i)
|
||||
for j := 0; j < i; j++ {
|
||||
fmt.Fprintf(f, " d%d,\n", j+1)
|
||||
}
|
||||
fmt.Fprint(f, " f,\n")
|
||||
fmt.Fprint(f, " ),\n")
|
||||
fmt.Fprint(f, " )\n")
|
||||
fmt.Fprintf(f, "}\n")
|
||||
}
|
||||
|
||||
func generateEraseProviderFactory(f *os.File, i int) {
|
||||
// non generic version
|
||||
fmt.Fprintf(f, "\n// eraseProviderFactory%d creates a function that takes a variadic number of untyped arguments and from a function of %d strongly typed arguments and %d dependencies\n", i, i, i)
|
||||
fmt.Fprintf(f, "func eraseProviderFactory%d[", i)
|
||||
for j := 0; j < i; j++ {
|
||||
if j > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "T%d", j+1)
|
||||
}
|
||||
fmt.Fprintf(f, " any, R any](\n")
|
||||
for j := 0; j < i; j++ {
|
||||
fmt.Fprintf(f, " d%d Dependency[T%d],\n", j+1, j+1)
|
||||
}
|
||||
fmt.Fprintf(f, " f func(")
|
||||
for j := 0; j < i; j++ {
|
||||
if j > 0 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
fmt.Fprintf(f, "T%d", j+1)
|
||||
}
|
||||
fmt.Fprintf(f, ") IOE.IOEither[error, R]) func(params ...any) IOE.IOEither[error, any] {\n")
|
||||
fmt.Fprintf(f, " ft := eraseTuple(T.Tupled%d(f))\n", i)
|
||||
for j := 0; j < i; j++ {
|
||||
fmt.Fprintf(f, " t%d := lookupAt[T%d](%d, d%d)\n", j+1, j+1, j, j+1)
|
||||
}
|
||||
fmt.Fprint(f, " return func(params ...any) IOE.IOEither[error, any] {\n")
|
||||
fmt.Fprintf(f, " return ft(E.SequenceT%d(\n", i)
|
||||
for j := 0; j < i; j++ {
|
||||
fmt.Fprintf(f, " t%d(params),\n", j+1)
|
||||
}
|
||||
fmt.Fprint(f, " ))\n")
|
||||
fmt.Fprint(f, " }\n")
|
||||
fmt.Fprintf(f, "}\n")
|
||||
}
|
||||
|
||||
func generateDIHelpers(filename string, count int) error {
|
||||
dir, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
absDir, err := filepath.Abs(dir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pkg := filepath.Base(absDir)
|
||||
f, err := os.Create(filepath.Clean(filename))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
// log
|
||||
log.Printf("Generating code in [%s] for package [%s] with [%d] repetitions ...", filename, pkg, count)
|
||||
|
||||
// some header
|
||||
fmt.Fprintln(f, "// Code generated by go generate; DO NOT EDIT.")
|
||||
fmt.Fprintln(f, "// This file was generated by robots at")
|
||||
fmt.Fprintf(f, "// %s\n\n", time.Now())
|
||||
|
||||
fmt.Fprintf(f, "package %s\n\n", pkg)
|
||||
|
||||
fmt.Fprint(f, `
|
||||
import (
|
||||
E "github.com/IBM/fp-go/either"
|
||||
IOE "github.com/IBM/fp-go/ioeither"
|
||||
T "github.com/IBM/fp-go/tuple"
|
||||
A "github.com/IBM/fp-go/array"
|
||||
DIE "github.com/IBM/fp-go/di/erasure"
|
||||
)
|
||||
`)
|
||||
|
||||
for i := 1; i <= count; i++ {
|
||||
generateEraseProviderFactory(f, i)
|
||||
generateMakeProviderFactory(f, i)
|
||||
generateMakeTokenWithDefault(f, i)
|
||||
generateMakeProvider(f, i)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func DICommand() *C.Command {
|
||||
return &C.Command{
|
||||
Name: "di",
|
||||
Usage: "generate code for the dependency injection package",
|
||||
Flags: []C.Flag{
|
||||
flagCount,
|
||||
flagFilename,
|
||||
},
|
||||
Action: func(ctx *C.Context) error {
|
||||
return generateDIHelpers(
|
||||
ctx.String(keyFilename),
|
||||
ctx.Int(keyCount),
|
||||
)
|
||||
},
|
||||
}
|
||||
}
|
39
cli/pipe.go
39
cli/pipe.go
@@ -265,10 +265,15 @@ func generatePipe(f *os.File, i int) {
|
||||
fmt.Fprintf(f, ", f%d F%d", j, j)
|
||||
}
|
||||
fmt.Fprintf(f, ") T%d {\n", i)
|
||||
for j := 1; j <= i; j++ {
|
||||
fmt.Fprintf(f, " t%d := f%d(t%d)\n", j, j, j-1)
|
||||
fmt.Fprintf(f, " return ")
|
||||
for j := i; j >= 1; j-- {
|
||||
fmt.Fprintf(f, "f%d(", j)
|
||||
}
|
||||
fmt.Fprintf(f, " return t%d\n", i)
|
||||
fmt.Fprintf(f, "t0")
|
||||
for j := 1; j <= i; j++ {
|
||||
fmt.Fprintf(f, ")")
|
||||
}
|
||||
fmt.Fprintf(f, "\n")
|
||||
fmt.Fprintln(f, "}")
|
||||
}
|
||||
|
||||
@@ -295,15 +300,16 @@ func recurseCurry(f *os.File, indent string, total, count int) {
|
||||
func generateCurry(f *os.File, i int) {
|
||||
// Create the curry version
|
||||
fmt.Fprintf(f, "\n// Curry%d takes a function with %d parameters and returns a cascade of functions each taking only one parameter.\n// The inverse function is [Uncurry%d]\n", i, i, i)
|
||||
fmt.Fprintf(f, "func Curry%d[T0", i)
|
||||
for j := 1; j <= i; j++ {
|
||||
fmt.Fprintf(f, "func Curry%d[FCT ~func(T0", i)
|
||||
for j := 1; j < i; j++ {
|
||||
fmt.Fprintf(f, ", T%d", j)
|
||||
}
|
||||
fmt.Fprintf(f, " any](f func(T0")
|
||||
for j := 2; j <= i; j++ {
|
||||
fmt.Fprintf(f, ", T%d", j-1)
|
||||
fmt.Fprintf(f, ") T%d", i)
|
||||
// type arguments
|
||||
for j := 0; j <= i; j++ {
|
||||
fmt.Fprintf(f, ", T%d", j)
|
||||
}
|
||||
fmt.Fprintf(f, ") T%d) func(T0)", i)
|
||||
fmt.Fprintf(f, " any](f FCT) func(T0)")
|
||||
for j := 2; j <= i; j++ {
|
||||
fmt.Fprintf(f, " func(T%d)", j-1)
|
||||
}
|
||||
@@ -315,15 +321,16 @@ func generateCurry(f *os.File, i int) {
|
||||
func generateUncurry(f *os.File, i int) {
|
||||
// Create the uncurry version
|
||||
fmt.Fprintf(f, "\n// Uncurry%d takes a cascade of %d functions each taking only one parameter and returns a function with %d parameters .\n// The inverse function is [Curry%d]\n", i, i, i, i)
|
||||
fmt.Fprintf(f, "func Uncurry%d[T0", i)
|
||||
for j := 1; j <= i; j++ {
|
||||
fmt.Fprintf(f, "func Uncurry%d[FCT ~func(T0)", i)
|
||||
for j := 1; j < i; j++ {
|
||||
fmt.Fprintf(f, " func(T%d)", j)
|
||||
}
|
||||
fmt.Fprintf(f, " T%d", i)
|
||||
// the type parameters
|
||||
for j := 0; j <= i; j++ {
|
||||
fmt.Fprintf(f, ", T%d", j)
|
||||
}
|
||||
fmt.Fprintf(f, " any](f")
|
||||
for j := 1; j <= i; j++ {
|
||||
fmt.Fprintf(f, " func(T%d)", j-1)
|
||||
}
|
||||
fmt.Fprintf(f, " T%d) func(", i)
|
||||
fmt.Fprintf(f, " any](f FCT) func(")
|
||||
for j := 1; j <= i; j++ {
|
||||
if j > 1 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
|
40
cli/tuple.go
40
cli/tuple.go
@@ -405,8 +405,6 @@ func generateTupleHelpers(filename string, count int) error {
|
||||
|
||||
fmt.Fprintf(f, `
|
||||
import (
|
||||
"fmt"
|
||||
"encoding/json"
|
||||
M "github.com/IBM/fp-go/monoid"
|
||||
O "github.com/IBM/fp-go/ord"
|
||||
)
|
||||
@@ -457,7 +455,7 @@ func generateTupleMarshal(f *os.File, i int) {
|
||||
fmt.Fprintf(f, "func (t ")
|
||||
writeTupleType(f, "T", i)
|
||||
fmt.Fprintf(f, ") MarshalJSON() ([]byte, error) {\n")
|
||||
fmt.Fprintf(f, " return json.Marshal([]any{")
|
||||
fmt.Fprintf(f, " return tupleMarshalJSON(")
|
||||
// function prototypes
|
||||
for j := 1; j <= i; j++ {
|
||||
if j > 1 {
|
||||
@@ -465,7 +463,7 @@ func generateTupleMarshal(f *os.File, i int) {
|
||||
}
|
||||
fmt.Fprintf(f, "t.F%d", j)
|
||||
}
|
||||
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 *")
|
||||
writeTupleType(f, "T", i)
|
||||
fmt.Fprintf(f, ") UnmarshalJSON(data []byte) error {\n")
|
||||
fmt.Fprintf(f, " var tmp []json.RawMessage\n")
|
||||
fmt.Fprintf(f, " if err := json.Unmarshal(data, &tmp); err != nil {return err}\n")
|
||||
fmt.Fprintf(f, " l := len(tmp)\n")
|
||||
// unmarshal fields
|
||||
fmt.Fprintf(f, " return tupleUnmarshalJSON(data")
|
||||
// function prototypes
|
||||
for j := 1; j <= i; j++ {
|
||||
fmt.Fprintf(f, " if l > %d {\n", j-1)
|
||||
fmt.Fprintf(f, " if err := json.Unmarshal(tmp[%d], &t.F%d); err != nil {return err}\n", j-1, j)
|
||||
fmt.Fprintf(f, ", &t.F%d", j)
|
||||
}
|
||||
fmt.Fprintf(f, " ")
|
||||
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)
|
||||
fmt.Fprintf(f, ") String() string {\n")
|
||||
// convert to string
|
||||
fmt.Fprintf(f, " return fmt.Sprintf(\"Tuple%d[", i)
|
||||
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, ")\", ")
|
||||
fmt.Fprint(f, " return tupleString(")
|
||||
for j := 1; j <= i; j++ {
|
||||
if j > 1 {
|
||||
fmt.Fprintf(f, ", ")
|
||||
}
|
||||
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")
|
||||
}
|
||||
|
@@ -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()))
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
|
68
context/readereither/bind.go
Normal file
68
context/readereither/bind.go
Normal 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)
|
||||
}
|
58
context/readereither/bind_test.go
Normal file
58
context/readereither/bind_test.go
Normal 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"))
|
||||
}
|
67
context/readerioeither/bind.go
Normal file
67
context/readerioeither/bind.go
Normal 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)
|
||||
}
|
58
context/readerioeither/bind_test.go
Normal file
58
context/readerioeither/bind_test.go
Normal 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"))
|
||||
}
|
@@ -47,5 +47,5 @@ func ExampleReadFile() {
|
||||
fmt.Println(result())
|
||||
|
||||
// Output:
|
||||
// Right[<nil>, string](Carsten)
|
||||
// Right[string](Carsten)
|
||||
}
|
||||
|
@@ -26,7 +26,7 @@ import (
|
||||
func onWriteAll[W io.Writer](data []byte) func(w W) RIOE.ReaderIOEither[[]byte] {
|
||||
return func(w W) RIOE.ReaderIOEither[[]byte] {
|
||||
return F.Pipe1(
|
||||
RIOE.TryCatch(func(ctx context.Context) func() ([]byte, error) {
|
||||
RIOE.TryCatch(func(_ context.Context) func() ([]byte, error) {
|
||||
return func() ([]byte, error) {
|
||||
_, err := w.Write(data)
|
||||
return data, err
|
||||
|
92
context/readerioeither/generic/bind.go
Normal file
92
context/readerioeither/generic/bind.go
Normal 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,
|
||||
)
|
||||
}
|
@@ -182,10 +182,7 @@ func withCancelCauseFunc[
|
||||
ma,
|
||||
IOE.Swap[GIOA, func() E.Either[A, error]],
|
||||
IOE.ChainFirstIOK[func() E.Either[A, error], func() any](func(err error) func() any {
|
||||
return IO.MakeIO[func() any](func() any {
|
||||
cancel(err)
|
||||
return nil
|
||||
})
|
||||
return IO.FromImpure[func() any](func() { cancel(err) })
|
||||
}),
|
||||
IOE.Swap[func() E.Either[A, error], GIOA],
|
||||
)
|
||||
|
@@ -166,3 +166,289 @@ func SequenceRecord[K comparable,
|
||||
|
||||
return MonadTraverseRecord[K, GAS, GRAS](ma, F.Identity[GRA])
|
||||
}
|
||||
|
||||
// MonadTraverseArraySeq transforms an array
|
||||
func MonadTraverseArraySeq[
|
||||
AS ~[]A,
|
||||
GRBS ~func(context.Context) GIOBS,
|
||||
GRB ~func(context.Context) GIOB,
|
||||
GIOBS ~func() E.Either[error, BS],
|
||||
GIOB ~func() E.Either[error, B],
|
||||
BS ~[]B,
|
||||
A, B any](as AS, f func(A) GRB) GRBS {
|
||||
|
||||
return RA.MonadTraverse[AS](
|
||||
Of[GRBS, GIOBS, BS],
|
||||
Map[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GIOBS, func() E.Either[error, func(B) BS], BS, func(B) BS],
|
||||
ApSeq[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GRB],
|
||||
|
||||
as, f,
|
||||
)
|
||||
}
|
||||
|
||||
// TraverseArraySeq transforms an array
|
||||
func TraverseArraySeq[
|
||||
AS ~[]A,
|
||||
GRBS ~func(context.Context) GIOBS,
|
||||
GRB ~func(context.Context) GIOB,
|
||||
GIOBS ~func() E.Either[error, BS],
|
||||
GIOB ~func() E.Either[error, B],
|
||||
BS ~[]B,
|
||||
A, B any](f func(A) GRB) func(AS) GRBS {
|
||||
|
||||
return RA.Traverse[AS](
|
||||
Of[GRBS, GIOBS, BS],
|
||||
Map[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GIOBS, func() E.Either[error, func(B) BS], BS, func(B) BS],
|
||||
ApSeq[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GRB],
|
||||
|
||||
f,
|
||||
)
|
||||
}
|
||||
|
||||
// TraverseArrayWithIndexSeq transforms an array
|
||||
func TraverseArrayWithIndexSeq[
|
||||
AS ~[]A,
|
||||
GRBS ~func(context.Context) GIOBS,
|
||||
GRB ~func(context.Context) GIOB,
|
||||
GIOBS ~func() E.Either[error, BS],
|
||||
GIOB ~func() E.Either[error, B],
|
||||
BS ~[]B,
|
||||
A, B any](f func(int, A) GRB) func(AS) GRBS {
|
||||
|
||||
return RA.TraverseWithIndex[AS](
|
||||
Of[GRBS, GIOBS, BS],
|
||||
Map[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GIOBS, func() E.Either[error, func(B) BS], BS, func(B) BS],
|
||||
ApSeq[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GRB],
|
||||
|
||||
f,
|
||||
)
|
||||
}
|
||||
|
||||
// SequenceArraySeq converts a homogeneous sequence of either into an either of sequence
|
||||
func SequenceArraySeq[
|
||||
AS ~[]A,
|
||||
GAS ~[]GRA,
|
||||
GRAS ~func(context.Context) GIOAS,
|
||||
GRA ~func(context.Context) GIOA,
|
||||
GIOAS ~func() E.Either[error, AS],
|
||||
GIOA ~func() E.Either[error, A],
|
||||
A any](ma GAS) GRAS {
|
||||
|
||||
return MonadTraverseArraySeq[GAS, GRAS](ma, F.Identity[GRA])
|
||||
}
|
||||
|
||||
// MonadTraverseRecordSeq transforms a record
|
||||
func MonadTraverseRecordSeq[K comparable,
|
||||
AS ~map[K]A,
|
||||
GRBS ~func(context.Context) GIOBS,
|
||||
GRB ~func(context.Context) GIOB,
|
||||
GIOBS ~func() E.Either[error, BS],
|
||||
GIOB ~func() E.Either[error, B],
|
||||
BS ~map[K]B,
|
||||
|
||||
A, B any](ma AS, f func(A) GRB) GRBS {
|
||||
|
||||
return RR.MonadTraverse[AS](
|
||||
Of[GRBS, GIOBS, BS],
|
||||
Map[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GIOBS, func() E.Either[error, func(B) BS], BS, func(B) BS],
|
||||
ApSeq[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GRB],
|
||||
|
||||
ma, f,
|
||||
)
|
||||
}
|
||||
|
||||
// TraverseRecordSeq transforms a record
|
||||
func TraverseRecordSeq[K comparable,
|
||||
AS ~map[K]A,
|
||||
GRBS ~func(context.Context) GIOBS,
|
||||
GRB ~func(context.Context) GIOB,
|
||||
GIOBS ~func() E.Either[error, BS],
|
||||
GIOB ~func() E.Either[error, B],
|
||||
BS ~map[K]B,
|
||||
|
||||
A, B any](f func(A) GRB) func(AS) GRBS {
|
||||
|
||||
return RR.Traverse[AS](
|
||||
Of[GRBS, GIOBS, BS],
|
||||
Map[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GIOBS, func() E.Either[error, func(B) BS], BS, func(B) BS],
|
||||
ApSeq[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GRB],
|
||||
|
||||
f,
|
||||
)
|
||||
}
|
||||
|
||||
// TraverseRecordWithIndexSeq transforms a record
|
||||
func TraverseRecordWithIndexSeq[K comparable,
|
||||
AS ~map[K]A,
|
||||
GRBS ~func(context.Context) GIOBS,
|
||||
GRB ~func(context.Context) GIOB,
|
||||
GIOBS ~func() E.Either[error, BS],
|
||||
GIOB ~func() E.Either[error, B],
|
||||
BS ~map[K]B,
|
||||
|
||||
A, B any](f func(K, A) GRB) func(AS) GRBS {
|
||||
|
||||
return RR.TraverseWithIndex[AS](
|
||||
Of[GRBS, GIOBS, BS],
|
||||
Map[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GIOBS, func() E.Either[error, func(B) BS], BS, func(B) BS],
|
||||
ApSeq[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GRB],
|
||||
|
||||
f,
|
||||
)
|
||||
}
|
||||
|
||||
// SequenceRecordSeq converts a homogeneous sequence of either into an either of sequence
|
||||
func SequenceRecordSeq[K comparable,
|
||||
AS ~map[K]A,
|
||||
GAS ~map[K]GRA,
|
||||
GRAS ~func(context.Context) GIOAS,
|
||||
GRA ~func(context.Context) GIOA,
|
||||
GIOAS ~func() E.Either[error, AS],
|
||||
GIOA ~func() E.Either[error, A],
|
||||
A any](ma GAS) GRAS {
|
||||
|
||||
return MonadTraverseRecordSeq[K, GAS, GRAS](ma, F.Identity[GRA])
|
||||
}
|
||||
|
||||
// MonadTraverseArrayPar transforms an array
|
||||
func MonadTraverseArrayPar[
|
||||
AS ~[]A,
|
||||
GRBS ~func(context.Context) GIOBS,
|
||||
GRB ~func(context.Context) GIOB,
|
||||
GIOBS ~func() E.Either[error, BS],
|
||||
GIOB ~func() E.Either[error, B],
|
||||
BS ~[]B,
|
||||
A, B any](as AS, f func(A) GRB) GRBS {
|
||||
|
||||
return RA.MonadTraverse[AS](
|
||||
Of[GRBS, GIOBS, BS],
|
||||
Map[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GIOBS, func() E.Either[error, func(B) BS], BS, func(B) BS],
|
||||
ApPar[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GRB],
|
||||
|
||||
as, f,
|
||||
)
|
||||
}
|
||||
|
||||
// TraverseArrayPar transforms an array
|
||||
func TraverseArrayPar[
|
||||
AS ~[]A,
|
||||
GRBS ~func(context.Context) GIOBS,
|
||||
GRB ~func(context.Context) GIOB,
|
||||
GIOBS ~func() E.Either[error, BS],
|
||||
GIOB ~func() E.Either[error, B],
|
||||
BS ~[]B,
|
||||
A, B any](f func(A) GRB) func(AS) GRBS {
|
||||
|
||||
return RA.Traverse[AS](
|
||||
Of[GRBS, GIOBS, BS],
|
||||
Map[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GIOBS, func() E.Either[error, func(B) BS], BS, func(B) BS],
|
||||
ApPar[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GRB],
|
||||
|
||||
f,
|
||||
)
|
||||
}
|
||||
|
||||
// TraverseArrayWithIndexPar transforms an array
|
||||
func TraverseArrayWithIndexPar[
|
||||
AS ~[]A,
|
||||
GRBS ~func(context.Context) GIOBS,
|
||||
GRB ~func(context.Context) GIOB,
|
||||
GIOBS ~func() E.Either[error, BS],
|
||||
GIOB ~func() E.Either[error, B],
|
||||
BS ~[]B,
|
||||
A, B any](f func(int, A) GRB) func(AS) GRBS {
|
||||
|
||||
return RA.TraverseWithIndex[AS](
|
||||
Of[GRBS, GIOBS, BS],
|
||||
Map[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GIOBS, func() E.Either[error, func(B) BS], BS, func(B) BS],
|
||||
ApPar[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GRB],
|
||||
|
||||
f,
|
||||
)
|
||||
}
|
||||
|
||||
// SequenceArrayPar converts a homogeneous sequence of either into an either of sequence
|
||||
func SequenceArrayPar[
|
||||
AS ~[]A,
|
||||
GAS ~[]GRA,
|
||||
GRAS ~func(context.Context) GIOAS,
|
||||
GRA ~func(context.Context) GIOA,
|
||||
GIOAS ~func() E.Either[error, AS],
|
||||
GIOA ~func() E.Either[error, A],
|
||||
A any](ma GAS) GRAS {
|
||||
|
||||
return MonadTraverseArrayPar[GAS, GRAS](ma, F.Identity[GRA])
|
||||
}
|
||||
|
||||
// MonadTraverseRecordPar transforms a record
|
||||
func MonadTraverseRecordPar[K comparable,
|
||||
AS ~map[K]A,
|
||||
GRBS ~func(context.Context) GIOBS,
|
||||
GRB ~func(context.Context) GIOB,
|
||||
GIOBS ~func() E.Either[error, BS],
|
||||
GIOB ~func() E.Either[error, B],
|
||||
BS ~map[K]B,
|
||||
|
||||
A, B any](ma AS, f func(A) GRB) GRBS {
|
||||
|
||||
return RR.MonadTraverse[AS](
|
||||
Of[GRBS, GIOBS, BS],
|
||||
Map[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GIOBS, func() E.Either[error, func(B) BS], BS, func(B) BS],
|
||||
ApPar[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GRB],
|
||||
|
||||
ma, f,
|
||||
)
|
||||
}
|
||||
|
||||
// TraverseRecordPar transforms a record
|
||||
func TraverseRecordPar[K comparable,
|
||||
AS ~map[K]A,
|
||||
GRBS ~func(context.Context) GIOBS,
|
||||
GRB ~func(context.Context) GIOB,
|
||||
GIOBS ~func() E.Either[error, BS],
|
||||
GIOB ~func() E.Either[error, B],
|
||||
BS ~map[K]B,
|
||||
|
||||
A, B any](f func(A) GRB) func(AS) GRBS {
|
||||
|
||||
return RR.Traverse[AS](
|
||||
Of[GRBS, GIOBS, BS],
|
||||
Map[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GIOBS, func() E.Either[error, func(B) BS], BS, func(B) BS],
|
||||
ApPar[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GRB],
|
||||
|
||||
f,
|
||||
)
|
||||
}
|
||||
|
||||
// TraverseRecordWithIndexPar transforms a record
|
||||
func TraverseRecordWithIndexPar[K comparable,
|
||||
AS ~map[K]A,
|
||||
GRBS ~func(context.Context) GIOBS,
|
||||
GRB ~func(context.Context) GIOB,
|
||||
GIOBS ~func() E.Either[error, BS],
|
||||
GIOB ~func() E.Either[error, B],
|
||||
BS ~map[K]B,
|
||||
|
||||
A, B any](f func(K, A) GRB) func(AS) GRBS {
|
||||
|
||||
return RR.TraverseWithIndex[AS](
|
||||
Of[GRBS, GIOBS, BS],
|
||||
Map[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GIOBS, func() E.Either[error, func(B) BS], BS, func(B) BS],
|
||||
ApPar[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GRB],
|
||||
|
||||
f,
|
||||
)
|
||||
}
|
||||
|
||||
// SequenceRecordPar converts a homogeneous sequence of either into an either of sequence
|
||||
func SequenceRecordPar[K comparable,
|
||||
AS ~map[K]A,
|
||||
GAS ~map[K]GRA,
|
||||
GRAS ~func(context.Context) GIOAS,
|
||||
GRA ~func(context.Context) GIOA,
|
||||
GIOAS ~func() E.Either[error, AS],
|
||||
GIOA ~func() E.Either[error, A],
|
||||
A any](ma GAS) GRAS {
|
||||
|
||||
return MonadTraverseRecordPar[K, GAS, GRAS](ma, F.Identity[GRA])
|
||||
}
|
||||
|
72
context/readerioeither/http/builder/builder.go
Normal file
72
context/readerioeither/http/builder/builder.go
Normal 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
|
||||
}),
|
||||
)
|
||||
}
|
59
context/readerioeither/http/builder/builder_test.go
Normal file
59
context/readerioeither/http/builder/builder_test.go
Normal 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())()))
|
||||
}
|
@@ -26,7 +26,7 @@ import (
|
||||
IOE "github.com/IBM/fp-go/ioeither"
|
||||
IOEF "github.com/IBM/fp-go/ioeither/file"
|
||||
J "github.com/IBM/fp-go/json"
|
||||
T "github.com/IBM/fp-go/tuple"
|
||||
P "github.com/IBM/fp-go/pair"
|
||||
)
|
||||
|
||||
type (
|
||||
@@ -79,7 +79,7 @@ func ReadFullResponse(client Client) func(Requester) RIOE.ReaderIOEither[H.FullR
|
||||
IOE.Of[error, 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
|
||||
//
|
||||
// Deprecated: use [ReadJSON] instead
|
||||
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(
|
||||
ReadFullResponse(client),
|
||||
RIOE.ChainFirstEitherK(F.Flow2(
|
||||
H.Response,
|
||||
H.ValidateJsonResponse,
|
||||
)),
|
||||
RIOE.ChainEitherK(F.Flow2(
|
||||
H.Body,
|
||||
J.Unmarshal[A],
|
||||
H.ValidateJSONResponse,
|
||||
)),
|
||||
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]),
|
||||
)
|
||||
}
|
||||
|
@@ -31,7 +31,7 @@ import (
|
||||
)
|
||||
|
||||
type PostItem struct {
|
||||
UserId uint `json:"userId"`
|
||||
UserID uint `json:"userId"`
|
||||
Id uint `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Body string `json:"body"`
|
||||
@@ -84,7 +84,7 @@ func TestSendSingleRequest(t *testing.T) {
|
||||
|
||||
req1 := MakeGetRequest("https://jsonplaceholder.typicode.com/posts/1")
|
||||
|
||||
readItem := ReadJson[PostItem](client)
|
||||
readItem := ReadJSON[PostItem](client)
|
||||
|
||||
resp1 := readItem(req1)
|
||||
|
||||
@@ -112,7 +112,7 @@ func TestSendSingleRequestWithHeaderUnsafe(t *testing.T) {
|
||||
R.Map(setHeaderUnsafe("Content-Type", "text/html")),
|
||||
)
|
||||
|
||||
readItem := ReadJson[PostItem](client)
|
||||
readItem := ReadJSON[PostItem](client)
|
||||
|
||||
resp1 := F.Pipe2(
|
||||
req1,
|
||||
@@ -140,7 +140,7 @@ func TestSendSingleRequestWithHeaderSafe(t *testing.T) {
|
||||
WithHeader("Content-Type", "text/html").
|
||||
Build()
|
||||
|
||||
readItem := ReadJson[PostItem](client)
|
||||
readItem := ReadJSON[PostItem](client)
|
||||
|
||||
response := F.Pipe2(
|
||||
request,
|
||||
|
@@ -48,3 +48,63 @@ func TraverseRecordWithIndex[K comparable, A, B any](f func(K, A) ReaderIOEither
|
||||
func SequenceRecord[K comparable, A any](ma map[K]ReaderIOEither[A]) ReaderIOEither[map[K]A] {
|
||||
return G.SequenceRecord[K, map[K]A, map[K]ReaderIOEither[A], ReaderIOEither[map[K]A]](ma)
|
||||
}
|
||||
|
||||
// TraverseArraySeq uses transforms an array [[]A] into [[]ReaderIOEither[B]] and then resolves that into a [ReaderIOEither[[]B]]
|
||||
func TraverseArraySeq[A, B any](f func(A) ReaderIOEither[B]) func([]A) ReaderIOEither[[]B] {
|
||||
return G.TraverseArraySeq[[]A, ReaderIOEither[[]B]](f)
|
||||
}
|
||||
|
||||
// TraverseArrayWithIndexSeq uses transforms an array [[]A] into [[]ReaderIOEither[B]] and then resolves that into a [ReaderIOEither[[]B]]
|
||||
func TraverseArrayWithIndexSeq[A, B any](f func(int, A) ReaderIOEither[B]) func([]A) ReaderIOEither[[]B] {
|
||||
return G.TraverseArrayWithIndexSeq[[]A, ReaderIOEither[[]B]](f)
|
||||
}
|
||||
|
||||
// SequenceArraySeq converts a homogeneous sequence of either into an either of sequence
|
||||
func SequenceArraySeq[A any](ma []ReaderIOEither[A]) ReaderIOEither[[]A] {
|
||||
return G.SequenceArraySeq[[]A, []ReaderIOEither[A], ReaderIOEither[[]A]](ma)
|
||||
}
|
||||
|
||||
// TraverseRecordSeq uses transforms a record [map[K]A] into [map[K]ReaderIOEither[B]] and then resolves that into a [ReaderIOEither[map[K]B]]
|
||||
func TraverseRecordSeq[K comparable, A, B any](f func(A) ReaderIOEither[B]) func(map[K]A) ReaderIOEither[map[K]B] {
|
||||
return G.TraverseRecordSeq[K, map[K]A, ReaderIOEither[map[K]B]](f)
|
||||
}
|
||||
|
||||
// TraverseRecordWithIndexSeq uses transforms a record [map[K]A] into [map[K]ReaderIOEither[B]] and then resolves that into a [ReaderIOEither[map[K]B]]
|
||||
func TraverseRecordWithIndexSeq[K comparable, A, B any](f func(K, A) ReaderIOEither[B]) func(map[K]A) ReaderIOEither[map[K]B] {
|
||||
return G.TraverseRecordWithIndexSeq[K, map[K]A, ReaderIOEither[map[K]B]](f)
|
||||
}
|
||||
|
||||
// SequenceRecordSeq converts a homogeneous sequence of either into an either of sequence
|
||||
func SequenceRecordSeq[K comparable, A any](ma map[K]ReaderIOEither[A]) ReaderIOEither[map[K]A] {
|
||||
return G.SequenceRecordSeq[K, map[K]A, map[K]ReaderIOEither[A], ReaderIOEither[map[K]A]](ma)
|
||||
}
|
||||
|
||||
// TraverseArrayPar uses transforms an array [[]A] into [[]ReaderIOEither[B]] and then resolves that into a [ReaderIOEither[[]B]]
|
||||
func TraverseArrayPar[A, B any](f func(A) ReaderIOEither[B]) func([]A) ReaderIOEither[[]B] {
|
||||
return G.TraverseArrayPar[[]A, ReaderIOEither[[]B]](f)
|
||||
}
|
||||
|
||||
// TraverseArrayWithIndexPar uses transforms an array [[]A] into [[]ReaderIOEither[B]] and then resolves that into a [ReaderIOEither[[]B]]
|
||||
func TraverseArrayWithIndexPar[A, B any](f func(int, A) ReaderIOEither[B]) func([]A) ReaderIOEither[[]B] {
|
||||
return G.TraverseArrayWithIndexPar[[]A, ReaderIOEither[[]B]](f)
|
||||
}
|
||||
|
||||
// SequenceArrayPar converts a homogeneous sequence of either into an either of sequence
|
||||
func SequenceArrayPar[A any](ma []ReaderIOEither[A]) ReaderIOEither[[]A] {
|
||||
return G.SequenceArrayPar[[]A, []ReaderIOEither[A], ReaderIOEither[[]A]](ma)
|
||||
}
|
||||
|
||||
// TraverseRecordPar uses transforms a record [map[K]A] into [map[K]ReaderIOEither[B]] and then resolves that into a [ReaderIOEither[map[K]B]]
|
||||
func TraverseRecordPar[K comparable, A, B any](f func(A) ReaderIOEither[B]) func(map[K]A) ReaderIOEither[map[K]B] {
|
||||
return G.TraverseRecordPar[K, map[K]A, ReaderIOEither[map[K]B]](f)
|
||||
}
|
||||
|
||||
// TraverseRecordWithIndexPar uses transforms a record [map[K]A] into [map[K]ReaderIOEither[B]] and then resolves that into a [ReaderIOEither[map[K]B]]
|
||||
func TraverseRecordWithIndexPar[K comparable, A, B any](f func(K, A) ReaderIOEither[B]) func(map[K]A) ReaderIOEither[map[K]B] {
|
||||
return G.TraverseRecordWithIndexPar[K, map[K]A, ReaderIOEither[map[K]B]](f)
|
||||
}
|
||||
|
||||
// SequenceRecordPar converts a homogeneous sequence of either into an either of sequence
|
||||
func SequenceRecordPar[K comparable, A any](ma map[K]ReaderIOEither[A]) ReaderIOEither[map[K]A] {
|
||||
return G.SequenceRecordPar[K, map[K]A, map[K]ReaderIOEither[A], ReaderIOEither[map[K]A]](ma)
|
||||
}
|
||||
|
38
di/app.go
Normal file
38
di/app.go
Normal file
@@ -0,0 +1,38 @@
|
||||
// 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 di
|
||||
|
||||
import (
|
||||
DIE "github.com/IBM/fp-go/di/erasure"
|
||||
F "github.com/IBM/fp-go/function"
|
||||
IO "github.com/IBM/fp-go/io"
|
||||
IOE "github.com/IBM/fp-go/ioeither"
|
||||
)
|
||||
|
||||
var (
|
||||
// InjMain is the [InjectionToken] for the main application
|
||||
InjMain = MakeToken[any]("APP")
|
||||
|
||||
// Main is the resolver for the main application
|
||||
Main = Resolve(InjMain)
|
||||
)
|
||||
|
||||
// RunMain runs the main application from a set of [DIE.Provider]s
|
||||
var RunMain = F.Flow3(
|
||||
DIE.MakeInjector,
|
||||
Main,
|
||||
IOE.Fold(IO.Of[error], F.Constant1[any](IO.Of[error](nil))),
|
||||
)
|
43
di/doc.go
Normal file
43
di/doc.go
Normal file
@@ -0,0 +1,43 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package di implements functions and data types supporting dependency injection patterns
|
||||
//
|
||||
// The fundamental building block is the concept of a [Dependency]. This describes the abstract concept of a function, service or value together with its type.
|
||||
// Examples for dependencies can be as simple as configuration values such as the API URL for a service, the current username, a [Dependency] could be the map
|
||||
// of the configuration environment, an http client or as complex as a service interface. Important is that a [Dependency] only defines the concept but
|
||||
// not the implementation.
|
||||
//
|
||||
// The implementation of a [Dependency] is called a [Provider], the dependency of an `API URL` could e.g. be realized by a provider that consults the environment to read the information
|
||||
// or a config file or simply hardcode it.
|
||||
// In many cases the implementation of a [Provider] depends in turn on other [Dependency] (but never directly on other [Provider]s), a provider for an `API URL` that reads
|
||||
// the information from the environment would e.g. depend on a [Dependency] that represents this environment.
|
||||
//
|
||||
// It is the resposibility of the [InjectableFactory] to create an instance of a [Dependency]. All instances are considered singletons. Create an [InjectableFactory] via the [MakeInjector] method. Use [Resolve] to create a strongly typed
|
||||
// factory for a particular [InjectionToken].
|
||||
//
|
||||
// In most cases it is not required to use [InjectableFactory] directly, instead you would create a [Provider] via the [MakeProvider2] method (suffix indicates the number of dependencies). In this call
|
||||
// you give a number of (strongly typed) [Dependency] identifiers and a (strongly typed) factory function for the implementation. The dependency injection framework makes
|
||||
// sure to resolve the dependencies before calling the factory method.
|
||||
//
|
||||
// For convenience purposes it can be helpful to attach a default implementation of a [Dependency]. In this case use the [MakeTokenWithDefault2] method (suffix indicates the number of dependencies)
|
||||
// to define the [InjectionToken].
|
||||
//
|
||||
// [Provider]: [github.com/IBM/fp-go/di/erasure.Provider]
|
||||
// [InjectableFactory]: [github.com/IBM/fp-go/di/erasure.InjectableFactory]
|
||||
// [MakeInjector]: [github.com/IBM/fp-go/di/erasure.MakeInjector]
|
||||
package di
|
||||
|
||||
//go:generate go run .. di --count 10 --filename gen.go
|
@@ -7,7 +7,6 @@
|
||||
//
|
||||
// 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.
|
||||
@@ -115,6 +114,9 @@ func itemProviderFactory(fcts []ProviderFactory) ProviderFactory {
|
||||
}
|
||||
|
||||
// MakeInjector creates an [InjectableFactory] based on a set of [Provider]s
|
||||
//
|
||||
// The resulting [InjectableFactory] can then be used to retrieve service instances given their [Dependency]. The implementation
|
||||
// makes sure to transitively resolve the required dependencies.
|
||||
func MakeInjector(providers []Provider) InjectableFactory {
|
||||
|
||||
type Result = IOE.IOEither[error, any]
|
||||
@@ -125,7 +127,7 @@ func MakeInjector(providers []Provider) InjectableFactory {
|
||||
var resolved sync.Map
|
||||
|
||||
// provide a mapping for all providers
|
||||
factoryById := assembleProviders(providers)
|
||||
factoryByID := assembleProviders(providers)
|
||||
|
||||
// the actual factory, we need lazy initialization
|
||||
var injFct InjectableFactory
|
||||
@@ -147,7 +149,7 @@ func MakeInjector(providers []Provider) InjectableFactory {
|
||||
T.Map2(F.Flow3(
|
||||
Dependency.Id,
|
||||
R.Lookup[ProviderFactory, string],
|
||||
I.Ap[O.Option[ProviderFactory]](factoryById),
|
||||
I.Ap[O.Option[ProviderFactory]](factoryByID),
|
||||
), handleMissingProvider),
|
||||
T.Tupled2(O.MonadGetOrElse[ProviderFactory]),
|
||||
IG.Ap[ProviderFactory](injFct),
|
||||
|
@@ -31,25 +31,29 @@ import (
|
||||
R "github.com/IBM/fp-go/record"
|
||||
)
|
||||
|
||||
type InjectableFactory = func(Dependency) IOE.IOEither[error, any]
|
||||
type ProviderFactory = func(InjectableFactory) IOE.IOEither[error, any]
|
||||
type (
|
||||
// InjectableFactory is a factory function that can create an untyped instance of a service based on its [Dependency] identifier
|
||||
InjectableFactory = func(Dependency) IOE.IOEither[error, any]
|
||||
ProviderFactory = func(InjectableFactory) IOE.IOEither[error, any]
|
||||
|
||||
type paramIndex = map[int]int
|
||||
type paramValue = map[int]any
|
||||
type handler = func(paramIndex) func([]IOE.IOEither[error, any]) IOE.IOEither[error, paramValue]
|
||||
paramIndex = map[int]int
|
||||
paramValue = map[int]any
|
||||
handler = func(paramIndex) func([]IOE.IOEither[error, any]) IOE.IOEither[error, paramValue]
|
||||
mapping = map[int]paramIndex
|
||||
|
||||
type Provider interface {
|
||||
fmt.Stringer
|
||||
// Provides returns the [Dependency] implemented by this provider
|
||||
Provides() Dependency
|
||||
// Factory returns s function that can create an instance of the dependency based on an [InjectableFactory]
|
||||
Factory() ProviderFactory
|
||||
}
|
||||
Provider interface {
|
||||
fmt.Stringer
|
||||
// Provides returns the [Dependency] implemented by this provider
|
||||
Provides() Dependency
|
||||
// Factory returns s function that can create an instance of the dependency based on an [InjectableFactory]
|
||||
Factory() ProviderFactory
|
||||
}
|
||||
|
||||
type provider struct {
|
||||
provides Dependency
|
||||
factory ProviderFactory
|
||||
}
|
||||
provider struct {
|
||||
provides Dependency
|
||||
factory ProviderFactory
|
||||
}
|
||||
)
|
||||
|
||||
func (p *provider) Provides() Dependency {
|
||||
return p.provides
|
||||
@@ -72,11 +76,16 @@ func mapFromToken(idx int, token Dependency) map[int]paramIndex {
|
||||
}
|
||||
|
||||
var (
|
||||
// Empty is the empty array of providers
|
||||
Empty = A.Empty[Provider]()
|
||||
|
||||
mergeTokenMaps = R.UnionMonoid[int](R.UnionLastSemigroup[int, int]())
|
||||
foldDeps = A.FoldMapWithIndex[Dependency](mergeTokenMaps)(mapFromToken)
|
||||
mergeMaps = R.UnionLastMonoid[int, any]()
|
||||
collectParams = R.CollectOrd[any, any](Int.Ord)(F.SK[int, any])
|
||||
|
||||
mapDeps = F.Curry2(A.MonadMap[Dependency, IOE.IOEither[error, any]])
|
||||
|
||||
handlers = map[int]handler{
|
||||
Identity: func(mp paramIndex) func([]IOE.IOEither[error, any]) IOE.IOEither[error, paramValue] {
|
||||
return func(res []IOE.IOEither[error, any]) IOE.IOEither[error, paramValue] {
|
||||
@@ -127,15 +136,13 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
type Mapping = map[int]paramIndex
|
||||
|
||||
func getAt[T any](ar []T) func(idx int) T {
|
||||
return func(idx int) T {
|
||||
return ar[idx]
|
||||
}
|
||||
}
|
||||
|
||||
func handleMapping(mp Mapping) func(res []IOE.IOEither[error, any]) IOE.IOEither[error, []any] {
|
||||
func handleMapping(mp mapping) func(res []IOE.IOEither[error, any]) IOE.IOEither[error, []any] {
|
||||
preFct := F.Pipe1(
|
||||
mp,
|
||||
R.Collect(func(idx int, p paramIndex) func([]IOE.IOEither[error, any]) IOE.IOEither[error, paramValue] {
|
||||
@@ -167,7 +174,7 @@ func MakeProviderFactory(
|
||||
fct func(param ...any) IOE.IOEither[error, any]) ProviderFactory {
|
||||
|
||||
return F.Flow3(
|
||||
F.Curry2(A.MonadMap[Dependency, IOE.IOEither[error, any]])(deps),
|
||||
mapDeps(deps),
|
||||
handleMapping(foldDeps(deps)),
|
||||
IOE.Chain(F.Unvariadic0(fct)),
|
||||
)
|
||||
|
@@ -7,7 +7,6 @@
|
||||
//
|
||||
// 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.
|
||||
|
121
di/provider.go
121
di/provider.go
@@ -12,6 +12,7 @@
|
||||
// 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 di
|
||||
|
||||
import (
|
||||
@@ -21,7 +22,6 @@ import (
|
||||
"github.com/IBM/fp-go/errors"
|
||||
F "github.com/IBM/fp-go/function"
|
||||
IOE "github.com/IBM/fp-go/ioeither"
|
||||
T "github.com/IBM/fp-go/tuple"
|
||||
)
|
||||
|
||||
func lookupAt[T any](idx int, token Dependency[T]) func(params []any) E.Either[error, T] {
|
||||
@@ -32,49 +32,25 @@ func lookupAt[T any](idx int, token Dependency[T]) func(params []any) E.Either[e
|
||||
)
|
||||
}
|
||||
|
||||
func eraseProviderFactory0[R any](f func() IOE.IOEither[error, R]) func(params ...any) IOE.IOEither[error, any] {
|
||||
return func(params ...any) IOE.IOEither[error, any] {
|
||||
func eraseTuple[A, R any](f func(A) IOE.IOEither[error, R]) func(E.Either[error, A]) IOE.IOEither[error, any] {
|
||||
return F.Flow3(
|
||||
IOE.FromEither[error, A],
|
||||
IOE.Chain(f),
|
||||
IOE.Map[error](F.ToAny[R]),
|
||||
)
|
||||
}
|
||||
|
||||
func eraseProviderFactory0[R any](f IOE.IOEither[error, R]) func(params ...any) IOE.IOEither[error, any] {
|
||||
return func(_ ...any) IOE.IOEither[error, any] {
|
||||
return F.Pipe1(
|
||||
f(),
|
||||
IOE.Map[error](F.ToAny[R]),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func eraseProviderFactory1[T1 any, R any](
|
||||
d1 Dependency[T1],
|
||||
f func(T1) IOE.IOEither[error, R]) func(params ...any) IOE.IOEither[error, any] {
|
||||
ft := T.Tupled1(f)
|
||||
t1 := lookupAt[T1](0, d1)
|
||||
return func(params ...any) IOE.IOEither[error, any] {
|
||||
return F.Pipe3(
|
||||
E.SequenceT1(t1(params)),
|
||||
IOE.FromEither[error, T.Tuple1[T1]],
|
||||
IOE.Chain(ft),
|
||||
IOE.Map[error](F.ToAny[R]),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func eraseProviderFactory2[T1, T2 any, R any](
|
||||
d1 Dependency[T1],
|
||||
d2 Dependency[T2],
|
||||
f func(T1, T2) IOE.IOEither[error, R]) func(params ...any) IOE.IOEither[error, any] {
|
||||
ft := T.Tupled2(f)
|
||||
t1 := lookupAt[T1](0, d1)
|
||||
t2 := lookupAt[T2](1, d2)
|
||||
return func(params ...any) IOE.IOEither[error, any] {
|
||||
return F.Pipe3(
|
||||
E.SequenceT2(t1(params), t2(params)),
|
||||
IOE.FromEither[error, T.Tuple2[T1, T2]],
|
||||
IOE.Chain(ft),
|
||||
f,
|
||||
IOE.Map[error](F.ToAny[R]),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func MakeProviderFactory0[R any](
|
||||
fct func() IOE.IOEither[error, R],
|
||||
fct IOE.IOEither[error, R],
|
||||
) DIE.ProviderFactory {
|
||||
return DIE.MakeProviderFactory(
|
||||
A.Empty[DIE.Dependency](),
|
||||
@@ -82,14 +58,14 @@ func MakeProviderFactory0[R any](
|
||||
)
|
||||
}
|
||||
|
||||
// MakeTokenWithDefault0 create a unique `InjectionToken` for a specific type with an attached default provider
|
||||
func MakeTokenWithDefault0[R any](name string, fct func() IOE.IOEither[error, R]) InjectionToken[R] {
|
||||
// MakeTokenWithDefault0 creates a unique [InjectionToken] for a specific type with an attached default [DIE.Provider]
|
||||
func MakeTokenWithDefault0[R any](name string, fct IOE.IOEither[error, R]) InjectionToken[R] {
|
||||
return MakeTokenWithDefault[R](name, MakeProviderFactory0(fct))
|
||||
}
|
||||
|
||||
func MakeProvider0[R any](
|
||||
token InjectionToken[R],
|
||||
fct func() IOE.IOEither[error, R],
|
||||
fct IOE.IOEither[error, R],
|
||||
) DIE.Provider {
|
||||
return DIE.MakeProvider(
|
||||
token,
|
||||
@@ -97,70 +73,7 @@ func MakeProvider0[R any](
|
||||
)
|
||||
}
|
||||
|
||||
func MakeProviderFactory1[T1, R any](
|
||||
d1 Dependency[T1],
|
||||
fct func(T1) IOE.IOEither[error, R],
|
||||
) DIE.ProviderFactory {
|
||||
|
||||
return DIE.MakeProviderFactory(
|
||||
A.From[DIE.Dependency](d1),
|
||||
eraseProviderFactory1(d1, fct),
|
||||
)
|
||||
}
|
||||
|
||||
// MakeTokenWithDefault1 create a unique `InjectionToken` for a specific type with an attached default provider
|
||||
func MakeTokenWithDefault1[T1, R any](name string,
|
||||
d1 Dependency[T1],
|
||||
fct func(T1) IOE.IOEither[error, R]) InjectionToken[R] {
|
||||
return MakeTokenWithDefault[R](name, MakeProviderFactory1(d1, fct))
|
||||
}
|
||||
|
||||
func MakeProvider1[T1, R any](
|
||||
token InjectionToken[R],
|
||||
d1 Dependency[T1],
|
||||
fct func(T1) IOE.IOEither[error, R],
|
||||
) DIE.Provider {
|
||||
|
||||
return DIE.MakeProvider(
|
||||
token,
|
||||
MakeProviderFactory1(d1, fct),
|
||||
)
|
||||
}
|
||||
|
||||
func MakeProviderFactory2[T1, T2, R any](
|
||||
d1 Dependency[T1],
|
||||
d2 Dependency[T2],
|
||||
fct func(T1, T2) IOE.IOEither[error, R],
|
||||
) DIE.ProviderFactory {
|
||||
|
||||
return DIE.MakeProviderFactory(
|
||||
A.From[DIE.Dependency](d1, d2),
|
||||
eraseProviderFactory2(d1, d2, fct),
|
||||
)
|
||||
}
|
||||
|
||||
// MakeTokenWithDefault2 create a unique `InjectionToken` for a specific type with an attached default provider
|
||||
func MakeTokenWithDefault2[T1, T2, R any](name string,
|
||||
d1 Dependency[T1],
|
||||
d2 Dependency[T2],
|
||||
fct func(T1, T2) IOE.IOEither[error, R]) InjectionToken[R] {
|
||||
return MakeTokenWithDefault[R](name, MakeProviderFactory2(d1, d2, fct))
|
||||
}
|
||||
|
||||
func MakeProvider2[T1, T2, R any](
|
||||
token InjectionToken[R],
|
||||
d1 Dependency[T1],
|
||||
d2 Dependency[T2],
|
||||
fct func(T1, T2) IOE.IOEither[error, R],
|
||||
) DIE.Provider {
|
||||
|
||||
return DIE.MakeProvider(
|
||||
token,
|
||||
MakeProviderFactory2(d1, d2, fct),
|
||||
)
|
||||
}
|
||||
|
||||
// ConstProvider simple implementation for a provider with a constant value
|
||||
func ConstProvider[R any](token InjectionToken[R], value R) DIE.Provider {
|
||||
return MakeProvider0[R](token, F.Constant(IOE.Of[error](value)))
|
||||
return MakeProvider0[R](token, IOE.Of[error](value))
|
||||
}
|
||||
|
@@ -12,6 +12,7 @@
|
||||
// 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 di
|
||||
|
||||
import (
|
||||
@@ -38,12 +39,10 @@ func TestSimpleProvider(t *testing.T) {
|
||||
|
||||
var staticCount int
|
||||
|
||||
staticValue := func(value string) func() IOE.IOEither[error, string] {
|
||||
return func() IOE.IOEither[error, string] {
|
||||
return func() E.Either[error, string] {
|
||||
staticCount++
|
||||
return E.Of[error](fmt.Sprintf("Static based on [%s], at [%s]", value, time.Now()))
|
||||
}
|
||||
staticValue := func(value string) IOE.IOEither[error, string] {
|
||||
return func() E.Either[error, string] {
|
||||
staticCount++
|
||||
return E.Of[error](fmt.Sprintf("Static based on [%s], at [%s]", value, time.Now()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,12 +81,10 @@ func TestOptionalProvider(t *testing.T) {
|
||||
|
||||
var staticCount int
|
||||
|
||||
staticValue := func(value string) func() IOE.IOEither[error, string] {
|
||||
return func() IOE.IOEither[error, string] {
|
||||
return func() E.Either[error, string] {
|
||||
staticCount++
|
||||
return E.Of[error](fmt.Sprintf("Static based on [%s], at [%s]", value, time.Now()))
|
||||
}
|
||||
staticValue := func(value string) IOE.IOEither[error, string] {
|
||||
return func() E.Either[error, string] {
|
||||
staticCount++
|
||||
return E.Of[error](fmt.Sprintf("Static based on [%s], at [%s]", value, time.Now()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,12 +179,10 @@ func TestEagerAndLazyProvider(t *testing.T) {
|
||||
|
||||
var staticCount int
|
||||
|
||||
staticValue := func(value string) func() IOE.IOEither[error, string] {
|
||||
return func() IOE.IOEither[error, string] {
|
||||
return func() E.Either[error, string] {
|
||||
staticCount++
|
||||
return E.Of[error](fmt.Sprintf("Static based on [%s], at [%s]", value, time.Now()))
|
||||
}
|
||||
staticValue := func(value string) IOE.IOEither[error, string] {
|
||||
return func() E.Either[error, string] {
|
||||
staticCount++
|
||||
return E.Of[error](fmt.Sprintf("Static based on [%s], at [%s]", value, time.Now()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -307,7 +302,7 @@ func TestTokenWithDefaultProvider(t *testing.T) {
|
||||
// token without a default
|
||||
injToken1 := MakeToken[string]("Token1")
|
||||
// token with a default
|
||||
injToken2 := MakeTokenWithDefault0("Token2", F.Constant(IOE.Of[error]("Carsten")))
|
||||
injToken2 := MakeTokenWithDefault0("Token2", IOE.Of[error]("Carsten"))
|
||||
// dependency
|
||||
injToken3 := MakeToken[string]("Token3")
|
||||
|
||||
@@ -330,7 +325,7 @@ func TestTokenWithDefaultProvider(t *testing.T) {
|
||||
|
||||
func TestTokenWithDefaultProviderAndOverride(t *testing.T) {
|
||||
// token with a default
|
||||
injToken2 := MakeTokenWithDefault0("Token2", F.Constant(IOE.Of[error]("Carsten")))
|
||||
injToken2 := MakeTokenWithDefault0("Token2", IOE.Of[error]("Carsten"))
|
||||
// dependency
|
||||
injToken3 := MakeToken[string]("Token3")
|
||||
|
||||
|
56
di/token.go
56
di/token.go
@@ -42,20 +42,21 @@ type InjectionToken[T any] interface {
|
||||
// Identity idenifies this dependency as a mandatory, required dependency, it will be resolved eagerly and injected as `T`.
|
||||
// If the dependency cannot be resolved, the resolution process fails
|
||||
Identity() Dependency[T]
|
||||
// Option identifies this dependency as optional, it will be resolved eagerly and injected as `O.Option[T]`.
|
||||
// If the dependency cannot be resolved, the resolution process continues and the dependency is represented as `O.None[T]`
|
||||
// Option identifies this dependency as optional, it will be resolved eagerly and injected as [O.Option[T]].
|
||||
// If the dependency cannot be resolved, the resolution process continues and the dependency is represented as [O.None[T]]
|
||||
Option() Dependency[O.Option[T]]
|
||||
// IOEither identifies this dependency as mandatory but it will be resolved lazily as a `IOE.IOEither[error, T]`. This
|
||||
// IOEither identifies this dependency as mandatory but it will be resolved lazily as a [IOE.IOEither[error, T]]. This
|
||||
// value is memoized to make sure the dependency is a singleton.
|
||||
// If the dependency cannot be resolved, the resolution process fails
|
||||
IOEither() Dependency[IOE.IOEither[error, T]]
|
||||
// IOOption identifies this dependency as optional but it will be resolved lazily as a `IOO.IOOption[T]`. This
|
||||
// IOOption identifies this dependency as optional but it will be resolved lazily as a [IOO.IOOption[T]]. This
|
||||
// value is memoized to make sure the dependency is a singleton.
|
||||
// If the dependency cannot be resolved, the resolution process continues and the dependency is represented as the none value.
|
||||
IOOption() Dependency[IOO.IOOption[T]]
|
||||
}
|
||||
|
||||
// MultiInjectionToken uniquely identifies a dependency by giving it an Id, Type and name.
|
||||
// MultiInjectionToken uniquely identifies a dependency by giving it an Id, Type and name that can have multiple implementations.
|
||||
// Implementations are provided via the [MultiInjectionToken.Item] injection token.
|
||||
type MultiInjectionToken[T any] interface {
|
||||
// Container returns the injection token used to request an array of all provided items
|
||||
Container() InjectionToken[[]T]
|
||||
@@ -64,34 +65,38 @@ type MultiInjectionToken[T any] interface {
|
||||
}
|
||||
|
||||
// makeID creates a generator of unique string IDs
|
||||
func makeId() IO.IO[string] {
|
||||
func makeID() IO.IO[string] {
|
||||
var count atomic.Int64
|
||||
return IO.MakeIO(func() string {
|
||||
return strconv.FormatInt(count.Add(1), 16)
|
||||
})
|
||||
}
|
||||
|
||||
// genId is the common generator of unique string IDs
|
||||
var genId = makeId()
|
||||
// genID is the common generator of unique string IDs
|
||||
var genID = makeID()
|
||||
|
||||
type token[T any] struct {
|
||||
type tokenBase struct {
|
||||
name string
|
||||
id string
|
||||
flag int
|
||||
toType func(val any) E.Either[error, T]
|
||||
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 {
|
||||
return t.id
|
||||
return t.base.id
|
||||
}
|
||||
|
||||
func (t *token[T]) Flag() int {
|
||||
return t.flag
|
||||
return t.base.flag
|
||||
}
|
||||
|
||||
func (t *token[T]) String() string {
|
||||
return t.name
|
||||
return t.base.name
|
||||
}
|
||||
|
||||
func (t *token[T]) Unerase(val any) E.Either[error, T] {
|
||||
@@ -99,11 +104,14 @@ func (t *token[T]) Unerase(val any) E.Either[error, T] {
|
||||
}
|
||||
|
||||
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] {
|
||||
return &token[T]{name, id, typ, unerase, providerFactory}
|
||||
return &token[T]{makeTokenBase(name, id, typ, providerFactory), unerase}
|
||||
}
|
||||
|
||||
type injectionToken[T any] struct {
|
||||
@@ -135,7 +143,7 @@ func (i *injectionToken[T]) IOOption() Dependency[IOO.IOOption[T]] {
|
||||
}
|
||||
|
||||
func (i *injectionToken[T]) ProviderFactory() O.Option[DIE.ProviderFactory] {
|
||||
return i.providerFactory
|
||||
return i.base.providerFactory
|
||||
}
|
||||
|
||||
func (m *multiInjectionToken[T]) Container() InjectionToken[[]T] {
|
||||
@@ -146,31 +154,31 @@ func (m *multiInjectionToken[T]) Item() InjectionToken[T] {
|
||||
return m.item
|
||||
}
|
||||
|
||||
// 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] {
|
||||
id := genId()
|
||||
id := genID()
|
||||
toIdentity := toType[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[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 create a unique `InjectionToken` for a specific type
|
||||
// MakeToken create a unique [InjectionToken] for a specific type
|
||||
func MakeToken[T any](name string) InjectionToken[T] {
|
||||
return makeInjectionToken[T](name, O.None[DIE.ProviderFactory]())
|
||||
}
|
||||
|
||||
// MakeToken create a unique `InjectionToken` for a specific type
|
||||
// MakeToken create a unique [InjectionToken] for a specific type
|
||||
func MakeTokenWithDefault[T any](name string, providerFactory DIE.ProviderFactory) InjectionToken[T] {
|
||||
return makeInjectionToken[T](name, O.Of(providerFactory))
|
||||
}
|
||||
|
||||
// MakeMultiToken creates a [MultiInjectionToken]
|
||||
func MakeMultiToken[T any](name string) MultiInjectionToken[T] {
|
||||
id := genId()
|
||||
id := genID()
|
||||
toItem := toType[T]()
|
||||
toContainer := toArrayType(toItem)
|
||||
containerName := fmt.Sprintf("Container[%s]", name)
|
||||
@@ -179,14 +187,14 @@ func MakeMultiToken[T any](name string) MultiInjectionToken[T] {
|
||||
providerFactory := O.None[DIE.ProviderFactory]()
|
||||
// container
|
||||
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[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),
|
||||
}
|
||||
// item
|
||||
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[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),
|
||||
|
16
di/utils.go
16
di/utils.go
@@ -12,6 +12,7 @@
|
||||
// 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 di
|
||||
|
||||
import (
|
||||
@@ -24,6 +25,13 @@ import (
|
||||
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]
|
||||
func asDependency[T DIE.Dependency](t T) DIE.Dependency {
|
||||
return t
|
||||
@@ -37,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]
|
||||
func toOptionType[T any](item func(any) E.Either[error, T]) func(t any) E.Either[error, O.Option[T]] {
|
||||
return F.Flow2(
|
||||
toType[O.Option[any]](),
|
||||
toOptionAny,
|
||||
E.Chain(O.Fold(
|
||||
F.Nullary2(O.None[T], E.Of[error, O.Option[T]]),
|
||||
F.Flow2(
|
||||
@@ -51,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]
|
||||
func toIOEitherType[T any](item func(any) E.Either[error, T]) func(t any) E.Either[error, IOE.IOEither[error, T]] {
|
||||
return F.Flow2(
|
||||
toType[IOE.IOEither[error, any]](),
|
||||
toIOEitherAny,
|
||||
E.Map[error](IOE.ChainEitherK(item)),
|
||||
)
|
||||
}
|
||||
@@ -59,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]
|
||||
func toIOOptionType[T any](item func(any) E.Either[error, T]) func(t any) E.Either[error, IOO.IOOption[T]] {
|
||||
return F.Flow2(
|
||||
toType[IOO.IOOption[any]](),
|
||||
toIOOptionAny,
|
||||
E.Map[error](IOO.ChainOptionK(F.Flow2(
|
||||
item,
|
||||
E.ToOption[error, T],
|
||||
@@ -70,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
|
||||
func toArrayType[T any](item func(any) E.Either[error, T]) func(t any) E.Either[error, []T] {
|
||||
return F.Flow2(
|
||||
toType[[]any](),
|
||||
toArrayAny,
|
||||
E.Chain(E.TraverseArray(item)),
|
||||
)
|
||||
}
|
||||
|
@@ -12,6 +12,7 @@
|
||||
// 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 di
|
||||
|
||||
import (
|
||||
|
@@ -61,14 +61,14 @@ func SequenceArray[E, A any](ma []Either[E, A]) Either[E, []A] {
|
||||
return SequenceArrayG[[]A](ma)
|
||||
}
|
||||
|
||||
// CompactArrayG discards the none values and keeps the some values
|
||||
// CompactArrayG discards the none values and keeps the right values
|
||||
func CompactArrayG[A1 ~[]Either[E, A], A2 ~[]A, E, A any](fa A1) A2 {
|
||||
return RA.Reduce(fa, func(out A2, value Either[E, A]) A2 {
|
||||
return MonadFold(value, F.Constant1[E](out), F.Bind1st(RA.Append[A2, A], out))
|
||||
}, make(A2, len(fa)))
|
||||
}, make(A2, 0, len(fa)))
|
||||
}
|
||||
|
||||
// CompactArray discards the none values and keeps the some values
|
||||
// CompactArray discards the none values and keeps the right values
|
||||
func CompactArray[E, A any](fa []Either[E, A]) []A {
|
||||
return CompactArrayG[[]Either[E, A], []A](fa)
|
||||
}
|
||||
|
18
either/array_test.go
Normal file
18
either/array_test.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package either
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestCompactArray(t *testing.T) {
|
||||
ar := []Either[string, string]{
|
||||
Of[string]("ok"),
|
||||
Left[string]("err"),
|
||||
Of[string]("ok"),
|
||||
}
|
||||
|
||||
res := CompactArray(ar)
|
||||
assert.Equal(t, 2, len(res))
|
||||
}
|
89
either/bind.go
Normal file
89
either/bind.go
Normal 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
56
either/bind_test.go
Normal 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"))
|
||||
}
|
@@ -20,30 +20,45 @@ import (
|
||||
)
|
||||
|
||||
type (
|
||||
// Either defines a data structure that logically holds either an E or an A. The flag discriminates the cases
|
||||
Either[E, A any] struct {
|
||||
either struct {
|
||||
isLeft bool
|
||||
left E
|
||||
right A
|
||||
value any
|
||||
}
|
||||
|
||||
// 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
|
||||
func (s Either[E, A]) String() string {
|
||||
//
|
||||
// go:noinline
|
||||
func eitherString(s *either) string {
|
||||
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
|
||||
func (s Either[E, A]) Format(f fmt.State, c rune) {
|
||||
switch c {
|
||||
case 's':
|
||||
fmt.Fprint(f, s.String())
|
||||
default:
|
||||
fmt.Fprint(f, s.String())
|
||||
}
|
||||
eitherFormat((*either)(&s), f, c)
|
||||
}
|
||||
|
||||
// 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.
|
||||
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.
|
||||
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
|
||||
func MonadFold[E, A, B any](ma Either[E, A], onLeft func(e E) B, onRight func(a A) B) B {
|
||||
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
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@@ -22,6 +22,7 @@ package either
|
||||
import (
|
||||
E "github.com/IBM/fp-go/errors"
|
||||
F "github.com/IBM/fp-go/function"
|
||||
C "github.com/IBM/fp-go/internal/chain"
|
||||
FC "github.com/IBM/fp-go/internal/functor"
|
||||
L "github.com/IBM/fp-go/lazy"
|
||||
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] {
|
||||
return F.Bind2nd(MonadMapTo[E, A, B], b)
|
||||
return Map[E](F.Constant1[A](b))
|
||||
}
|
||||
|
||||
func MonadMapLeft[E, A, B any](fa Either[E, A], f func(E) B) Either[B, A] {
|
||||
return MonadFold(fa, F.Flow2(f, Left[A, B]), Right[B, A])
|
||||
func MonadMapLeft[E1, A, E2 any](fa Either[E1, A], f func(E1) E2) Either[E2, A] {
|
||||
return MonadFold(fa, F.Flow2(f, Left[A, E2]), Right[E2, A])
|
||||
}
|
||||
|
||||
func Map[E, A, B any](f func(a A) B) func(fa Either[E, A]) Either[E, B] {
|
||||
return Chain(F.Flow2(f, Right[E, B]))
|
||||
}
|
||||
|
||||
func MapLeft[E, A, B any](f func(E) B) func(fa Either[E, A]) Either[B, A] {
|
||||
return F.Bind2nd(MonadMapLeft[E, A, B], f)
|
||||
// MapLeft applies a mapping function to the error channel
|
||||
func MapLeft[A, E1, E2 any](f func(E1) E2) func(fa Either[E1, A]) Either[E2, A] {
|
||||
return Fold(F.Flow2(f, Left[A, E2]), Right[E2, A])
|
||||
}
|
||||
|
||||
func MonadChain[E, A, B any](fa Either[E, A], f func(a A) Either[E, B]) Either[E, B] {
|
||||
@@ -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] {
|
||||
return MonadChain(ma, func(a A) Either[E, A] {
|
||||
return MonadMap(f(a), F.Constant1[B](a))
|
||||
})
|
||||
return C.MonadChainFirst(
|
||||
MonadChain[E, A, A],
|
||||
MonadMap[E, B, A],
|
||||
ma,
|
||||
f,
|
||||
)
|
||||
}
|
||||
|
||||
func MonadChainTo[E, A, 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
|
||||
}
|
||||
|
||||
@@ -104,16 +109,20 @@ func ChainOptionK[A, B, E any](onNone func() E) func(func(A) O.Option[B]) func(E
|
||||
}
|
||||
}
|
||||
|
||||
func ChainTo[E, A, B any](mb Either[E, B]) func(Either[E, A]) Either[E, B] {
|
||||
return F.Bind2nd(MonadChainTo[E, A, B], mb)
|
||||
func ChainTo[A, E, B any](mb Either[E, B]) func(Either[E, A]) Either[E, B] {
|
||||
return F.Constant1[Either[E, A]](mb)
|
||||
}
|
||||
|
||||
func Chain[E, A, B any](f func(a A) Either[E, B]) func(Either[E, A]) Either[E, B] {
|
||||
return F.Bind2nd(MonadChain[E, A, B], f)
|
||||
return Fold(Left[B, E], f)
|
||||
}
|
||||
|
||||
func ChainFirst[E, A, B any](f func(a A) Either[E, B]) func(Either[E, A]) Either[E, A] {
|
||||
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] {
|
||||
@@ -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] {
|
||||
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] {
|
||||
|
@@ -17,6 +17,7 @@ package either
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
F "github.com/IBM/fp-go/function"
|
||||
@@ -26,12 +27,6 @@ import (
|
||||
"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) {
|
||||
err := errors.New("Some error")
|
||||
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, 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())
|
||||
}
|
||||
|
@@ -34,7 +34,7 @@ func Eq[E, A any](e EQ.Eq[E], a EQ.Eq[A]) EQ.Eq[Either[E, A]] {
|
||||
return EQ.FromEquals(F.Uncurry2(fld))
|
||||
}
|
||||
|
||||
// FromStrictEquals constructs an `Eq` from the canonical comparison function
|
||||
// FromStrictEquals constructs an [EQ.Eq] from the canonical comparison function
|
||||
func FromStrictEquals[E, A comparable]() EQ.Eq[Either[E, A]] {
|
||||
return Eq(EQ.FromStrictEquals[E](), EQ.FromStrictEquals[A]())
|
||||
}
|
||||
|
@@ -48,11 +48,11 @@ func ExampleEither_creation() {
|
||||
fmt.Println(rightFromPred)
|
||||
|
||||
// Output:
|
||||
// Left[*errors.errorString, string](some error)
|
||||
// Right[<nil>, string](value)
|
||||
// Left[*errors.errorString, *string](value was nil)
|
||||
// Left[*errors.errorString](some error)
|
||||
// Right[string](value)
|
||||
// Left[*errors.errorString](value was nil)
|
||||
// true
|
||||
// Left[*errors.errorString, int](3 is an odd number)
|
||||
// Right[<nil>, int](4)
|
||||
// Left[*errors.errorString](3 is an odd number)
|
||||
// Right[int](4)
|
||||
|
||||
}
|
||||
|
@@ -53,8 +53,8 @@ func ExampleEither_extraction() {
|
||||
fmt.Println(doubleFromRightBis)
|
||||
|
||||
// Output:
|
||||
// Left[*errors.errorString, int](Division by Zero!)
|
||||
// Right[<nil>, int](10)
|
||||
// Left[*errors.errorString](Division by Zero!)
|
||||
// Right[int](10)
|
||||
// 0
|
||||
// 10
|
||||
// 0
|
||||
|
@@ -41,7 +41,7 @@ func Logger[E, A any](loggers ...*log.Logger) func(string) func(Either[E, A]) Ei
|
||||
return func(ma Either[E, A]) Either[E, A] {
|
||||
return F.Pipe1(
|
||||
delegate(ma),
|
||||
ChainTo[E, A](ma),
|
||||
ChainTo[A](ma),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
43
either/monad.go
Normal file
43
either/monad.go
Normal 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]{}
|
||||
}
|
@@ -19,38 +19,17 @@ import (
|
||||
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]
|
||||
func Traverse[A, E, B, HKTB, HKTRB any](
|
||||
mof func(Either[E, B]) HKTRB,
|
||||
mmap func(func(B) Either[E, B]) func(HKTB) 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 F.Bind2nd(delegate, f)
|
||||
return Fold(left, F.Flow2(f, right))
|
||||
}
|
||||
}
|
||||
|
||||
|
30
endomorphism/curry.go
Normal file
30
endomorphism/curry.go
Normal 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 endomorphism
|
||||
|
||||
import (
|
||||
G "github.com/IBM/fp-go/endomorphism/generic"
|
||||
)
|
||||
|
||||
// Curry2 curries a binary function
|
||||
func Curry2[FCT ~func(T0, T1) T1, T0, T1 any](f FCT) func(T0) Endomorphism[T1] {
|
||||
return G.Curry2[Endomorphism[T1]](f)
|
||||
}
|
||||
|
||||
// Curry3 curries a ternary function
|
||||
func Curry3[FCT ~func(T0, T1, T2) T2, T0, T1, T2 any](f FCT) func(T0) func(T1) Endomorphism[T2] {
|
||||
return G.Curry3[Endomorphism[T2]](f)
|
||||
}
|
36
endomorphism/endo.go
Normal file
36
endomorphism/endo.go
Normal file
@@ -0,0 +1,36 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package endomorphism
|
||||
|
||||
import (
|
||||
G "github.com/IBM/fp-go/endomorphism/generic"
|
||||
)
|
||||
|
||||
func MonadAp[A any](fab Endomorphism[A], fa A) A {
|
||||
return G.MonadAp[Endomorphism[A]](fab, fa)
|
||||
}
|
||||
|
||||
func Ap[A any](fa A) func(Endomorphism[A]) A {
|
||||
return G.Ap[Endomorphism[A]](fa)
|
||||
}
|
||||
|
||||
func MonadChain[A any](ma Endomorphism[A], f Endomorphism[A]) Endomorphism[A] {
|
||||
return G.MonadChain[Endomorphism[A]](ma, f)
|
||||
}
|
||||
|
||||
func Chain[A any](f Endomorphism[A]) Endomorphism[Endomorphism[A]] {
|
||||
return G.Chain[Endomorphism[Endomorphism[A]], Endomorphism[A], A](f)
|
||||
}
|
36
endomorphism/generic/curry.go
Normal file
36
endomorphism/generic/curry.go
Normal file
@@ -0,0 +1,36 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package generic
|
||||
|
||||
// Curry2 is a duplicate of [F.Curry2] but because of the type system it's not compatible otherwise
|
||||
func Curry2[GT1 ~func(T1) T1, FCT ~func(T0, T1) T1, T0, T1 any](f FCT) func(T0) GT1 {
|
||||
return func(t0 T0) GT1 {
|
||||
return func(t1 T1) T1 {
|
||||
return f(t0, t1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Curry2 is a duplicate of [F.Curry2] but because of the type system it's not compatible otherwise
|
||||
func Curry3[GT2 ~func(T2) T2, FCT ~func(T0, T1, T2) T2, T0, T1, T2 any](f FCT) func(T0) func(T1) GT2 {
|
||||
return func(t0 T0) func(T1) GT2 {
|
||||
return func(t1 T1) GT2 {
|
||||
return func(t2 T2) T2 {
|
||||
return f(t0, t1, t2)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
37
endomorphism/generic/endo.go
Normal file
37
endomorphism/generic/endo.go
Normal file
@@ -0,0 +1,37 @@
|
||||
// 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 (
|
||||
F "github.com/IBM/fp-go/function"
|
||||
I "github.com/IBM/fp-go/identity/generic"
|
||||
)
|
||||
|
||||
func MonadAp[GA ~func(A) A, A any](fab GA, fa A) A {
|
||||
return I.MonadAp[GA, A, A](fab, fa)
|
||||
}
|
||||
|
||||
func Ap[GA ~func(A) A, A any](fa A) func(GA) A {
|
||||
return I.Ap[GA, A, A](fa)
|
||||
}
|
||||
|
||||
func MonadChain[GA ~func(A) A, A any](ma GA, f GA) GA {
|
||||
return Compose(ma, f)
|
||||
}
|
||||
|
||||
func Chain[ENDO ~func(GA) GA, GA ~func(A) A, A any](f GA) ENDO {
|
||||
return Of[ENDO](F.Bind2nd(Compose[GA], f))
|
||||
}
|
57
endomorphism/generic/monoid.go
Normal file
57
endomorphism/generic/monoid.go
Normal 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 generic
|
||||
|
||||
import (
|
||||
F "github.com/IBM/fp-go/function"
|
||||
M "github.com/IBM/fp-go/monoid"
|
||||
S "github.com/IBM/fp-go/semigroup"
|
||||
)
|
||||
|
||||
// Of converts any function to an [Endomorphism]
|
||||
func Of[ENDO ~func(A) A, F ~func(A) A, A any](f F) ENDO {
|
||||
return func(a A) A {
|
||||
return f(a)
|
||||
}
|
||||
}
|
||||
|
||||
// Wrap converts any function to an [Endomorphism]
|
||||
func Wrap[ENDO ~func(A) A, F ~func(A) A, A any](f F) ENDO {
|
||||
return Of[ENDO](f)
|
||||
}
|
||||
|
||||
// Unwrap converts any [Endomorphism] to a normal function
|
||||
func Unwrap[F ~func(A) A, ENDO ~func(A) A, A any](f ENDO) F {
|
||||
return Of[F](f)
|
||||
}
|
||||
|
||||
func Identity[ENDO ~func(A) A, A any]() ENDO {
|
||||
return Of[ENDO](F.Identity[A])
|
||||
}
|
||||
|
||||
func Compose[ENDO ~func(A) A, A any](f1, f2 ENDO) ENDO {
|
||||
return Of[ENDO](F.Flow2(f1, f2))
|
||||
}
|
||||
|
||||
// Semigroup for the Endomorphism where the `concat` operation is the usual function composition.
|
||||
func Semigroup[ENDO ~func(A) A, A any]() S.Semigroup[ENDO] {
|
||||
return S.MakeSemigroup(Compose[ENDO])
|
||||
}
|
||||
|
||||
// Monoid for the Endomorphism where the `concat` operation is the usual function composition.
|
||||
func Monoid[ENDO ~func(A) A, A any]() M.Monoid[ENDO] {
|
||||
return M.MakeMonoid(Compose[ENDO], Identity[ENDO]())
|
||||
}
|
@@ -16,17 +16,40 @@
|
||||
package endomorphism
|
||||
|
||||
import (
|
||||
F "github.com/IBM/fp-go/function"
|
||||
G "github.com/IBM/fp-go/endomorphism/generic"
|
||||
M "github.com/IBM/fp-go/monoid"
|
||||
S "github.com/IBM/fp-go/semigroup"
|
||||
)
|
||||
|
||||
// Endomorphism is a function that
|
||||
type Endomorphism[A any] func(A) A
|
||||
|
||||
// Of converts any function to an [Endomorphism]
|
||||
func Of[F ~func(A) A, A any](f F) Endomorphism[A] {
|
||||
return G.Of[Endomorphism[A]](f)
|
||||
}
|
||||
|
||||
// Wrap converts any function to an [Endomorphism]
|
||||
func Wrap[F ~func(A) A, A any](f F) Endomorphism[A] {
|
||||
return G.Wrap[Endomorphism[A]](f)
|
||||
}
|
||||
|
||||
// Unwrap converts any [Endomorphism] to a function
|
||||
func Unwrap[F ~func(A) A, A any](f Endomorphism[A]) F {
|
||||
return G.Unwrap[F](f)
|
||||
}
|
||||
|
||||
// Identity returns the identity [Endomorphism]
|
||||
func Identity[A any]() Endomorphism[A] {
|
||||
return G.Identity[Endomorphism[A]]()
|
||||
}
|
||||
|
||||
// Semigroup for the Endomorphism where the `concat` operation is the usual function composition.
|
||||
func Semigroup[A any]() S.Semigroup[func(A) A] {
|
||||
return S.MakeSemigroup(F.Flow2[func(A) A, func(A) A])
|
||||
func Semigroup[A any]() S.Semigroup[Endomorphism[A]] {
|
||||
return G.Semigroup[Endomorphism[A]]()
|
||||
}
|
||||
|
||||
// Monoid for the Endomorphism where the `concat` operation is the usual function composition.
|
||||
func Monoid[A any]() M.Monoid[func(A) A] {
|
||||
return M.MakeMonoid(F.Flow2[func(A) A, func(A) A], F.Identity[A])
|
||||
func Monoid[A any]() M.Monoid[Endomorphism[A]] {
|
||||
return G.Monoid[Endomorphism[A]]()
|
||||
}
|
||||
|
8
eq/eq.go
8
eq/eq.go
@@ -27,20 +27,20 @@ type eq[T any] struct {
|
||||
c func(x, y T) bool
|
||||
}
|
||||
|
||||
func (self eq[T]) Equals(x, y T) bool {
|
||||
return self.c(x, y)
|
||||
func (e eq[T]) Equals(x, y T) bool {
|
||||
return e.c(x, y)
|
||||
}
|
||||
|
||||
func strictEq[A comparable](a, b A) bool {
|
||||
return a == b
|
||||
}
|
||||
|
||||
// FromStrictEquals constructs an `Eq` from the canonical comparison function
|
||||
// FromStrictEquals constructs an [EQ.Eq] from the canonical comparison function
|
||||
func FromStrictEquals[T comparable]() Eq[T] {
|
||||
return FromEquals(strictEq[T])
|
||||
}
|
||||
|
||||
// FromEquals constructs an `Eq` from the comparison function
|
||||
// FromEquals constructs an [EQ.Eq] from the comparison function
|
||||
func FromEquals[T any](c func(x, y T) bool) Eq[T] {
|
||||
return eq[T]{c: c}
|
||||
}
|
||||
|
@@ -16,18 +16,18 @@
|
||||
package exec
|
||||
|
||||
import (
|
||||
T "github.com/IBM/fp-go/tuple"
|
||||
P "github.com/IBM/fp-go/pair"
|
||||
)
|
||||
|
||||
type (
|
||||
// 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
|
||||
CommandOutput = T.Tuple2[[]byte, []byte]
|
||||
CommandOutput = P.Pair[[]byte, []byte]
|
||||
)
|
||||
|
||||
var (
|
||||
// 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 = T.Second[[]byte, []byte]
|
||||
StdErr = P.Tail[[]byte, []byte]
|
||||
)
|
||||
|
@@ -15,7 +15,10 @@
|
||||
|
||||
package file
|
||||
|
||||
import "path/filepath"
|
||||
import (
|
||||
"io"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// Join appends a filename to a root path
|
||||
func Join(name string) func(root string) string {
|
||||
@@ -23,3 +26,13 @@ func Join(name string) func(root string) string {
|
||||
return filepath.Join(root, name)
|
||||
}
|
||||
}
|
||||
|
||||
// ToReader converts a [io.Reader]
|
||||
func ToReader[R io.Reader](r R) io.Reader {
|
||||
return r
|
||||
}
|
||||
|
||||
// ToCloser converts a [io.Closer]
|
||||
func ToCloser[C io.Closer](c C) io.Closer {
|
||||
return c
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
// Code generated by go generate; DO NOT EDIT.
|
||||
// This file was generated by robots at
|
||||
// 2023-10-23 08:30:44.6474482 +0200 CEST m=+0.150851901
|
||||
// 2024-01-31 21:45:01.6437619 +0100 CET m=+0.032758901
|
||||
|
||||
package function
|
||||
|
||||
|
@@ -25,6 +25,17 @@ 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
|
||||
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)
|
||||
}
|
||||
|
||||
// CacheCallback converts a unary function into a unary function that caches the value depending on the parameter
|
||||
func CacheCallback[
|
||||
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)
|
||||
}
|
||||
|
||||
// SingleElementCache creates a cache function for use with the [CacheCallback] method that has a maximum capacity of one single item
|
||||
func SingleElementCache[K comparable, T any]() func(K, func() func() T) func() T {
|
||||
return G.SingleElementCache[func() func() T, K]()
|
||||
}
|
||||
|
@@ -16,6 +16,8 @@
|
||||
package function
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -48,3 +50,21 @@ func TestCache(t *testing.T) {
|
||||
assert.Equal(t, 10, cached(10))
|
||||
assert.Equal(t, 2, count)
|
||||
}
|
||||
|
||||
func TestSingleElementCache(t *testing.T) {
|
||||
f := func(key string) string {
|
||||
return fmt.Sprintf("%s: %d", key, rand.Int())
|
||||
}
|
||||
cb := CacheCallback(func(s string) string { return s }, SingleElementCache[string, string]())
|
||||
cf := cb(f)
|
||||
|
||||
v1 := cf("1")
|
||||
v2 := cf("1")
|
||||
v3 := cf("2")
|
||||
v4 := cf("1")
|
||||
|
||||
assert.Equal(t, v1, v2)
|
||||
assert.NotEqual(t, v2, v3)
|
||||
assert.NotEqual(t, v3, v4)
|
||||
assert.NotEqual(t, v1, v4)
|
||||
}
|
||||
|
332
function/gen.go
332
function/gen.go
@@ -1,6 +1,6 @@
|
||||
// Code generated by go generate; DO NOT EDIT.
|
||||
// This file was generated by robots at
|
||||
// 2023-10-23 08:30:41.7972101 +0200 CEST m=+0.008029101
|
||||
// 2024-01-31 21:44:55.7538323 +0100 CET m=+0.013067701
|
||||
|
||||
package function
|
||||
|
||||
@@ -34,8 +34,7 @@ func Unsliced0[F ~func([]T) R, T, R any](f F) func() R {
|
||||
// Pipe1 takes an initial value t0 and successively applies 1 functions where the input of a function is the return value of the previous function
|
||||
// The final return value is the result of the last function application
|
||||
func Pipe1[F1 ~func(T0) T1, T0, T1 any](t0 T0, f1 F1) T1 {
|
||||
t1 := f1(t0)
|
||||
return t1
|
||||
return f1(t0)
|
||||
}
|
||||
|
||||
// Flow1 creates a function that takes an initial value t0 and successively applies 1 functions where the input of a function is the return value of the previous function
|
||||
@@ -55,7 +54,7 @@ func Nullary1[F1 ~func() T1, T1 any](f1 F1) func() T1 {
|
||||
|
||||
// Curry1 takes a function with 1 parameters and returns a cascade of functions each taking only one parameter.
|
||||
// The inverse function is [Uncurry1]
|
||||
func Curry1[T0, T1 any](f func(T0) T1) func(T0) T1 {
|
||||
func Curry1[FCT ~func(T0) T1, T0, T1 any](f FCT) func(T0) T1 {
|
||||
return func(t0 T0) T1 {
|
||||
return f(t0)
|
||||
}
|
||||
@@ -63,7 +62,7 @@ func Curry1[T0, T1 any](f func(T0) T1) func(T0) T1 {
|
||||
|
||||
// Uncurry1 takes a cascade of 1 functions each taking only one parameter and returns a function with 1 parameters .
|
||||
// The inverse function is [Curry1]
|
||||
func Uncurry1[T0, T1 any](f func(T0) T1) func(T0) T1 {
|
||||
func Uncurry1[FCT ~func(T0) T1, T0, T1 any](f FCT) func(T0) T1 {
|
||||
return func(t0 T0) T1 {
|
||||
return f(t0)
|
||||
}
|
||||
@@ -93,9 +92,7 @@ func Unsliced1[F ~func([]T) R, T, R any](f F) func(T) R {
|
||||
// Pipe2 takes an initial value t0 and successively applies 2 functions where the input of a function is the return value of the previous function
|
||||
// The final return value is the result of the last function application
|
||||
func Pipe2[F1 ~func(T0) T1, F2 ~func(T1) T2, T0, T1, T2 any](t0 T0, f1 F1, f2 F2) T2 {
|
||||
t1 := f1(t0)
|
||||
t2 := f2(t1)
|
||||
return t2
|
||||
return f2(f1(t0))
|
||||
}
|
||||
|
||||
// Flow2 creates a function that takes an initial value t0 and successively applies 2 functions where the input of a function is the return value of the previous function
|
||||
@@ -115,7 +112,7 @@ func Nullary2[F1 ~func() T1, F2 ~func(T1) T2, T1, T2 any](f1 F1, f2 F2) func() T
|
||||
|
||||
// Curry2 takes a function with 2 parameters and returns a cascade of functions each taking only one parameter.
|
||||
// The inverse function is [Uncurry2]
|
||||
func Curry2[T0, T1, T2 any](f func(T0, T1) T2) func(T0) func(T1) T2 {
|
||||
func Curry2[FCT ~func(T0, T1) T2, T0, T1, T2 any](f FCT) func(T0) func(T1) T2 {
|
||||
return func(t0 T0) func(t1 T1) T2 {
|
||||
return func(t1 T1) T2 {
|
||||
return f(t0, t1)
|
||||
@@ -125,7 +122,7 @@ func Curry2[T0, T1, T2 any](f func(T0, T1) T2) func(T0) func(T1) T2 {
|
||||
|
||||
// Uncurry2 takes a cascade of 2 functions each taking only one parameter and returns a function with 2 parameters .
|
||||
// The inverse function is [Curry2]
|
||||
func Uncurry2[T0, T1, T2 any](f func(T0) func(T1) T2) func(T0, T1) T2 {
|
||||
func Uncurry2[FCT ~func(T0) func(T1) T2, T0, T1, T2 any](f FCT) func(T0, T1) T2 {
|
||||
return func(t0 T0, t1 T1) T2 {
|
||||
return f(t0)(t1)
|
||||
}
|
||||
@@ -155,10 +152,7 @@ func Unsliced2[F ~func([]T) R, T, R any](f F) func(T, T) R {
|
||||
// Pipe3 takes an initial value t0 and successively applies 3 functions where the input of a function is the return value of the previous function
|
||||
// The final return value is the result of the last function application
|
||||
func Pipe3[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, T0, T1, T2, T3 any](t0 T0, f1 F1, f2 F2, f3 F3) T3 {
|
||||
t1 := f1(t0)
|
||||
t2 := f2(t1)
|
||||
t3 := f3(t2)
|
||||
return t3
|
||||
return f3(f2(f1(t0)))
|
||||
}
|
||||
|
||||
// Flow3 creates a function that takes an initial value t0 and successively applies 3 functions where the input of a function is the return value of the previous function
|
||||
@@ -178,7 +172,7 @@ func Nullary3[F1 ~func() T1, F2 ~func(T1) T2, F3 ~func(T2) T3, T1, T2, T3 any](f
|
||||
|
||||
// Curry3 takes a function with 3 parameters and returns a cascade of functions each taking only one parameter.
|
||||
// The inverse function is [Uncurry3]
|
||||
func Curry3[T0, T1, T2, T3 any](f func(T0, T1, T2) T3) func(T0) func(T1) func(T2) T3 {
|
||||
func Curry3[FCT ~func(T0, T1, T2) T3, T0, T1, T2, T3 any](f FCT) func(T0) func(T1) func(T2) T3 {
|
||||
return func(t0 T0) func(t1 T1) func(t2 T2) T3 {
|
||||
return func(t1 T1) func(t2 T2) T3 {
|
||||
return func(t2 T2) T3 {
|
||||
@@ -190,7 +184,7 @@ func Curry3[T0, T1, T2, T3 any](f func(T0, T1, T2) T3) func(T0) func(T1) func(T2
|
||||
|
||||
// Uncurry3 takes a cascade of 3 functions each taking only one parameter and returns a function with 3 parameters .
|
||||
// The inverse function is [Curry3]
|
||||
func Uncurry3[T0, T1, T2, T3 any](f func(T0) func(T1) func(T2) T3) func(T0, T1, T2) T3 {
|
||||
func Uncurry3[FCT ~func(T0) func(T1) func(T2) T3, T0, T1, T2, T3 any](f FCT) func(T0, T1, T2) T3 {
|
||||
return func(t0 T0, t1 T1, t2 T2) T3 {
|
||||
return f(t0)(t1)(t2)
|
||||
}
|
||||
@@ -220,11 +214,7 @@ func Unsliced3[F ~func([]T) R, T, R any](f F) func(T, T, T) R {
|
||||
// Pipe4 takes an initial value t0 and successively applies 4 functions where the input of a function is the return value of the previous function
|
||||
// The final return value is the result of the last function application
|
||||
func Pipe4[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, T0, T1, T2, T3, T4 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4) T4 {
|
||||
t1 := f1(t0)
|
||||
t2 := f2(t1)
|
||||
t3 := f3(t2)
|
||||
t4 := f4(t3)
|
||||
return t4
|
||||
return f4(f3(f2(f1(t0))))
|
||||
}
|
||||
|
||||
// Flow4 creates a function that takes an initial value t0 and successively applies 4 functions where the input of a function is the return value of the previous function
|
||||
@@ -244,7 +234,7 @@ func Nullary4[F1 ~func() T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4,
|
||||
|
||||
// Curry4 takes a function with 4 parameters and returns a cascade of functions each taking only one parameter.
|
||||
// The inverse function is [Uncurry4]
|
||||
func Curry4[T0, T1, T2, T3, T4 any](f func(T0, T1, T2, T3) T4) func(T0) func(T1) func(T2) func(T3) T4 {
|
||||
func Curry4[FCT ~func(T0, T1, T2, T3) T4, T0, T1, T2, T3, T4 any](f FCT) func(T0) func(T1) func(T2) func(T3) T4 {
|
||||
return func(t0 T0) func(t1 T1) func(t2 T2) func(t3 T3) T4 {
|
||||
return func(t1 T1) func(t2 T2) func(t3 T3) T4 {
|
||||
return func(t2 T2) func(t3 T3) T4 {
|
||||
@@ -258,7 +248,7 @@ func Curry4[T0, T1, T2, T3, T4 any](f func(T0, T1, T2, T3) T4) func(T0) func(T1)
|
||||
|
||||
// Uncurry4 takes a cascade of 4 functions each taking only one parameter and returns a function with 4 parameters .
|
||||
// The inverse function is [Curry4]
|
||||
func Uncurry4[T0, T1, T2, T3, T4 any](f func(T0) func(T1) func(T2) func(T3) T4) func(T0, T1, T2, T3) T4 {
|
||||
func Uncurry4[FCT ~func(T0) func(T1) func(T2) func(T3) T4, T0, T1, T2, T3, T4 any](f FCT) func(T0, T1, T2, T3) T4 {
|
||||
return func(t0 T0, t1 T1, t2 T2, t3 T3) T4 {
|
||||
return f(t0)(t1)(t2)(t3)
|
||||
}
|
||||
@@ -288,12 +278,7 @@ func Unsliced4[F ~func([]T) R, T, R any](f F) func(T, T, T, T) R {
|
||||
// Pipe5 takes an initial value t0 and successively applies 5 functions where the input of a function is the return value of the previous function
|
||||
// The final return value is the result of the last function application
|
||||
func Pipe5[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, T0, T1, T2, T3, T4, T5 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5) T5 {
|
||||
t1 := f1(t0)
|
||||
t2 := f2(t1)
|
||||
t3 := f3(t2)
|
||||
t4 := f4(t3)
|
||||
t5 := f5(t4)
|
||||
return t5
|
||||
return f5(f4(f3(f2(f1(t0)))))
|
||||
}
|
||||
|
||||
// Flow5 creates a function that takes an initial value t0 and successively applies 5 functions where the input of a function is the return value of the previous function
|
||||
@@ -313,7 +298,7 @@ func Nullary5[F1 ~func() T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4,
|
||||
|
||||
// Curry5 takes a function with 5 parameters and returns a cascade of functions each taking only one parameter.
|
||||
// The inverse function is [Uncurry5]
|
||||
func Curry5[T0, T1, T2, T3, T4, T5 any](f func(T0, T1, T2, T3, T4) T5) func(T0) func(T1) func(T2) func(T3) func(T4) T5 {
|
||||
func Curry5[FCT ~func(T0, T1, T2, T3, T4) T5, T0, T1, T2, T3, T4, T5 any](f FCT) func(T0) func(T1) func(T2) func(T3) func(T4) T5 {
|
||||
return func(t0 T0) func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) T5 {
|
||||
return func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) T5 {
|
||||
return func(t2 T2) func(t3 T3) func(t4 T4) T5 {
|
||||
@@ -329,7 +314,7 @@ func Curry5[T0, T1, T2, T3, T4, T5 any](f func(T0, T1, T2, T3, T4) T5) func(T0)
|
||||
|
||||
// Uncurry5 takes a cascade of 5 functions each taking only one parameter and returns a function with 5 parameters .
|
||||
// The inverse function is [Curry5]
|
||||
func Uncurry5[T0, T1, T2, T3, T4, T5 any](f func(T0) func(T1) func(T2) func(T3) func(T4) T5) func(T0, T1, T2, T3, T4) T5 {
|
||||
func Uncurry5[FCT ~func(T0) func(T1) func(T2) func(T3) func(T4) T5, T0, T1, T2, T3, T4, T5 any](f FCT) func(T0, T1, T2, T3, T4) T5 {
|
||||
return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4) T5 {
|
||||
return f(t0)(t1)(t2)(t3)(t4)
|
||||
}
|
||||
@@ -359,13 +344,7 @@ func Unsliced5[F ~func([]T) R, T, R any](f F) func(T, T, T, T, T) R {
|
||||
// Pipe6 takes an initial value t0 and successively applies 6 functions where the input of a function is the return value of the previous function
|
||||
// The final return value is the result of the last function application
|
||||
func Pipe6[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, T0, T1, T2, T3, T4, T5, T6 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6) T6 {
|
||||
t1 := f1(t0)
|
||||
t2 := f2(t1)
|
||||
t3 := f3(t2)
|
||||
t4 := f4(t3)
|
||||
t5 := f5(t4)
|
||||
t6 := f6(t5)
|
||||
return t6
|
||||
return f6(f5(f4(f3(f2(f1(t0))))))
|
||||
}
|
||||
|
||||
// Flow6 creates a function that takes an initial value t0 and successively applies 6 functions where the input of a function is the return value of the previous function
|
||||
@@ -385,7 +364,7 @@ func Nullary6[F1 ~func() T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4,
|
||||
|
||||
// Curry6 takes a function with 6 parameters and returns a cascade of functions each taking only one parameter.
|
||||
// The inverse function is [Uncurry6]
|
||||
func Curry6[T0, T1, T2, T3, T4, T5, T6 any](f func(T0, T1, T2, T3, T4, T5) T6) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) T6 {
|
||||
func Curry6[FCT ~func(T0, T1, T2, T3, T4, T5) T6, T0, T1, T2, T3, T4, T5, T6 any](f FCT) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) T6 {
|
||||
return func(t0 T0) func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) T6 {
|
||||
return func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) T6 {
|
||||
return func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) T6 {
|
||||
@@ -403,7 +382,7 @@ func Curry6[T0, T1, T2, T3, T4, T5, T6 any](f func(T0, T1, T2, T3, T4, T5) T6) f
|
||||
|
||||
// Uncurry6 takes a cascade of 6 functions each taking only one parameter and returns a function with 6 parameters .
|
||||
// The inverse function is [Curry6]
|
||||
func Uncurry6[T0, T1, T2, T3, T4, T5, T6 any](f func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) T6) func(T0, T1, T2, T3, T4, T5) T6 {
|
||||
func Uncurry6[FCT ~func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) T6, T0, T1, T2, T3, T4, T5, T6 any](f FCT) func(T0, T1, T2, T3, T4, T5) T6 {
|
||||
return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5) T6 {
|
||||
return f(t0)(t1)(t2)(t3)(t4)(t5)
|
||||
}
|
||||
@@ -433,14 +412,7 @@ func Unsliced6[F ~func([]T) R, T, R any](f F) func(T, T, T, T, T, T) R {
|
||||
// Pipe7 takes an initial value t0 and successively applies 7 functions where the input of a function is the return value of the previous function
|
||||
// The final return value is the result of the last function application
|
||||
func Pipe7[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, T0, T1, T2, T3, T4, T5, T6, T7 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7) T7 {
|
||||
t1 := f1(t0)
|
||||
t2 := f2(t1)
|
||||
t3 := f3(t2)
|
||||
t4 := f4(t3)
|
||||
t5 := f5(t4)
|
||||
t6 := f6(t5)
|
||||
t7 := f7(t6)
|
||||
return t7
|
||||
return f7(f6(f5(f4(f3(f2(f1(t0)))))))
|
||||
}
|
||||
|
||||
// Flow7 creates a function that takes an initial value t0 and successively applies 7 functions where the input of a function is the return value of the previous function
|
||||
@@ -460,7 +432,7 @@ func Nullary7[F1 ~func() T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4,
|
||||
|
||||
// Curry7 takes a function with 7 parameters and returns a cascade of functions each taking only one parameter.
|
||||
// The inverse function is [Uncurry7]
|
||||
func Curry7[T0, T1, T2, T3, T4, T5, T6, T7 any](f func(T0, T1, T2, T3, T4, T5, T6) T7) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) T7 {
|
||||
func Curry7[FCT ~func(T0, T1, T2, T3, T4, T5, T6) T7, T0, T1, T2, T3, T4, T5, T6, T7 any](f FCT) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) T7 {
|
||||
return func(t0 T0) func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) T7 {
|
||||
return func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) T7 {
|
||||
return func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) T7 {
|
||||
@@ -480,7 +452,7 @@ func Curry7[T0, T1, T2, T3, T4, T5, T6, T7 any](f func(T0, T1, T2, T3, T4, T5, T
|
||||
|
||||
// Uncurry7 takes a cascade of 7 functions each taking only one parameter and returns a function with 7 parameters .
|
||||
// The inverse function is [Curry7]
|
||||
func Uncurry7[T0, T1, T2, T3, T4, T5, T6, T7 any](f func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) T7) func(T0, T1, T2, T3, T4, T5, T6) T7 {
|
||||
func Uncurry7[FCT ~func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) T7, T0, T1, T2, T3, T4, T5, T6, T7 any](f FCT) func(T0, T1, T2, T3, T4, T5, T6) T7 {
|
||||
return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6) T7 {
|
||||
return f(t0)(t1)(t2)(t3)(t4)(t5)(t6)
|
||||
}
|
||||
@@ -510,15 +482,7 @@ func Unsliced7[F ~func([]T) R, T, R any](f F) func(T, T, T, T, T, T, T) R {
|
||||
// Pipe8 takes an initial value t0 and successively applies 8 functions where the input of a function is the return value of the previous function
|
||||
// The final return value is the result of the last function application
|
||||
func Pipe8[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, T0, T1, T2, T3, T4, T5, T6, T7, T8 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8) T8 {
|
||||
t1 := f1(t0)
|
||||
t2 := f2(t1)
|
||||
t3 := f3(t2)
|
||||
t4 := f4(t3)
|
||||
t5 := f5(t4)
|
||||
t6 := f6(t5)
|
||||
t7 := f7(t6)
|
||||
t8 := f8(t7)
|
||||
return t8
|
||||
return f8(f7(f6(f5(f4(f3(f2(f1(t0))))))))
|
||||
}
|
||||
|
||||
// Flow8 creates a function that takes an initial value t0 and successively applies 8 functions where the input of a function is the return value of the previous function
|
||||
@@ -538,7 +502,7 @@ func Nullary8[F1 ~func() T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4,
|
||||
|
||||
// Curry8 takes a function with 8 parameters and returns a cascade of functions each taking only one parameter.
|
||||
// The inverse function is [Uncurry8]
|
||||
func Curry8[T0, T1, T2, T3, T4, T5, T6, T7, T8 any](f func(T0, T1, T2, T3, T4, T5, T6, T7) T8) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T8 {
|
||||
func Curry8[FCT ~func(T0, T1, T2, T3, T4, T5, T6, T7) T8, T0, T1, T2, T3, T4, T5, T6, T7, T8 any](f FCT) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T8 {
|
||||
return func(t0 T0) func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) T8 {
|
||||
return func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) T8 {
|
||||
return func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) T8 {
|
||||
@@ -560,7 +524,7 @@ func Curry8[T0, T1, T2, T3, T4, T5, T6, T7, T8 any](f func(T0, T1, T2, T3, T4, T
|
||||
|
||||
// Uncurry8 takes a cascade of 8 functions each taking only one parameter and returns a function with 8 parameters .
|
||||
// The inverse function is [Curry8]
|
||||
func Uncurry8[T0, T1, T2, T3, T4, T5, T6, T7, T8 any](f func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T8) func(T0, T1, T2, T3, T4, T5, T6, T7) T8 {
|
||||
func Uncurry8[FCT ~func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T8, T0, T1, T2, T3, T4, T5, T6, T7, T8 any](f FCT) func(T0, T1, T2, T3, T4, T5, T6, T7) T8 {
|
||||
return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7) T8 {
|
||||
return f(t0)(t1)(t2)(t3)(t4)(t5)(t6)(t7)
|
||||
}
|
||||
@@ -590,16 +554,7 @@ func Unsliced8[F ~func([]T) R, T, R any](f F) func(T, T, T, T, T, T, T, T) R {
|
||||
// Pipe9 takes an initial value t0 and successively applies 9 functions where the input of a function is the return value of the previous function
|
||||
// The final return value is the result of the last function application
|
||||
func Pipe9[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9) T9 {
|
||||
t1 := f1(t0)
|
||||
t2 := f2(t1)
|
||||
t3 := f3(t2)
|
||||
t4 := f4(t3)
|
||||
t5 := f5(t4)
|
||||
t6 := f6(t5)
|
||||
t7 := f7(t6)
|
||||
t8 := f8(t7)
|
||||
t9 := f9(t8)
|
||||
return t9
|
||||
return f9(f8(f7(f6(f5(f4(f3(f2(f1(t0)))))))))
|
||||
}
|
||||
|
||||
// Flow9 creates a function that takes an initial value t0 and successively applies 9 functions where the input of a function is the return value of the previous function
|
||||
@@ -619,7 +574,7 @@ func Nullary9[F1 ~func() T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4,
|
||||
|
||||
// Curry9 takes a function with 9 parameters and returns a cascade of functions each taking only one parameter.
|
||||
// The inverse function is [Uncurry9]
|
||||
func Curry9[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9 any](f func(T0, T1, T2, T3, T4, T5, T6, T7, T8) T9) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T9 {
|
||||
func Curry9[FCT ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8) T9, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9 any](f FCT) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T9 {
|
||||
return func(t0 T0) func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) T9 {
|
||||
return func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) T9 {
|
||||
return func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) T9 {
|
||||
@@ -643,7 +598,7 @@ func Curry9[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9 any](f func(T0, T1, T2, T3, T
|
||||
|
||||
// Uncurry9 takes a cascade of 9 functions each taking only one parameter and returns a function with 9 parameters .
|
||||
// The inverse function is [Curry9]
|
||||
func Uncurry9[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9 any](f func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T9) func(T0, T1, T2, T3, T4, T5, T6, T7, T8) T9 {
|
||||
func Uncurry9[FCT ~func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T9, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9 any](f FCT) func(T0, T1, T2, T3, T4, T5, T6, T7, T8) T9 {
|
||||
return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8) T9 {
|
||||
return f(t0)(t1)(t2)(t3)(t4)(t5)(t6)(t7)(t8)
|
||||
}
|
||||
@@ -673,17 +628,7 @@ func Unsliced9[F ~func([]T) R, T, R any](f F) func(T, T, T, T, T, T, T, T, T) R
|
||||
// Pipe10 takes an initial value t0 and successively applies 10 functions where the input of a function is the return value of the previous function
|
||||
// The final return value is the result of the last function application
|
||||
func Pipe10[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10) T10 {
|
||||
t1 := f1(t0)
|
||||
t2 := f2(t1)
|
||||
t3 := f3(t2)
|
||||
t4 := f4(t3)
|
||||
t5 := f5(t4)
|
||||
t6 := f6(t5)
|
||||
t7 := f7(t6)
|
||||
t8 := f8(t7)
|
||||
t9 := f9(t8)
|
||||
t10 := f10(t9)
|
||||
return t10
|
||||
return f10(f9(f8(f7(f6(f5(f4(f3(f2(f1(t0))))))))))
|
||||
}
|
||||
|
||||
// Flow10 creates a function that takes an initial value t0 and successively applies 10 functions where the input of a function is the return value of the previous function
|
||||
@@ -703,7 +648,7 @@ func Nullary10[F1 ~func() T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4,
|
||||
|
||||
// Curry10 takes a function with 10 parameters and returns a cascade of functions each taking only one parameter.
|
||||
// The inverse function is [Uncurry10]
|
||||
func Curry10[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any](f func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) T10) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T10 {
|
||||
func Curry10[FCT ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) T10, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any](f FCT) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T10 {
|
||||
return func(t0 T0) func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) T10 {
|
||||
return func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) T10 {
|
||||
return func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) T10 {
|
||||
@@ -729,7 +674,7 @@ func Curry10[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any](f func(T0, T1, T2,
|
||||
|
||||
// Uncurry10 takes a cascade of 10 functions each taking only one parameter and returns a function with 10 parameters .
|
||||
// The inverse function is [Curry10]
|
||||
func Uncurry10[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any](f func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T10) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) T10 {
|
||||
func Uncurry10[FCT ~func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T10, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any](f FCT) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) T10 {
|
||||
return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9) T10 {
|
||||
return f(t0)(t1)(t2)(t3)(t4)(t5)(t6)(t7)(t8)(t9)
|
||||
}
|
||||
@@ -759,18 +704,7 @@ func Unsliced10[F ~func([]T) R, T, R any](f F) func(T, T, T, T, T, T, T, T, T, T
|
||||
// Pipe11 takes an initial value t0 and successively applies 11 functions where the input of a function is the return value of the previous function
|
||||
// The final return value is the result of the last function application
|
||||
func Pipe11[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, F11 ~func(T10) T11, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11) T11 {
|
||||
t1 := f1(t0)
|
||||
t2 := f2(t1)
|
||||
t3 := f3(t2)
|
||||
t4 := f4(t3)
|
||||
t5 := f5(t4)
|
||||
t6 := f6(t5)
|
||||
t7 := f7(t6)
|
||||
t8 := f8(t7)
|
||||
t9 := f9(t8)
|
||||
t10 := f10(t9)
|
||||
t11 := f11(t10)
|
||||
return t11
|
||||
return f11(f10(f9(f8(f7(f6(f5(f4(f3(f2(f1(t0)))))))))))
|
||||
}
|
||||
|
||||
// Flow11 creates a function that takes an initial value t0 and successively applies 11 functions where the input of a function is the return value of the previous function
|
||||
@@ -790,7 +724,7 @@ func Nullary11[F1 ~func() T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4,
|
||||
|
||||
// Curry11 takes a function with 11 parameters and returns a cascade of functions each taking only one parameter.
|
||||
// The inverse function is [Uncurry11]
|
||||
func Curry11[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11 any](f func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10) T11) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T11 {
|
||||
func Curry11[FCT ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10) T11, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11 any](f FCT) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T11 {
|
||||
return func(t0 T0) func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) T11 {
|
||||
return func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) T11 {
|
||||
return func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) T11 {
|
||||
@@ -818,7 +752,7 @@ func Curry11[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11 any](f func(T0, T1
|
||||
|
||||
// Uncurry11 takes a cascade of 11 functions each taking only one parameter and returns a function with 11 parameters .
|
||||
// The inverse function is [Curry11]
|
||||
func Uncurry11[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11 any](f func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T11) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10) T11 {
|
||||
func Uncurry11[FCT ~func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T11, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11 any](f FCT) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10) T11 {
|
||||
return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10) T11 {
|
||||
return f(t0)(t1)(t2)(t3)(t4)(t5)(t6)(t7)(t8)(t9)(t10)
|
||||
}
|
||||
@@ -848,19 +782,7 @@ func Unsliced11[F ~func([]T) R, T, R any](f F) func(T, T, T, T, T, T, T, T, T, T
|
||||
// Pipe12 takes an initial value t0 and successively applies 12 functions where the input of a function is the return value of the previous function
|
||||
// The final return value is the result of the last function application
|
||||
func Pipe12[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, F11 ~func(T10) T11, F12 ~func(T11) T12, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12) T12 {
|
||||
t1 := f1(t0)
|
||||
t2 := f2(t1)
|
||||
t3 := f3(t2)
|
||||
t4 := f4(t3)
|
||||
t5 := f5(t4)
|
||||
t6 := f6(t5)
|
||||
t7 := f7(t6)
|
||||
t8 := f8(t7)
|
||||
t9 := f9(t8)
|
||||
t10 := f10(t9)
|
||||
t11 := f11(t10)
|
||||
t12 := f12(t11)
|
||||
return t12
|
||||
return f12(f11(f10(f9(f8(f7(f6(f5(f4(f3(f2(f1(t0))))))))))))
|
||||
}
|
||||
|
||||
// Flow12 creates a function that takes an initial value t0 and successively applies 12 functions where the input of a function is the return value of the previous function
|
||||
@@ -880,7 +802,7 @@ func Nullary12[F1 ~func() T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4,
|
||||
|
||||
// Curry12 takes a function with 12 parameters and returns a cascade of functions each taking only one parameter.
|
||||
// The inverse function is [Uncurry12]
|
||||
func Curry12[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12 any](f func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11) T12) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) T12 {
|
||||
func Curry12[FCT ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11) T12, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12 any](f FCT) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) T12 {
|
||||
return func(t0 T0) func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) T12 {
|
||||
return func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) T12 {
|
||||
return func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) T12 {
|
||||
@@ -910,7 +832,7 @@ func Curry12[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12 any](f func(T
|
||||
|
||||
// Uncurry12 takes a cascade of 12 functions each taking only one parameter and returns a function with 12 parameters .
|
||||
// The inverse function is [Curry12]
|
||||
func Uncurry12[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12 any](f func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) T12) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11) T12 {
|
||||
func Uncurry12[FCT ~func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) T12, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12 any](f FCT) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11) T12 {
|
||||
return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11) T12 {
|
||||
return f(t0)(t1)(t2)(t3)(t4)(t5)(t6)(t7)(t8)(t9)(t10)(t11)
|
||||
}
|
||||
@@ -940,20 +862,7 @@ func Unsliced12[F ~func([]T) R, T, R any](f F) func(T, T, T, T, T, T, T, T, T, T
|
||||
// Pipe13 takes an initial value t0 and successively applies 13 functions where the input of a function is the return value of the previous function
|
||||
// The final return value is the result of the last function application
|
||||
func Pipe13[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, F11 ~func(T10) T11, F12 ~func(T11) T12, F13 ~func(T12) T13, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12, f13 F13) T13 {
|
||||
t1 := f1(t0)
|
||||
t2 := f2(t1)
|
||||
t3 := f3(t2)
|
||||
t4 := f4(t3)
|
||||
t5 := f5(t4)
|
||||
t6 := f6(t5)
|
||||
t7 := f7(t6)
|
||||
t8 := f8(t7)
|
||||
t9 := f9(t8)
|
||||
t10 := f10(t9)
|
||||
t11 := f11(t10)
|
||||
t12 := f12(t11)
|
||||
t13 := f13(t12)
|
||||
return t13
|
||||
return f13(f12(f11(f10(f9(f8(f7(f6(f5(f4(f3(f2(f1(t0)))))))))))))
|
||||
}
|
||||
|
||||
// Flow13 creates a function that takes an initial value t0 and successively applies 13 functions where the input of a function is the return value of the previous function
|
||||
@@ -973,7 +882,7 @@ func Nullary13[F1 ~func() T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4,
|
||||
|
||||
// Curry13 takes a function with 13 parameters and returns a cascade of functions each taking only one parameter.
|
||||
// The inverse function is [Uncurry13]
|
||||
func Curry13[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13 any](f func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12) T13) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) T13 {
|
||||
func Curry13[FCT ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12) T13, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13 any](f FCT) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) T13 {
|
||||
return func(t0 T0) func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) T13 {
|
||||
return func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) T13 {
|
||||
return func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) T13 {
|
||||
@@ -1005,7 +914,7 @@ func Curry13[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13 any](f f
|
||||
|
||||
// Uncurry13 takes a cascade of 13 functions each taking only one parameter and returns a function with 13 parameters .
|
||||
// The inverse function is [Curry13]
|
||||
func Uncurry13[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13 any](f func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) T13) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12) T13 {
|
||||
func Uncurry13[FCT ~func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) T13, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13 any](f FCT) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12) T13 {
|
||||
return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12) T13 {
|
||||
return f(t0)(t1)(t2)(t3)(t4)(t5)(t6)(t7)(t8)(t9)(t10)(t11)(t12)
|
||||
}
|
||||
@@ -1035,21 +944,7 @@ func Unsliced13[F ~func([]T) R, T, R any](f F) func(T, T, T, T, T, T, T, T, T, T
|
||||
// Pipe14 takes an initial value t0 and successively applies 14 functions where the input of a function is the return value of the previous function
|
||||
// The final return value is the result of the last function application
|
||||
func Pipe14[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, F11 ~func(T10) T11, F12 ~func(T11) T12, F13 ~func(T12) T13, F14 ~func(T13) T14, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12, f13 F13, f14 F14) T14 {
|
||||
t1 := f1(t0)
|
||||
t2 := f2(t1)
|
||||
t3 := f3(t2)
|
||||
t4 := f4(t3)
|
||||
t5 := f5(t4)
|
||||
t6 := f6(t5)
|
||||
t7 := f7(t6)
|
||||
t8 := f8(t7)
|
||||
t9 := f9(t8)
|
||||
t10 := f10(t9)
|
||||
t11 := f11(t10)
|
||||
t12 := f12(t11)
|
||||
t13 := f13(t12)
|
||||
t14 := f14(t13)
|
||||
return t14
|
||||
return f14(f13(f12(f11(f10(f9(f8(f7(f6(f5(f4(f3(f2(f1(t0))))))))))))))
|
||||
}
|
||||
|
||||
// Flow14 creates a function that takes an initial value t0 and successively applies 14 functions where the input of a function is the return value of the previous function
|
||||
@@ -1069,7 +964,7 @@ func Nullary14[F1 ~func() T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4,
|
||||
|
||||
// Curry14 takes a function with 14 parameters and returns a cascade of functions each taking only one parameter.
|
||||
// The inverse function is [Uncurry14]
|
||||
func Curry14[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14 any](f func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13) T14) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) T14 {
|
||||
func Curry14[FCT ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13) T14, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14 any](f FCT) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) T14 {
|
||||
return func(t0 T0) func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) T14 {
|
||||
return func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) T14 {
|
||||
return func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) T14 {
|
||||
@@ -1103,7 +998,7 @@ func Curry14[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14 any
|
||||
|
||||
// Uncurry14 takes a cascade of 14 functions each taking only one parameter and returns a function with 14 parameters .
|
||||
// The inverse function is [Curry14]
|
||||
func Uncurry14[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14 any](f func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) T14) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13) T14 {
|
||||
func Uncurry14[FCT ~func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) T14, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14 any](f FCT) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13) T14 {
|
||||
return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12, t13 T13) T14 {
|
||||
return f(t0)(t1)(t2)(t3)(t4)(t5)(t6)(t7)(t8)(t9)(t10)(t11)(t12)(t13)
|
||||
}
|
||||
@@ -1133,22 +1028,7 @@ func Unsliced14[F ~func([]T) R, T, R any](f F) func(T, T, T, T, T, T, T, T, T, T
|
||||
// Pipe15 takes an initial value t0 and successively applies 15 functions where the input of a function is the return value of the previous function
|
||||
// The final return value is the result of the last function application
|
||||
func Pipe15[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, F11 ~func(T10) T11, F12 ~func(T11) T12, F13 ~func(T12) T13, F14 ~func(T13) T14, F15 ~func(T14) T15, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12, f13 F13, f14 F14, f15 F15) T15 {
|
||||
t1 := f1(t0)
|
||||
t2 := f2(t1)
|
||||
t3 := f3(t2)
|
||||
t4 := f4(t3)
|
||||
t5 := f5(t4)
|
||||
t6 := f6(t5)
|
||||
t7 := f7(t6)
|
||||
t8 := f8(t7)
|
||||
t9 := f9(t8)
|
||||
t10 := f10(t9)
|
||||
t11 := f11(t10)
|
||||
t12 := f12(t11)
|
||||
t13 := f13(t12)
|
||||
t14 := f14(t13)
|
||||
t15 := f15(t14)
|
||||
return t15
|
||||
return f15(f14(f13(f12(f11(f10(f9(f8(f7(f6(f5(f4(f3(f2(f1(t0)))))))))))))))
|
||||
}
|
||||
|
||||
// Flow15 creates a function that takes an initial value t0 and successively applies 15 functions where the input of a function is the return value of the previous function
|
||||
@@ -1168,7 +1048,7 @@ func Nullary15[F1 ~func() T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4,
|
||||
|
||||
// Curry15 takes a function with 15 parameters and returns a cascade of functions each taking only one parameter.
|
||||
// The inverse function is [Uncurry15]
|
||||
func Curry15[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15 any](f func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14) T15) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) T15 {
|
||||
func Curry15[FCT ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14) T15, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15 any](f FCT) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) T15 {
|
||||
return func(t0 T0) func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) T15 {
|
||||
return func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) T15 {
|
||||
return func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) T15 {
|
||||
@@ -1204,7 +1084,7 @@ func Curry15[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T1
|
||||
|
||||
// Uncurry15 takes a cascade of 15 functions each taking only one parameter and returns a function with 15 parameters .
|
||||
// The inverse function is [Curry15]
|
||||
func Uncurry15[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15 any](f func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) T15) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14) T15 {
|
||||
func Uncurry15[FCT ~func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) T15, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15 any](f FCT) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14) T15 {
|
||||
return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12, t13 T13, t14 T14) T15 {
|
||||
return f(t0)(t1)(t2)(t3)(t4)(t5)(t6)(t7)(t8)(t9)(t10)(t11)(t12)(t13)(t14)
|
||||
}
|
||||
@@ -1234,23 +1114,7 @@ func Unsliced15[F ~func([]T) R, T, R any](f F) func(T, T, T, T, T, T, T, T, T, T
|
||||
// Pipe16 takes an initial value t0 and successively applies 16 functions where the input of a function is the return value of the previous function
|
||||
// The final return value is the result of the last function application
|
||||
func Pipe16[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, F11 ~func(T10) T11, F12 ~func(T11) T12, F13 ~func(T12) T13, F14 ~func(T13) T14, F15 ~func(T14) T15, F16 ~func(T15) T16, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12, f13 F13, f14 F14, f15 F15, f16 F16) T16 {
|
||||
t1 := f1(t0)
|
||||
t2 := f2(t1)
|
||||
t3 := f3(t2)
|
||||
t4 := f4(t3)
|
||||
t5 := f5(t4)
|
||||
t6 := f6(t5)
|
||||
t7 := f7(t6)
|
||||
t8 := f8(t7)
|
||||
t9 := f9(t8)
|
||||
t10 := f10(t9)
|
||||
t11 := f11(t10)
|
||||
t12 := f12(t11)
|
||||
t13 := f13(t12)
|
||||
t14 := f14(t13)
|
||||
t15 := f15(t14)
|
||||
t16 := f16(t15)
|
||||
return t16
|
||||
return f16(f15(f14(f13(f12(f11(f10(f9(f8(f7(f6(f5(f4(f3(f2(f1(t0))))))))))))))))
|
||||
}
|
||||
|
||||
// Flow16 creates a function that takes an initial value t0 and successively applies 16 functions where the input of a function is the return value of the previous function
|
||||
@@ -1270,7 +1134,7 @@ func Nullary16[F1 ~func() T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4,
|
||||
|
||||
// Curry16 takes a function with 16 parameters and returns a cascade of functions each taking only one parameter.
|
||||
// The inverse function is [Uncurry16]
|
||||
func Curry16[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16 any](f func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15) T16) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) T16 {
|
||||
func Curry16[FCT ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15) T16, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16 any](f FCT) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) T16 {
|
||||
return func(t0 T0) func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) T16 {
|
||||
return func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) T16 {
|
||||
return func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) T16 {
|
||||
@@ -1308,7 +1172,7 @@ func Curry16[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T1
|
||||
|
||||
// Uncurry16 takes a cascade of 16 functions each taking only one parameter and returns a function with 16 parameters .
|
||||
// The inverse function is [Curry16]
|
||||
func Uncurry16[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16 any](f func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) T16) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15) T16 {
|
||||
func Uncurry16[FCT ~func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) T16, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16 any](f FCT) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15) T16 {
|
||||
return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12, t13 T13, t14 T14, t15 T15) T16 {
|
||||
return f(t0)(t1)(t2)(t3)(t4)(t5)(t6)(t7)(t8)(t9)(t10)(t11)(t12)(t13)(t14)(t15)
|
||||
}
|
||||
@@ -1338,24 +1202,7 @@ func Unsliced16[F ~func([]T) R, T, R any](f F) func(T, T, T, T, T, T, T, T, T, T
|
||||
// Pipe17 takes an initial value t0 and successively applies 17 functions where the input of a function is the return value of the previous function
|
||||
// The final return value is the result of the last function application
|
||||
func Pipe17[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, F11 ~func(T10) T11, F12 ~func(T11) T12, F13 ~func(T12) T13, F14 ~func(T13) T14, F15 ~func(T14) T15, F16 ~func(T15) T16, F17 ~func(T16) T17, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12, f13 F13, f14 F14, f15 F15, f16 F16, f17 F17) T17 {
|
||||
t1 := f1(t0)
|
||||
t2 := f2(t1)
|
||||
t3 := f3(t2)
|
||||
t4 := f4(t3)
|
||||
t5 := f5(t4)
|
||||
t6 := f6(t5)
|
||||
t7 := f7(t6)
|
||||
t8 := f8(t7)
|
||||
t9 := f9(t8)
|
||||
t10 := f10(t9)
|
||||
t11 := f11(t10)
|
||||
t12 := f12(t11)
|
||||
t13 := f13(t12)
|
||||
t14 := f14(t13)
|
||||
t15 := f15(t14)
|
||||
t16 := f16(t15)
|
||||
t17 := f17(t16)
|
||||
return t17
|
||||
return f17(f16(f15(f14(f13(f12(f11(f10(f9(f8(f7(f6(f5(f4(f3(f2(f1(t0)))))))))))))))))
|
||||
}
|
||||
|
||||
// Flow17 creates a function that takes an initial value t0 and successively applies 17 functions where the input of a function is the return value of the previous function
|
||||
@@ -1375,7 +1222,7 @@ func Nullary17[F1 ~func() T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4,
|
||||
|
||||
// Curry17 takes a function with 17 parameters and returns a cascade of functions each taking only one parameter.
|
||||
// The inverse function is [Uncurry17]
|
||||
func Curry17[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17 any](f func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16) T17) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) func(T16) T17 {
|
||||
func Curry17[FCT ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16) T17, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17 any](f FCT) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) func(T16) T17 {
|
||||
return func(t0 T0) func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) T17 {
|
||||
return func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) T17 {
|
||||
return func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) T17 {
|
||||
@@ -1415,7 +1262,7 @@ func Curry17[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T1
|
||||
|
||||
// Uncurry17 takes a cascade of 17 functions each taking only one parameter and returns a function with 17 parameters .
|
||||
// The inverse function is [Curry17]
|
||||
func Uncurry17[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17 any](f func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) func(T16) T17) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16) T17 {
|
||||
func Uncurry17[FCT ~func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) func(T16) T17, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17 any](f FCT) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16) T17 {
|
||||
return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12, t13 T13, t14 T14, t15 T15, t16 T16) T17 {
|
||||
return f(t0)(t1)(t2)(t3)(t4)(t5)(t6)(t7)(t8)(t9)(t10)(t11)(t12)(t13)(t14)(t15)(t16)
|
||||
}
|
||||
@@ -1445,25 +1292,7 @@ func Unsliced17[F ~func([]T) R, T, R any](f F) func(T, T, T, T, T, T, T, T, T, T
|
||||
// Pipe18 takes an initial value t0 and successively applies 18 functions where the input of a function is the return value of the previous function
|
||||
// The final return value is the result of the last function application
|
||||
func Pipe18[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, F11 ~func(T10) T11, F12 ~func(T11) T12, F13 ~func(T12) T13, F14 ~func(T13) T14, F15 ~func(T14) T15, F16 ~func(T15) T16, F17 ~func(T16) T17, F18 ~func(T17) T18, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12, f13 F13, f14 F14, f15 F15, f16 F16, f17 F17, f18 F18) T18 {
|
||||
t1 := f1(t0)
|
||||
t2 := f2(t1)
|
||||
t3 := f3(t2)
|
||||
t4 := f4(t3)
|
||||
t5 := f5(t4)
|
||||
t6 := f6(t5)
|
||||
t7 := f7(t6)
|
||||
t8 := f8(t7)
|
||||
t9 := f9(t8)
|
||||
t10 := f10(t9)
|
||||
t11 := f11(t10)
|
||||
t12 := f12(t11)
|
||||
t13 := f13(t12)
|
||||
t14 := f14(t13)
|
||||
t15 := f15(t14)
|
||||
t16 := f16(t15)
|
||||
t17 := f17(t16)
|
||||
t18 := f18(t17)
|
||||
return t18
|
||||
return f18(f17(f16(f15(f14(f13(f12(f11(f10(f9(f8(f7(f6(f5(f4(f3(f2(f1(t0))))))))))))))))))
|
||||
}
|
||||
|
||||
// Flow18 creates a function that takes an initial value t0 and successively applies 18 functions where the input of a function is the return value of the previous function
|
||||
@@ -1483,7 +1312,7 @@ func Nullary18[F1 ~func() T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4,
|
||||
|
||||
// Curry18 takes a function with 18 parameters and returns a cascade of functions each taking only one parameter.
|
||||
// The inverse function is [Uncurry18]
|
||||
func Curry18[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18 any](f func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17) T18) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) func(T16) func(T17) T18 {
|
||||
func Curry18[FCT ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17) T18, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18 any](f FCT) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) func(T16) func(T17) T18 {
|
||||
return func(t0 T0) func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) func(t17 T17) T18 {
|
||||
return func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) func(t17 T17) T18 {
|
||||
return func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) func(t17 T17) T18 {
|
||||
@@ -1525,7 +1354,7 @@ func Curry18[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T1
|
||||
|
||||
// Uncurry18 takes a cascade of 18 functions each taking only one parameter and returns a function with 18 parameters .
|
||||
// The inverse function is [Curry18]
|
||||
func Uncurry18[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18 any](f func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) func(T16) func(T17) T18) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17) T18 {
|
||||
func Uncurry18[FCT ~func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) func(T16) func(T17) T18, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18 any](f FCT) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17) T18 {
|
||||
return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12, t13 T13, t14 T14, t15 T15, t16 T16, t17 T17) T18 {
|
||||
return f(t0)(t1)(t2)(t3)(t4)(t5)(t6)(t7)(t8)(t9)(t10)(t11)(t12)(t13)(t14)(t15)(t16)(t17)
|
||||
}
|
||||
@@ -1555,26 +1384,7 @@ func Unsliced18[F ~func([]T) R, T, R any](f F) func(T, T, T, T, T, T, T, T, T, T
|
||||
// Pipe19 takes an initial value t0 and successively applies 19 functions where the input of a function is the return value of the previous function
|
||||
// The final return value is the result of the last function application
|
||||
func Pipe19[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, F11 ~func(T10) T11, F12 ~func(T11) T12, F13 ~func(T12) T13, F14 ~func(T13) T14, F15 ~func(T14) T15, F16 ~func(T15) T16, F17 ~func(T16) T17, F18 ~func(T17) T18, F19 ~func(T18) T19, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12, f13 F13, f14 F14, f15 F15, f16 F16, f17 F17, f18 F18, f19 F19) T19 {
|
||||
t1 := f1(t0)
|
||||
t2 := f2(t1)
|
||||
t3 := f3(t2)
|
||||
t4 := f4(t3)
|
||||
t5 := f5(t4)
|
||||
t6 := f6(t5)
|
||||
t7 := f7(t6)
|
||||
t8 := f8(t7)
|
||||
t9 := f9(t8)
|
||||
t10 := f10(t9)
|
||||
t11 := f11(t10)
|
||||
t12 := f12(t11)
|
||||
t13 := f13(t12)
|
||||
t14 := f14(t13)
|
||||
t15 := f15(t14)
|
||||
t16 := f16(t15)
|
||||
t17 := f17(t16)
|
||||
t18 := f18(t17)
|
||||
t19 := f19(t18)
|
||||
return t19
|
||||
return f19(f18(f17(f16(f15(f14(f13(f12(f11(f10(f9(f8(f7(f6(f5(f4(f3(f2(f1(t0)))))))))))))))))))
|
||||
}
|
||||
|
||||
// Flow19 creates a function that takes an initial value t0 and successively applies 19 functions where the input of a function is the return value of the previous function
|
||||
@@ -1594,7 +1404,7 @@ func Nullary19[F1 ~func() T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4,
|
||||
|
||||
// Curry19 takes a function with 19 parameters and returns a cascade of functions each taking only one parameter.
|
||||
// The inverse function is [Uncurry19]
|
||||
func Curry19[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19 any](f func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18) T19) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) func(T16) func(T17) func(T18) T19 {
|
||||
func Curry19[FCT ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18) T19, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19 any](f FCT) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) func(T16) func(T17) func(T18) T19 {
|
||||
return func(t0 T0) func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) func(t17 T17) func(t18 T18) T19 {
|
||||
return func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) func(t17 T17) func(t18 T18) T19 {
|
||||
return func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) func(t17 T17) func(t18 T18) T19 {
|
||||
@@ -1638,7 +1448,7 @@ func Curry19[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T1
|
||||
|
||||
// Uncurry19 takes a cascade of 19 functions each taking only one parameter and returns a function with 19 parameters .
|
||||
// The inverse function is [Curry19]
|
||||
func Uncurry19[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19 any](f func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) func(T16) func(T17) func(T18) T19) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18) T19 {
|
||||
func Uncurry19[FCT ~func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) func(T16) func(T17) func(T18) T19, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19 any](f FCT) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18) T19 {
|
||||
return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12, t13 T13, t14 T14, t15 T15, t16 T16, t17 T17, t18 T18) T19 {
|
||||
return f(t0)(t1)(t2)(t3)(t4)(t5)(t6)(t7)(t8)(t9)(t10)(t11)(t12)(t13)(t14)(t15)(t16)(t17)(t18)
|
||||
}
|
||||
@@ -1668,27 +1478,7 @@ func Unsliced19[F ~func([]T) R, T, R any](f F) func(T, T, T, T, T, T, T, T, T, T
|
||||
// Pipe20 takes an initial value t0 and successively applies 20 functions where the input of a function is the return value of the previous function
|
||||
// The final return value is the result of the last function application
|
||||
func Pipe20[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, F11 ~func(T10) T11, F12 ~func(T11) T12, F13 ~func(T12) T13, F14 ~func(T13) T14, F15 ~func(T14) T15, F16 ~func(T15) T16, F17 ~func(T16) T17, F18 ~func(T17) T18, F19 ~func(T18) T19, F20 ~func(T19) T20, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12, f13 F13, f14 F14, f15 F15, f16 F16, f17 F17, f18 F18, f19 F19, f20 F20) T20 {
|
||||
t1 := f1(t0)
|
||||
t2 := f2(t1)
|
||||
t3 := f3(t2)
|
||||
t4 := f4(t3)
|
||||
t5 := f5(t4)
|
||||
t6 := f6(t5)
|
||||
t7 := f7(t6)
|
||||
t8 := f8(t7)
|
||||
t9 := f9(t8)
|
||||
t10 := f10(t9)
|
||||
t11 := f11(t10)
|
||||
t12 := f12(t11)
|
||||
t13 := f13(t12)
|
||||
t14 := f14(t13)
|
||||
t15 := f15(t14)
|
||||
t16 := f16(t15)
|
||||
t17 := f17(t16)
|
||||
t18 := f18(t17)
|
||||
t19 := f19(t18)
|
||||
t20 := f20(t19)
|
||||
return t20
|
||||
return f20(f19(f18(f17(f16(f15(f14(f13(f12(f11(f10(f9(f8(f7(f6(f5(f4(f3(f2(f1(t0))))))))))))))))))))
|
||||
}
|
||||
|
||||
// Flow20 creates a function that takes an initial value t0 and successively applies 20 functions where the input of a function is the return value of the previous function
|
||||
@@ -1708,7 +1498,7 @@ func Nullary20[F1 ~func() T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4,
|
||||
|
||||
// Curry20 takes a function with 20 parameters and returns a cascade of functions each taking only one parameter.
|
||||
// The inverse function is [Uncurry20]
|
||||
func Curry20[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20 any](f func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19) T20) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) func(T16) func(T17) func(T18) func(T19) T20 {
|
||||
func Curry20[FCT ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19) T20, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20 any](f FCT) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) func(T16) func(T17) func(T18) func(T19) T20 {
|
||||
return func(t0 T0) func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) func(t17 T17) func(t18 T18) func(t19 T19) T20 {
|
||||
return func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) func(t17 T17) func(t18 T18) func(t19 T19) T20 {
|
||||
return func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) func(t17 T17) func(t18 T18) func(t19 T19) T20 {
|
||||
@@ -1754,7 +1544,7 @@ func Curry20[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T1
|
||||
|
||||
// Uncurry20 takes a cascade of 20 functions each taking only one parameter and returns a function with 20 parameters .
|
||||
// The inverse function is [Curry20]
|
||||
func Uncurry20[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20 any](f func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) func(T16) func(T17) func(T18) func(T19) T20) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19) T20 {
|
||||
func Uncurry20[FCT ~func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) func(T16) func(T17) func(T18) 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](f FCT) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19) T20 {
|
||||
return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12, t13 T13, t14 T14, t15 T15, t16 T16, t17 T17, t18 T18, t19 T19) T20 {
|
||||
return f(t0)(t1)(t2)(t3)(t4)(t5)(t6)(t7)(t8)(t9)(t10)(t11)(t12)(t13)(t14)(t15)(t16)(t17)(t18)(t19)
|
||||
}
|
||||
|
@@ -28,7 +28,7 @@ func Memoize[F ~func(K) T, K comparable, T any](f F) F {
|
||||
|
||||
// ContramapMemoize converts a unary function into a unary function that caches the value depending on the parameter
|
||||
func ContramapMemoize[F ~func(A) T, KF func(A) K, A any, K comparable, T any](kf KF) func(F) F {
|
||||
return CacheCallback[F](kf, getOrCreate[K, T]())
|
||||
return CacheCallback[func(F) F, func() func() T](kf, getOrCreate[K, T]())
|
||||
}
|
||||
|
||||
// getOrCreate is a naive implementation of a cache, without bounds
|
||||
@@ -50,13 +50,51 @@ func getOrCreate[K comparable, T any]() func(K, func() func() T) func() T {
|
||||
}
|
||||
}
|
||||
|
||||
// SingleElementCache is a cache with a capacity of a single element
|
||||
func SingleElementCache[
|
||||
LLT ~func() LT, // generator of the generator
|
||||
K comparable, // key into the cache
|
||||
LT ~func() T, // generator of a value
|
||||
T any, // the cached data type
|
||||
]() func(K, LLT) LT {
|
||||
var l sync.Mutex
|
||||
|
||||
var key K
|
||||
var value LT
|
||||
hasKey := false
|
||||
|
||||
return func(k K, gen LLT) LT {
|
||||
l.Lock()
|
||||
|
||||
existing := value
|
||||
if !hasKey || key != k {
|
||||
existing = gen()
|
||||
// update state
|
||||
key = k
|
||||
value = existing
|
||||
hasKey = true
|
||||
}
|
||||
|
||||
l.Unlock()
|
||||
|
||||
return existing
|
||||
}
|
||||
}
|
||||
|
||||
// CacheCallback converts a unary function into a unary function that caches the value depending on the parameter
|
||||
func CacheCallback[F ~func(A) T, KF func(A) K, C ~func(K, func() func() T) func() T, A any, K comparable, T any](kf KF, getOrCreate C) func(F) F {
|
||||
func CacheCallback[
|
||||
EM ~func(F) F, // endomorphism of the function
|
||||
LLT ~func() LT, // generator of the generator
|
||||
LT ~func() T, // generator of a value
|
||||
F ~func(A) T, // function to actually cache
|
||||
KF func(A) K, // extracts the cache key from the input
|
||||
C ~func(K, LLT) LT, // the cache callback function
|
||||
A any, K comparable, T any](kf KF, getOrCreate C) EM {
|
||||
return func(f F) F {
|
||||
return func(a A) T {
|
||||
// cache entry
|
||||
return getOrCreate(kf(a), func() func() T {
|
||||
return L.Memoize[func() T](func() T {
|
||||
return getOrCreate(kf(a), func() LT {
|
||||
return L.Memoize[LT](func() T {
|
||||
return f(a)
|
||||
})
|
||||
})()
|
||||
|
4
go.mod
4
go.mod
@@ -4,7 +4,7 @@ go 1.20
|
||||
|
||||
require (
|
||||
github.com/stretchr/testify v1.8.4
|
||||
github.com/urfave/cli/v2 v2.25.7
|
||||
github.com/urfave/cli/v2 v2.27.1
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -12,6 +12,6 @@ require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
|
||||
github.com/xrash/smetrics v0.0.0-20231213231151-1d8dd44e695e // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
8
go.sum
8
go.sum
@@ -8,10 +8,10 @@ github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs=
|
||||
github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ=
|
||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
|
||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
|
||||
github.com/urfave/cli/v2 v2.27.1 h1:8xSQ6szndafKVRmfyeUMxkNUJQMjL1F2zmsZ+qHpfho=
|
||||
github.com/urfave/cli/v2 v2.27.1/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ=
|
||||
github.com/xrash/smetrics v0.0.0-20231213231151-1d8dd44e695e h1:+SOyEddqYF09QP7vr7CgJ1eti3pY9Fn3LHO1M1r/0sI=
|
||||
github.com/xrash/smetrics v0.0.0-20231213231151-1d8dd44e695e/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
|
387
http/builder/builder.go
Normal file
387
http/builder/builder.go
Normal 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)))
|
||||
}
|
93
http/builder/builder_test.go
Normal file
93
http/builder/builder_test.go
Normal 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))
|
||||
}
|
23
http/content/content.go
Normal file
23
http/content/content.go
Normal file
@@ -0,0 +1,23 @@
|
||||
// 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 content
|
||||
|
||||
const (
|
||||
TextPlain = "text/plain"
|
||||
JSON = "application/json"
|
||||
Json = JSON // Deprecated: use [JSON] instead
|
||||
FormEncoded = "application/x-www-form-urlencoded"
|
||||
)
|
74
http/form/form.go
Normal file
74
http/form/form.go
Normal 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 form
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
|
||||
A "github.com/IBM/fp-go/array"
|
||||
ENDO "github.com/IBM/fp-go/endomorphism"
|
||||
F "github.com/IBM/fp-go/function"
|
||||
L "github.com/IBM/fp-go/optics/lens"
|
||||
LA "github.com/IBM/fp-go/optics/lens/array"
|
||||
LRG "github.com/IBM/fp-go/optics/lens/record/generic"
|
||||
O "github.com/IBM/fp-go/option"
|
||||
RG "github.com/IBM/fp-go/record/generic"
|
||||
)
|
||||
|
||||
type (
|
||||
// Endomorphism returns an [ENDO.Endomorphism] that transforms a form
|
||||
Endomorphism = ENDO.Endomorphism[url.Values]
|
||||
)
|
||||
|
||||
var (
|
||||
// Default is the default form field
|
||||
Default = make(url.Values)
|
||||
|
||||
noField = O.None[string]()
|
||||
|
||||
// Monoid is the [M.Monoid] for the [Endomorphism]
|
||||
Monoid = ENDO.Monoid[url.Values]()
|
||||
|
||||
// ValuesMonoid is a [M.Monoid] to concatenate [url.Values] maps
|
||||
ValuesMonoid = RG.UnionMonoid[url.Values](A.Semigroup[string]())
|
||||
|
||||
// AtValues is a [L.Lens] that focusses on the values of a form field
|
||||
AtValues = LRG.AtRecord[url.Values, []string]
|
||||
|
||||
composeHead = F.Pipe1(
|
||||
LA.AtHead[string](),
|
||||
L.ComposeOptions[url.Values, string](A.Empty[string]()),
|
||||
)
|
||||
|
||||
// AtValue is a [L.Lens] that focusses on first value in form fields
|
||||
AtValue = F.Flow2(
|
||||
AtValues,
|
||||
composeHead,
|
||||
)
|
||||
)
|
||||
|
||||
// WithValue creates a [FormBuilder] for a certain field
|
||||
func WithValue(name string) func(value string) Endomorphism {
|
||||
return F.Flow2(
|
||||
O.Of[string],
|
||||
AtValue(name).Set,
|
||||
)
|
||||
}
|
||||
|
||||
// WithoutValue creates a [FormBuilder] that removes a field
|
||||
func WithoutValue(name string) Endomorphism {
|
||||
return AtValue(name).Set(noField)
|
||||
}
|
93
http/form/form_test.go
Normal file
93
http/form/form_test.go
Normal 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 form
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
A "github.com/IBM/fp-go/array"
|
||||
"github.com/IBM/fp-go/eq"
|
||||
F "github.com/IBM/fp-go/function"
|
||||
LT "github.com/IBM/fp-go/optics/lens/testing"
|
||||
O "github.com/IBM/fp-go/option"
|
||||
RG "github.com/IBM/fp-go/record/generic"
|
||||
S "github.com/IBM/fp-go/string"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var (
|
||||
sEq = eq.FromEquals(S.Eq)
|
||||
valuesEq = RG.Eq[url.Values](A.Eq(sEq))
|
||||
)
|
||||
|
||||
func TestLaws(t *testing.T) {
|
||||
name := "Content-Type"
|
||||
fieldLaws := LT.AssertLaws[url.Values, O.Option[string]](t, O.Eq(sEq), valuesEq)(AtValue(name))
|
||||
|
||||
n := O.None[string]()
|
||||
s1 := O.Some("s1")
|
||||
|
||||
v1 := F.Pipe1(
|
||||
Default,
|
||||
WithValue(name)("v1"),
|
||||
)
|
||||
|
||||
v2 := F.Pipe1(
|
||||
Default,
|
||||
WithValue("Other-Header")("v2"),
|
||||
)
|
||||
|
||||
assert.True(t, fieldLaws(Default, n))
|
||||
assert.True(t, fieldLaws(v1, n))
|
||||
assert.True(t, fieldLaws(v2, n))
|
||||
|
||||
assert.True(t, fieldLaws(Default, s1))
|
||||
assert.True(t, fieldLaws(v1, s1))
|
||||
assert.True(t, fieldLaws(v2, s1))
|
||||
}
|
||||
|
||||
func TestFormField(t *testing.T) {
|
||||
|
||||
v1 := F.Pipe1(
|
||||
Default,
|
||||
WithValue("h1")("v1"),
|
||||
)
|
||||
|
||||
v2 := F.Pipe1(
|
||||
v1,
|
||||
WithValue("h2")("v2"),
|
||||
)
|
||||
|
||||
// make sure the code does not change structures
|
||||
assert.False(t, valuesEq.Equals(Default, v1))
|
||||
assert.False(t, valuesEq.Equals(Default, v2))
|
||||
assert.False(t, valuesEq.Equals(v1, v2))
|
||||
|
||||
// check for existence of values
|
||||
assert.Equal(t, "v1", v1.Get("h1"))
|
||||
assert.Equal(t, "v1", v2.Get("h1"))
|
||||
assert.Equal(t, "v2", v2.Get("h2"))
|
||||
|
||||
// check getter on lens
|
||||
|
||||
l1 := AtValue("h1")
|
||||
l2 := AtValue("h2")
|
||||
|
||||
assert.Equal(t, O.Of("v1"), l1.Get(v1))
|
||||
assert.Equal(t, O.Of("v1"), l1.Get(v2))
|
||||
assert.Equal(t, O.Of("v2"), l2.Get(v2))
|
||||
}
|
58
http/headers/headers.go
Normal file
58
http/headers/headers.go
Normal 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 headers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/textproto"
|
||||
|
||||
A "github.com/IBM/fp-go/array"
|
||||
F "github.com/IBM/fp-go/function"
|
||||
L "github.com/IBM/fp-go/optics/lens"
|
||||
LA "github.com/IBM/fp-go/optics/lens/array"
|
||||
LRG "github.com/IBM/fp-go/optics/lens/record/generic"
|
||||
RG "github.com/IBM/fp-go/record/generic"
|
||||
)
|
||||
|
||||
// HTTP headers
|
||||
const (
|
||||
Accept = "Accept"
|
||||
Authorization = "Authorization"
|
||||
ContentType = "Content-Type"
|
||||
ContentLength = "Content-Length"
|
||||
)
|
||||
|
||||
var (
|
||||
// Monoid is a [M.Monoid] to concatenate [http.Header] maps
|
||||
Monoid = RG.UnionMonoid[http.Header](A.Semigroup[string]())
|
||||
|
||||
// AtValues is a [L.Lens] that focusses on the values of a header
|
||||
AtValues = F.Flow2(
|
||||
textproto.CanonicalMIMEHeaderKey,
|
||||
LRG.AtRecord[http.Header, []string],
|
||||
)
|
||||
|
||||
composeHead = F.Pipe1(
|
||||
LA.AtHead[string](),
|
||||
L.ComposeOptions[http.Header, string](A.Empty[string]()),
|
||||
)
|
||||
|
||||
// AtValue is a [L.Lens] that focusses on first value of a header
|
||||
AtValue = F.Flow2(
|
||||
AtValues,
|
||||
composeHead,
|
||||
)
|
||||
)
|
58
http/headers/headers_test.go
Normal file
58
http/headers/headers_test.go
Normal 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 headers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
A "github.com/IBM/fp-go/array"
|
||||
"github.com/IBM/fp-go/eq"
|
||||
LT "github.com/IBM/fp-go/optics/lens/testing"
|
||||
O "github.com/IBM/fp-go/option"
|
||||
RG "github.com/IBM/fp-go/record/generic"
|
||||
S "github.com/IBM/fp-go/string"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var (
|
||||
sEq = eq.FromEquals(S.Eq)
|
||||
valuesEq = RG.Eq[http.Header](A.Eq(sEq))
|
||||
)
|
||||
|
||||
func TestLaws(t *testing.T) {
|
||||
name := "Content-Type"
|
||||
fieldLaws := LT.AssertLaws[http.Header, O.Option[string]](t, O.Eq(sEq), valuesEq)(AtValue(name))
|
||||
|
||||
n := O.None[string]()
|
||||
s1 := O.Some("s1")
|
||||
|
||||
def := make(http.Header)
|
||||
|
||||
v1 := make(http.Header)
|
||||
v1.Set(name, "v1")
|
||||
|
||||
v2 := make(http.Header)
|
||||
v2.Set("Other-Header", "v2")
|
||||
|
||||
assert.True(t, fieldLaws(def, n))
|
||||
assert.True(t, fieldLaws(v1, n))
|
||||
assert.True(t, fieldLaws(v2, n))
|
||||
|
||||
assert.True(t, fieldLaws(def, s1))
|
||||
assert.True(t, fieldLaws(v1, s1))
|
||||
assert.True(t, fieldLaws(v2, s1))
|
||||
}
|
@@ -18,15 +18,15 @@ package http
|
||||
import (
|
||||
H "net/http"
|
||||
|
||||
T "github.com/IBM/fp-go/tuple"
|
||||
P "github.com/IBM/fp-go/pair"
|
||||
)
|
||||
|
||||
type (
|
||||
// FullResponse represents a full http response, including headers and body
|
||||
FullResponse = T.Tuple2[*H.Response, []byte]
|
||||
FullResponse = P.Pair[*H.Response, []byte]
|
||||
)
|
||||
|
||||
var (
|
||||
Response = T.First[*H.Response, []byte]
|
||||
Body = T.Second[*H.Response, []byte]
|
||||
Response = P.Head[*H.Response, []byte]
|
||||
Body = P.Tail[*H.Response, []byte]
|
||||
)
|
||||
|
@@ -20,6 +20,7 @@ import (
|
||||
"io"
|
||||
"mime"
|
||||
H "net/http"
|
||||
"net/url"
|
||||
"regexp"
|
||||
|
||||
A "github.com/IBM/fp-go/array"
|
||||
@@ -27,39 +28,48 @@ import (
|
||||
"github.com/IBM/fp-go/errors"
|
||||
F "github.com/IBM/fp-go/function"
|
||||
O "github.com/IBM/fp-go/option"
|
||||
P "github.com/IBM/fp-go/pair"
|
||||
R "github.com/IBM/fp-go/record/generic"
|
||||
T "github.com/IBM/fp-go/tuple"
|
||||
)
|
||||
|
||||
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 (
|
||||
// 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 = E.FromPredicate(isValidStatus, StatusCodeError)
|
||||
// alidateJsonContentTypeString parses a content type a validates that it is valid JSON
|
||||
validateJsonContentTypeString = F.Flow2(
|
||||
validateJSONContentTypeString = F.Flow2(
|
||||
ParseMediaType,
|
||||
E.ChainFirst(F.Flow2(
|
||||
T.First[string, map[string]string],
|
||||
E.FromPredicate(reJsonMimeType.MatchString, func(mimeType string) error {
|
||||
return fmt.Errorf("mimetype [%s] is not a valid JSON content type", mimeType)
|
||||
}),
|
||||
P.Head[string, map[string]string],
|
||||
E.FromPredicate(isJSONMimeType, errors.OnSome[string]("mimetype [%s] is not a valid JSON content type")),
|
||||
)),
|
||||
)
|
||||
// ValidateJsonResponse checks if an HTTP response is a valid JSON response
|
||||
ValidateJsonResponse = F.Flow2(
|
||||
// ValidateJSONResponse checks if an HTTP response is a valid JSON response
|
||||
ValidateJSONResponse = F.Flow2(
|
||||
E.Of[error, *H.Response],
|
||||
E.ChainFirst(F.Flow5(
|
||||
GetHeader,
|
||||
R.Lookup[H.Header](HeaderContentType),
|
||||
O.Chain(A.First[string]),
|
||||
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 (
|
||||
@@ -69,7 +79,32 @@ const (
|
||||
// ParseMediaType parses a media type into a tuple
|
||||
func ParseMediaType(mediaType string) E.Either[error, ParsedMediaType] {
|
||||
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 {
|
||||
@@ -84,6 +119,13 @@ func isValidStatus(resp *H.Response) bool {
|
||||
return resp.StatusCode >= H.StatusOK && resp.StatusCode < H.StatusMultipleChoices
|
||||
}
|
||||
|
||||
// StatusCodeError creates an instance of [HttpError] filled with information from the response
|
||||
func StatusCodeError(resp *H.Response) error {
|
||||
return fmt.Errorf("invalid status code [%d] when accessing URL [%s]", resp.StatusCode, resp.Request.URL)
|
||||
// read the body
|
||||
bodyRdr := GetBody(resp)
|
||||
defer bodyRdr.Close()
|
||||
// try to access body content
|
||||
body, _ := io.ReadAll(bodyRdr)
|
||||
// return an error with comprehensive information
|
||||
return &HttpError{statusCode: resp.StatusCode, headers: GetHeader(resp).Clone(), body: body, url: resp.Request.URL}
|
||||
}
|
||||
|
@@ -20,6 +20,7 @@ import (
|
||||
|
||||
E "github.com/IBM/fp-go/either"
|
||||
F "github.com/IBM/fp-go/function"
|
||||
C "github.com/IBM/fp-go/http/content"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -38,7 +39,7 @@ func Error[A any](t *testing.T) func(E.Either[error, A]) bool {
|
||||
func TestValidateJsonContentTypeString(t *testing.T) {
|
||||
|
||||
res := F.Pipe1(
|
||||
validateJsonContentTypeString("application/json"),
|
||||
validateJSONContentTypeString(C.JSON),
|
||||
NoError[ParsedMediaType](t),
|
||||
)
|
||||
|
||||
@@ -48,7 +49,7 @@ func TestValidateJsonContentTypeString(t *testing.T) {
|
||||
func TestValidateInvalidJsonContentTypeString(t *testing.T) {
|
||||
|
||||
res := F.Pipe1(
|
||||
validateJsonContentTypeString("application/xml"),
|
||||
validateJSONContentTypeString("application/xml"),
|
||||
Error[ParsedMediaType](t),
|
||||
)
|
||||
|
||||
|
89
identity/bind.go
Normal file
89
identity/bind.go
Normal 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,
|
||||
)
|
||||
}
|
@@ -50,7 +50,11 @@ func MonadChainFirst[GAB ~func(A) B, A, B any](fa A, f GAB) A {
|
||||
}
|
||||
|
||||
func ChainFirst[GAB ~func(A) B, A, B any](f GAB) func(A) A {
|
||||
return C.ChainFirst(MonadChain[func(A) A, A, A], MonadMap[func(B) A, B, A], f)
|
||||
return C.ChainFirst(
|
||||
Chain[func(A) A, A, A],
|
||||
Map[func(B) A, B, A],
|
||||
f,
|
||||
)
|
||||
}
|
||||
|
||||
func MonadFlap[GAB ~func(A) B, A, B any](fab GAB, a A) B {
|
||||
@@ -58,5 +62,5 @@ func MonadFlap[GAB ~func(A) B, A, B any](fab GAB, a A) B {
|
||||
}
|
||||
|
||||
func Flap[GAB ~func(A) B, B, A any](a A) func(GAB) B {
|
||||
return F.Bind2nd(MonadFlap[GAB, A, B], a)
|
||||
return FC.Flap(Map[func(GAB) B, GAB, B], a)
|
||||
}
|
||||
|
@@ -36,7 +36,7 @@ func Map[A, B any](f func(A) B) func(A) B {
|
||||
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
|
||||
}
|
||||
|
||||
|
43
identity/monad.go
Normal file
43
identity/monad.go
Normal 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]{}
|
||||
}
|
@@ -20,13 +20,19 @@ import (
|
||||
|
||||
E "github.com/IBM/fp-go/eq"
|
||||
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"
|
||||
"github.com/IBM/fp-go/internal/functor"
|
||||
"github.com/IBM/fp-go/internal/pointed"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// Applicative identity law
|
||||
//
|
||||
// A.ap(A.of(a => a), fa) <-> fa
|
||||
//
|
||||
// Deprecated: use [ApplicativeAssertIdentity]
|
||||
func AssertIdentity[HKTA, HKTAA, A any](t *testing.T,
|
||||
eq E.Eq[HKTA],
|
||||
|
||||
@@ -34,6 +40,9 @@ func AssertIdentity[HKTA, HKTAA, A any](t *testing.T,
|
||||
|
||||
fap func(HKTAA, HKTA) HKTA,
|
||||
) func(fa HKTA) bool {
|
||||
// mark as test helper
|
||||
t.Helper()
|
||||
|
||||
return func(fa HKTA) bool {
|
||||
|
||||
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
|
||||
//
|
||||
// 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,
|
||||
eq E.Eq[HKTB],
|
||||
|
||||
@@ -57,6 +90,9 @@ func AssertHomomorphism[HKTA, HKTB, HKTAB, A, B any](t *testing.T,
|
||||
|
||||
ab func(A) B,
|
||||
) func(a A) bool {
|
||||
// mark as test helper
|
||||
t.Helper()
|
||||
|
||||
return func(a A) bool {
|
||||
|
||||
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
|
||||
//
|
||||
// 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,
|
||||
eq E.Eq[HKTB],
|
||||
|
||||
fofa func(A) HKTA,
|
||||
fofb func(B) HKTB,
|
||||
fofab func(func(A) B) HKTAB,
|
||||
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,
|
||||
) func(a A) bool {
|
||||
// mark as test helper
|
||||
t.Helper()
|
||||
|
||||
return func(a A) bool {
|
||||
|
||||
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'
|
||||
//
|
||||
// Deprecated: use [ApplicativeAssertLaws] instead
|
||||
func AssertLaws[HKTA, HKTB, HKTC, HKTAA, HKTAB, HKTBC, HKTAC, HKTABB, HKTABAC, A, B, C any](t *testing.T,
|
||||
eqa E.Eq[HKTA],
|
||||
eqb E.Eq[HKTB],
|
||||
@@ -127,15 +222,62 @@ func AssertLaws[HKTA, HKTB, HKTC, HKTAA, HKTAB, HKTBC, HKTAC, HKTABB, HKTABAC, A
|
||||
ab func(A) B,
|
||||
bc func(B) C,
|
||||
) func(a A) bool {
|
||||
// mark as test helper
|
||||
t.Helper()
|
||||
|
||||
// apply laws
|
||||
apply := L.AssertLaws(t, eqa, eqc, fofab, fofbc, faa, fab, fac, fbc, fmap, fapab, fapbc, fapac, fapabac, ab, bc)
|
||||
// applicative laws
|
||||
identity := AssertIdentity(t, eqa, fofaa, fapaa)
|
||||
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 {
|
||||
fa := fofa(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)
|
||||
}
|
||||
}
|
||||
|
42
internal/applicative/types.go
Normal file
42
internal/applicative/types.go
Normal 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
|
||||
}
|
@@ -19,13 +19,6 @@ import (
|
||||
F "github.com/IBM/fp-go/function"
|
||||
)
|
||||
|
||||
// HKTFGA = HKT<F, HKT<G, A>>
|
||||
// HKTFGB = HKT<F, HKT<G, B>>
|
||||
// HKTFGAB = HKT<F, HKT<G, (a: A) => B>>
|
||||
|
||||
// HKTGA = HKT<G, A>
|
||||
// HKTGB = HKT<G, B>
|
||||
// HKTGAB = HKT<G, (a: A) => B>
|
||||
func MonadAp[HKTGA, HKTGB, HKTGAB, HKTFGAB, HKTFGGAB, HKTFGA, HKTFGB any](
|
||||
fap func(HKTFGGAB, HKTFGA) HKTFGB,
|
||||
fmap func(HKTFGAB, func(HKTGAB) func(HKTGA) HKTGB) HKTFGGAB,
|
||||
@@ -37,6 +30,29 @@ func MonadAp[HKTGA, HKTGB, HKTGAB, HKTFGAB, HKTFGGAB, HKTFGA, HKTFGB any](
|
||||
return fap(fmap(fab, F.Bind1st(F.Bind1st[HKTGAB, HKTGA, HKTGB], gap)), fa)
|
||||
}
|
||||
|
||||
func Ap[HKTGA, HKTGB, HKTGAB, HKTFGAB, HKTFGGAB, HKTFGA, HKTFGB any](
|
||||
fap func(HKTFGA) func(HKTFGGAB) HKTFGB,
|
||||
fmap func(func(HKTGAB) func(HKTGA) HKTGB) func(HKTFGAB) HKTFGGAB,
|
||||
gap func(HKTGA) func(HKTGAB) HKTGB,
|
||||
|
||||
fa HKTFGA) func(HKTFGAB) HKTFGB {
|
||||
|
||||
return F.Flow2(
|
||||
fmap(F.Flip(gap)),
|
||||
fap(fa),
|
||||
)
|
||||
}
|
||||
|
||||
// func Ap[HKTGA, HKTGB, HKTGAB, HKTFGAB, HKTFGGAB, HKTFGA, HKTFGB any](
|
||||
// fap func(HKTFGA) func(HKTFGGAB) HKTFGB,
|
||||
// fmap func(func(HKTGAB) func(HKTGA) HKTGB) func(HKTFGAB) HKTFGGAB,
|
||||
// gap func(HKTGA) func(HKTGAB) HKTGB,
|
||||
|
||||
// fa HKTFGA) func(HKTFGAB) HKTFGB {
|
||||
|
||||
// return fap(fmap(F.Bind1st(F.Bind1st[HKTGAB, HKTGA, HKTGB], gap)), fa)
|
||||
// }
|
||||
|
||||
// export function ap<F, G>(
|
||||
// F: Apply<F>,
|
||||
// G: Apply<G>
|
||||
@@ -107,3 +123,25 @@ func ApSecond[HKTGA, HKTGB, HKTGBB, A, B any](
|
||||
return MonadApSecond(fap, fmap, first, second)
|
||||
}
|
||||
}
|
||||
|
||||
func MonadApS[S1, S2, B, HKTBGBS2, HKTS1, HKTS2, HKTB any](
|
||||
fap func(HKTBGBS2, HKTB) HKTS2,
|
||||
fmap func(HKTS1, func(S1) func(B) S2) HKTBGBS2,
|
||||
fa HKTS1,
|
||||
key func(B) func(S1) S2,
|
||||
fb HKTB,
|
||||
) HKTS2 {
|
||||
return fap(fmap(fa, F.Flip(key)), fb)
|
||||
}
|
||||
|
||||
func ApS[S1, S2, B, HKTBGBS2, HKTS1, HKTS2, HKTB any](
|
||||
fap func(HKTB) func(HKTBGBS2) HKTS2,
|
||||
fmap func(func(S1) func(B) S2) func(HKTS1) HKTBGBS2,
|
||||
key func(B) func(S1) S2,
|
||||
fb HKTB,
|
||||
) func(HKTS1) HKTS2 {
|
||||
return F.Flow2(
|
||||
fmap(F.Flip(key)),
|
||||
fap(fb),
|
||||
)
|
||||
}
|
||||
|
@@ -19,13 +19,18 @@ import (
|
||||
"testing"
|
||||
|
||||
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"
|
||||
"github.com/IBM/fp-go/internal/pointed"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// 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))
|
||||
//
|
||||
// Deprecated: use [ApplyAssertAssociativeComposition] instead
|
||||
func AssertAssociativeComposition[HKTA, HKTB, HKTC, HKTAB, HKTBC, HKTAC, HKTABAC, A, B, C any](t *testing.T,
|
||||
eq E.Eq[HKTC],
|
||||
|
||||
@@ -43,6 +48,7 @@ func AssertAssociativeComposition[HKTA, HKTB, HKTC, HKTAB, HKTBC, HKTAC, HKTABAC
|
||||
ab func(A) B,
|
||||
bc func(B) C,
|
||||
) func(fa HKTA) bool {
|
||||
t.Helper()
|
||||
return func(fa HKTA) bool {
|
||||
|
||||
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`
|
||||
//
|
||||
// Deprecated: use [ApplyAssertLaws] instead
|
||||
func AssertLaws[HKTA, HKTB, HKTC, HKTAB, HKTBC, HKTAC, HKTABAC, A, B, C any](t *testing.T,
|
||||
eqa E.Eq[HKTA],
|
||||
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,
|
||||
bc func(B) C,
|
||||
) func(fa HKTA) bool {
|
||||
// mark as test helper
|
||||
t.Helper()
|
||||
// functor laws
|
||||
functor := FCT.AssertLaws(t, eqa, eqc, faa, fab, fac, fbc, ab, bc)
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
|
||||
// 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
30
internal/apply/types.go
Normal 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
|
||||
}
|
@@ -106,6 +106,12 @@ func MonadMap[GA ~[]A, GB ~[]B, A, B any](as GA, f func(a A) B) GB {
|
||||
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 {
|
||||
count := len(as)
|
||||
bs := make(GB, count)
|
||||
|
28
internal/array/prepend.go
Normal file
28
internal/array/prepend.go
Normal file
@@ -0,0 +1,28 @@
|
||||
// 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
|
||||
|
||||
// Prepend prepends a single value to an array
|
||||
func Prepend[ENDO ~func(AS) AS, AS ~[]A, A any](head A) ENDO {
|
||||
return func(as AS) AS {
|
||||
l := len(as)
|
||||
cpy := make(AS, l+1)
|
||||
copy(cpy[1:], as)
|
||||
cpy[0] = head
|
||||
return cpy
|
||||
|
||||
}
|
||||
}
|
@@ -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]),
|
||||
))
|
||||
}
|
@@ -43,19 +43,68 @@ func MonadChain[A, B, HKTA, HKTB any](
|
||||
// HKTA=HKT[A]
|
||||
// HKTB=HKT[B]
|
||||
func ChainFirst[A, B, HKTA, HKTB any](
|
||||
mchain func(HKTA, func(A) HKTA) HKTA,
|
||||
mmap func(HKTB, func(B) A) HKTA,
|
||||
mchain func(func(A) HKTA) func(HKTA) HKTA,
|
||||
mmap func(func(B) A) func(HKTB) HKTA,
|
||||
f func(A) HKTB) func(HKTA) HKTA {
|
||||
return F.Bind2nd(mchain, func(a A) HKTA {
|
||||
return mmap(f(a), F.Constant1[B](a))
|
||||
return mchain(func(a A) HKTA {
|
||||
return mmap(F.Constant1[B](a))(f(a))
|
||||
})
|
||||
}
|
||||
|
||||
func Chain[A, B, HKTA, HKTB any](
|
||||
mchain func(HKTA, func(A) HKTB) HKTB,
|
||||
mchain func(func(A) HKTB) func(HKTA) HKTB,
|
||||
f func(A) HKTB,
|
||||
) func(HKTA) HKTB {
|
||||
return func(first HKTA) HKTB {
|
||||
return MonadChain[A, B](mchain, first, f)
|
||||
}
|
||||
return mchain(f)
|
||||
}
|
||||
|
||||
func MonadBind[S1, S2, B, HKTS1, HKTS2, HKTB any](
|
||||
mchain func(HKTS1, func(S1) HKTS2) HKTS2,
|
||||
mmap func(HKTB, func(B) S2) HKTS2,
|
||||
first HKTS1,
|
||||
key func(B) func(S1) S2,
|
||||
f func(S1) HKTB,
|
||||
) HKTS2 {
|
||||
return mchain(first, func(s1 S1) HKTS2 {
|
||||
return mmap(f(s1), func(b B) S2 {
|
||||
return key(b)(s1)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func Bind[S1, S2, B, HKTS1, HKTS2, HKTB any](
|
||||
mchain func(func(S1) HKTS2) func(HKTS1) HKTS2,
|
||||
mmap func(func(B) S2) func(HKTB) HKTS2,
|
||||
key func(B) func(S1) S2,
|
||||
f func(S1) HKTB,
|
||||
) func(HKTS1) HKTS2 {
|
||||
mapb := F.Flow2(
|
||||
F.Flip(key),
|
||||
mmap,
|
||||
)
|
||||
return mchain(func(s1 S1) HKTS2 {
|
||||
return F.Pipe2(
|
||||
s1,
|
||||
f,
|
||||
F.Pipe1(
|
||||
s1,
|
||||
mapb,
|
||||
),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
func BindTo[S1, B, HKTS1, HKTB any](
|
||||
mmap func(func(B) S1) func(HKTB) HKTS1,
|
||||
key func(B) S1,
|
||||
) func(fa HKTB) HKTS1 {
|
||||
return mmap(key)
|
||||
}
|
||||
|
||||
func MonadBindTo[S1, B, HKTS1, HKTB any](
|
||||
mmap func(HKTB, func(B) S1) HKTS1,
|
||||
first HKTB,
|
||||
key func(B) S1,
|
||||
) HKTS1 {
|
||||
return mmap(first, key)
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user