mirror of
https://github.com/IBM/fp-go.git
synced 2025-08-24 19:29:11 +02:00
Compare commits
38 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
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 |
8
.github/workflows/build.yml
vendored
8
.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,7 +26,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [ '1.20.x', '1.21.x' ]
|
||||
go-version: [ '1.20.x', '1.21.x']
|
||||
steps:
|
||||
# full checkout for semantic-release
|
||||
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
@@ -60,7 +60,7 @@ jobs:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Node.js ${{ env.NODE_VERSION }}
|
||||
uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4.0.0
|
||||
uses: actions/setup-node@b39b52d1213e96004bfcb1c61a8a6fa8ab84f3e8 # v4.0.1
|
||||
with:
|
||||
node-version: ${{ env.NODE_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"
|
||||
@@ -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,7 +305,7 @@ 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)
|
||||
}
|
||||
|
||||
@@ -333,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 {
|
||||
@@ -344,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"))
|
||||
}
|
@@ -335,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)
|
||||
}
|
||||
|
||||
@@ -346,3 +346,7 @@ func MonadFlap[FAB ~func(A) B, GFAB ~[]FAB, GB ~[]B, A, B any](fab GFAB, a A) GB
|
||||
func Flap[FAB ~func(A) B, GFAB ~[]FAB, GB ~[]B, A, B any](a A) func(GFAB) GB {
|
||||
return F.Bind2nd(MonadFlap[FAB, GFAB, GB, A, B], a)
|
||||
}
|
||||
|
||||
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,
|
||||
)
|
||||
}
|
@@ -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"
|
||||
@@ -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))
|
||||
}
|
@@ -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, ", ")
|
||||
|
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"))
|
||||
}
|
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,
|
||||
)
|
||||
}
|
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())()))
|
||||
}
|
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]
|
||||
|
@@ -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.
|
||||
|
234
di/provider.go
234
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,89 +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] {
|
||||
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(params ...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),
|
||||
IOE.Map[error](F.ToAny[R]),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func eraseProviderFactory3[T1, T2, T3 any, R any](
|
||||
d1 Dependency[T1],
|
||||
d2 Dependency[T2],
|
||||
d3 Dependency[T3],
|
||||
f func(T1, T2, T3) IOE.IOEither[error, R]) func(params ...any) IOE.IOEither[error, any] {
|
||||
ft := T.Tupled3(f)
|
||||
t1 := lookupAt[T1](0, d1)
|
||||
t2 := lookupAt[T2](1, d2)
|
||||
t3 := lookupAt[T3](2, d3)
|
||||
return func(params ...any) IOE.IOEither[error, any] {
|
||||
return F.Pipe3(
|
||||
E.SequenceT3(t1(params), t2(params), t3(params)),
|
||||
IOE.FromEither[error, T.Tuple3[T1, T2, T3]],
|
||||
IOE.Chain(ft),
|
||||
IOE.Map[error](F.ToAny[R]),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func eraseProviderFactory4[T1, T2, T3, T4 any, R any](
|
||||
d1 Dependency[T1],
|
||||
d2 Dependency[T2],
|
||||
d3 Dependency[T3],
|
||||
d4 Dependency[T4],
|
||||
f func(T1, T2, T3, T4) IOE.IOEither[error, R]) func(params ...any) IOE.IOEither[error, any] {
|
||||
ft := T.Tupled4(f)
|
||||
t1 := lookupAt[T1](0, d1)
|
||||
t2 := lookupAt[T2](1, d2)
|
||||
t3 := lookupAt[T3](2, d3)
|
||||
t4 := lookupAt[T4](3, d4)
|
||||
return func(params ...any) IOE.IOEither[error, any] {
|
||||
return F.Pipe3(
|
||||
E.SequenceT4(t1(params), t2(params), t3(params), t4(params)),
|
||||
IOE.FromEither[error, T.Tuple4[T1, T2, T3, T4]],
|
||||
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](),
|
||||
@@ -122,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,
|
||||
@@ -137,145 +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),
|
||||
)
|
||||
}
|
||||
|
||||
func MakeProviderFactory3[T1, T2, T3, R any](
|
||||
d1 Dependency[T1],
|
||||
d2 Dependency[T2],
|
||||
d3 Dependency[T3],
|
||||
fct func(T1, T2, T3) IOE.IOEither[error, R],
|
||||
) DIE.ProviderFactory {
|
||||
|
||||
return DIE.MakeProviderFactory(
|
||||
A.From[DIE.Dependency](d1, d2, d3),
|
||||
eraseProviderFactory3(d1, d2, d3, fct),
|
||||
)
|
||||
}
|
||||
|
||||
// MakeTokenWithDefault3 create a unique `InjectionToken` for a specific type with an attached default provider
|
||||
func MakeTokenWithDefault3[T1, T2, T3, R any](name string,
|
||||
d1 Dependency[T1],
|
||||
d2 Dependency[T2],
|
||||
d3 Dependency[T3],
|
||||
fct func(T1, T2, T3) IOE.IOEither[error, R]) InjectionToken[R] {
|
||||
return MakeTokenWithDefault[R](name, MakeProviderFactory3(d1, d2, d3, fct))
|
||||
}
|
||||
|
||||
func MakeProvider3[T1, T2, T3, R any](
|
||||
token InjectionToken[R],
|
||||
d1 Dependency[T1],
|
||||
d2 Dependency[T2],
|
||||
d3 Dependency[T3],
|
||||
fct func(T1, T2, T3) IOE.IOEither[error, R],
|
||||
) DIE.Provider {
|
||||
|
||||
return DIE.MakeProvider(
|
||||
token,
|
||||
MakeProviderFactory3(d1, d2, d3, fct),
|
||||
)
|
||||
}
|
||||
|
||||
func MakeProviderFactory4[T1, T2, T3, T4, R any](
|
||||
d1 Dependency[T1],
|
||||
d2 Dependency[T2],
|
||||
d3 Dependency[T3],
|
||||
d4 Dependency[T4],
|
||||
fct func(T1, T2, T3, T4) IOE.IOEither[error, R],
|
||||
) DIE.ProviderFactory {
|
||||
|
||||
return DIE.MakeProviderFactory(
|
||||
A.From[DIE.Dependency](d1, d2, d3, d4),
|
||||
eraseProviderFactory4(d1, d2, d3, d4, fct),
|
||||
)
|
||||
}
|
||||
|
||||
// MakeTokenWithDefault4 create a unique `InjectionToken` for a specific type with an attached default provider
|
||||
func MakeTokenWithDefault4[T1, T2, T3, T4, R any](name string,
|
||||
d1 Dependency[T1],
|
||||
d2 Dependency[T2],
|
||||
d3 Dependency[T3],
|
||||
d4 Dependency[T4],
|
||||
fct func(T1, T2, T3, T4) IOE.IOEither[error, R]) InjectionToken[R] {
|
||||
return MakeTokenWithDefault[R](name, MakeProviderFactory4(d1, d2, d3, d4, fct))
|
||||
}
|
||||
|
||||
func MakeProvider4[T1, T2, T3, T4, R any](
|
||||
token InjectionToken[R],
|
||||
d1 Dependency[T1],
|
||||
d2 Dependency[T2],
|
||||
d3 Dependency[T3],
|
||||
d4 Dependency[T4],
|
||||
fct func(T1, T2, T3, T4) IOE.IOEither[error, R],
|
||||
) DIE.Provider {
|
||||
|
||||
return DIE.MakeProvider(
|
||||
token,
|
||||
MakeProviderFactory4(d1, d2, d3, d4, 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")
|
||||
|
||||
|
17
di/token.go
17
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]
|
||||
@@ -146,7 +147,7 @@ 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()
|
||||
toIdentity := toType[T]()
|
||||
@@ -158,12 +159,12 @@ func makeInjectionToken[T any](name string, providerFactory O.Option[DIE.Provide
|
||||
}
|
||||
}
|
||||
|
||||
// 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))
|
||||
}
|
||||
|
@@ -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 (
|
||||
|
@@ -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 (
|
||||
|
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"))
|
||||
}
|
@@ -67,16 +67,17 @@ func MapTo[E, A, B any](b B) func(Either[E, A]) Either[E, B] {
|
||||
return F.Bind2nd(MonadMapTo[E, A, B], 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 F.Bind2nd(MonadMapLeft[E1, A, E2], f)
|
||||
}
|
||||
|
||||
func MonadChain[E, A, B any](fa Either[E, A], f func(a A) Either[E, B]) Either[E, B] {
|
||||
|
@@ -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]())
|
||||
}
|
||||
|
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))
|
||||
}
|
@@ -28,6 +28,16 @@ func Of[ENDO ~func(A) A, F ~func(A) A, A any](f F) ENDO {
|
||||
}
|
||||
}
|
||||
|
||||
// Wrap converts any function to an [Endomorphism]
|
||||
func Wrap[ENDO ~func(A) A, F ~func(A) A, A any](f F) ENDO {
|
||||
return Of[ENDO](f)
|
||||
}
|
||||
|
||||
// Unwrap converts any [Endomorphism] to a normal function
|
||||
func Unwrap[F ~func(A) A, ENDO ~func(A) A, A any](f ENDO) F {
|
||||
return Of[F](f)
|
||||
}
|
||||
|
||||
func Identity[ENDO ~func(A) A, A any]() ENDO {
|
||||
return Of[ENDO](F.Identity[A])
|
||||
}
|
||||
|
@@ -29,6 +29,16 @@ func Of[F ~func(A) A, A any](f F) Endomorphism[A] {
|
||||
return G.Of[Endomorphism[A]](f)
|
||||
}
|
||||
|
||||
// Wrap converts any function to an [Endomorphism]
|
||||
func Wrap[F ~func(A) A, A any](f F) Endomorphism[A] {
|
||||
return G.Wrap[Endomorphism[A]](f)
|
||||
}
|
||||
|
||||
// Unwrap converts any [Endomorphism] to a function
|
||||
func Unwrap[F ~func(A) A, A any](f Endomorphism[A]) F {
|
||||
return G.Unwrap[F](f)
|
||||
}
|
||||
|
||||
// Identity returns the identity [Endomorphism]
|
||||
func Identity[A any]() Endomorphism[A] {
|
||||
return G.Identity[Endomorphism[A]]()
|
||||
|
4
eq/eq.go
4
eq/eq.go
@@ -35,12 +35,12 @@ func strictEq[A comparable](a, b A) bool {
|
||||
return a == b
|
||||
}
|
||||
|
||||
// FromStrictEquals constructs an `Eq` from the canonical comparison function
|
||||
// FromStrictEquals constructs an [EQ.Eq] from the canonical comparison function
|
||||
func FromStrictEquals[T comparable]() Eq[T] {
|
||||
return FromEquals(strictEq[T])
|
||||
}
|
||||
|
||||
// FromEquals constructs an `Eq` from the comparison function
|
||||
// FromEquals constructs an [EQ.Eq] from the comparison function
|
||||
func FromEquals[T any](c func(x, y T) bool) Eq[T] {
|
||||
return eq[T]{c: c}
|
||||
}
|
||||
|
@@ -1,455 +1,451 @@
|
||||
// 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
|
||||
|
||||
// Combinations for a total of 1 arguments
|
||||
|
||||
// Bind1of1 takes a function with 1 parameters and returns a new function with 1 parameters that will bind these parameters to the positions [1] of the original function.
|
||||
// The return value of is a function with the remaining 0 parameters at positions [] of the original function.
|
||||
func Bind1of1[F ~func(T1) R, T1, R any](f F) func(T1) func() R {
|
||||
return func(t1 T1) func() R {
|
||||
return func() R {
|
||||
return f(t1)
|
||||
}
|
||||
}
|
||||
return func(t1 T1) func() R {
|
||||
return func() R {
|
||||
return f(t1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore1of1 takes a function with 0 parameters and returns a new function with 1 parameters that will ignore the values at positions [1] and pass the remaining 0 parameters to the original function
|
||||
func Ignore1of1[T1 any, F ~func() R, R any](f F) func(T1) R {
|
||||
return func(t1 T1) R {
|
||||
return f()
|
||||
}
|
||||
return func(t1 T1) R {
|
||||
return f()
|
||||
}
|
||||
}
|
||||
|
||||
// Combinations for a total of 2 arguments
|
||||
|
||||
// Bind1of2 takes a function with 2 parameters and returns a new function with 1 parameters that will bind these parameters to the positions [1] of the original function.
|
||||
// The return value of is a function with the remaining 1 parameters at positions [2] of the original function.
|
||||
func Bind1of2[F ~func(T1, T2) R, T1, T2, R any](f F) func(T1) func(T2) R {
|
||||
return func(t1 T1) func(T2) R {
|
||||
return func(t2 T2) R {
|
||||
return f(t1, t2)
|
||||
}
|
||||
}
|
||||
return func(t1 T1) func(T2) R {
|
||||
return func(t2 T2) R {
|
||||
return f(t1, t2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore1of2 takes a function with 1 parameters and returns a new function with 2 parameters that will ignore the values at positions [1] and pass the remaining 1 parameters to the original function
|
||||
func Ignore1of2[T1 any, F ~func(T2) R, T2, R any](f F) func(T1, T2) R {
|
||||
return func(t1 T1, t2 T2) R {
|
||||
return f(t2)
|
||||
}
|
||||
return func(t1 T1, t2 T2) R {
|
||||
return f(t2)
|
||||
}
|
||||
}
|
||||
|
||||
// Bind2of2 takes a function with 2 parameters and returns a new function with 1 parameters that will bind these parameters to the positions [2] of the original function.
|
||||
// The return value of is a function with the remaining 1 parameters at positions [1] of the original function.
|
||||
func Bind2of2[F ~func(T1, T2) R, T1, T2, R any](f F) func(T2) func(T1) R {
|
||||
return func(t2 T2) func(T1) R {
|
||||
return func(t1 T1) R {
|
||||
return f(t1, t2)
|
||||
}
|
||||
}
|
||||
return func(t2 T2) func(T1) R {
|
||||
return func(t1 T1) R {
|
||||
return f(t1, t2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore2of2 takes a function with 1 parameters and returns a new function with 2 parameters that will ignore the values at positions [2] and pass the remaining 1 parameters to the original function
|
||||
func Ignore2of2[T2 any, F ~func(T1) R, T1, R any](f F) func(T1, T2) R {
|
||||
return func(t1 T1, t2 T2) R {
|
||||
return f(t1)
|
||||
}
|
||||
return func(t1 T1, t2 T2) R {
|
||||
return f(t1)
|
||||
}
|
||||
}
|
||||
|
||||
// Bind12of2 takes a function with 2 parameters and returns a new function with 2 parameters that will bind these parameters to the positions [1, 2] of the original function.
|
||||
// The return value of is a function with the remaining 0 parameters at positions [] of the original function.
|
||||
func Bind12of2[F ~func(T1, T2) R, T1, T2, R any](f F) func(T1, T2) func() R {
|
||||
return func(t1 T1, t2 T2) func() R {
|
||||
return func() R {
|
||||
return f(t1, t2)
|
||||
}
|
||||
}
|
||||
return func(t1 T1, t2 T2) func() R {
|
||||
return func() R {
|
||||
return f(t1, t2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore12of2 takes a function with 0 parameters and returns a new function with 2 parameters that will ignore the values at positions [1, 2] and pass the remaining 0 parameters to the original function
|
||||
func Ignore12of2[T1, T2 any, F ~func() R, R any](f F) func(T1, T2) R {
|
||||
return func(t1 T1, t2 T2) R {
|
||||
return f()
|
||||
}
|
||||
return func(t1 T1, t2 T2) R {
|
||||
return f()
|
||||
}
|
||||
}
|
||||
|
||||
// Combinations for a total of 3 arguments
|
||||
|
||||
// Bind1of3 takes a function with 3 parameters and returns a new function with 1 parameters that will bind these parameters to the positions [1] of the original function.
|
||||
// The return value of is a function with the remaining 2 parameters at positions [2, 3] of the original function.
|
||||
func Bind1of3[F ~func(T1, T2, T3) R, T1, T2, T3, R any](f F) func(T1) func(T2, T3) R {
|
||||
return func(t1 T1) func(T2, T3) R {
|
||||
return func(t2 T2, t3 T3) R {
|
||||
return f(t1, t2, t3)
|
||||
}
|
||||
}
|
||||
return func(t1 T1) func(T2, T3) R {
|
||||
return func(t2 T2, t3 T3) R {
|
||||
return f(t1, t2, t3)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore1of3 takes a function with 2 parameters and returns a new function with 3 parameters that will ignore the values at positions [1] and pass the remaining 2 parameters to the original function
|
||||
func Ignore1of3[T1 any, F ~func(T2, T3) R, T2, T3, R any](f F) func(T1, T2, T3) R {
|
||||
return func(t1 T1, t2 T2, t3 T3) R {
|
||||
return f(t2, t3)
|
||||
}
|
||||
return func(t1 T1, t2 T2, t3 T3) R {
|
||||
return f(t2, t3)
|
||||
}
|
||||
}
|
||||
|
||||
// Bind2of3 takes a function with 3 parameters and returns a new function with 1 parameters that will bind these parameters to the positions [2] of the original function.
|
||||
// The return value of is a function with the remaining 2 parameters at positions [1, 3] of the original function.
|
||||
func Bind2of3[F ~func(T1, T2, T3) R, T1, T2, T3, R any](f F) func(T2) func(T1, T3) R {
|
||||
return func(t2 T2) func(T1, T3) R {
|
||||
return func(t1 T1, t3 T3) R {
|
||||
return f(t1, t2, t3)
|
||||
}
|
||||
}
|
||||
return func(t2 T2) func(T1, T3) R {
|
||||
return func(t1 T1, t3 T3) R {
|
||||
return f(t1, t2, t3)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore2of3 takes a function with 2 parameters and returns a new function with 3 parameters that will ignore the values at positions [2] and pass the remaining 2 parameters to the original function
|
||||
func Ignore2of3[T2 any, F ~func(T1, T3) R, T1, T3, R any](f F) func(T1, T2, T3) R {
|
||||
return func(t1 T1, t2 T2, t3 T3) R {
|
||||
return f(t1, t3)
|
||||
}
|
||||
return func(t1 T1, t2 T2, t3 T3) R {
|
||||
return f(t1, t3)
|
||||
}
|
||||
}
|
||||
|
||||
// Bind3of3 takes a function with 3 parameters and returns a new function with 1 parameters that will bind these parameters to the positions [3] of the original function.
|
||||
// The return value of is a function with the remaining 2 parameters at positions [1, 2] of the original function.
|
||||
func Bind3of3[F ~func(T1, T2, T3) R, T1, T2, T3, R any](f F) func(T3) func(T1, T2) R {
|
||||
return func(t3 T3) func(T1, T2) R {
|
||||
return func(t1 T1, t2 T2) R {
|
||||
return f(t1, t2, t3)
|
||||
}
|
||||
}
|
||||
return func(t3 T3) func(T1, T2) R {
|
||||
return func(t1 T1, t2 T2) R {
|
||||
return f(t1, t2, t3)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore3of3 takes a function with 2 parameters and returns a new function with 3 parameters that will ignore the values at positions [3] and pass the remaining 2 parameters to the original function
|
||||
func Ignore3of3[T3 any, F ~func(T1, T2) R, T1, T2, R any](f F) func(T1, T2, T3) R {
|
||||
return func(t1 T1, t2 T2, t3 T3) R {
|
||||
return f(t1, t2)
|
||||
}
|
||||
return func(t1 T1, t2 T2, t3 T3) R {
|
||||
return f(t1, t2)
|
||||
}
|
||||
}
|
||||
|
||||
// Bind12of3 takes a function with 3 parameters and returns a new function with 2 parameters that will bind these parameters to the positions [1, 2] of the original function.
|
||||
// The return value of is a function with the remaining 1 parameters at positions [3] of the original function.
|
||||
func Bind12of3[F ~func(T1, T2, T3) R, T1, T2, T3, R any](f F) func(T1, T2) func(T3) R {
|
||||
return func(t1 T1, t2 T2) func(T3) R {
|
||||
return func(t3 T3) R {
|
||||
return f(t1, t2, t3)
|
||||
}
|
||||
}
|
||||
return func(t1 T1, t2 T2) func(T3) R {
|
||||
return func(t3 T3) R {
|
||||
return f(t1, t2, t3)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore12of3 takes a function with 1 parameters and returns a new function with 3 parameters that will ignore the values at positions [1, 2] and pass the remaining 1 parameters to the original function
|
||||
func Ignore12of3[T1, T2 any, F ~func(T3) R, T3, R any](f F) func(T1, T2, T3) R {
|
||||
return func(t1 T1, t2 T2, t3 T3) R {
|
||||
return f(t3)
|
||||
}
|
||||
return func(t1 T1, t2 T2, t3 T3) R {
|
||||
return f(t3)
|
||||
}
|
||||
}
|
||||
|
||||
// Bind13of3 takes a function with 3 parameters and returns a new function with 2 parameters that will bind these parameters to the positions [1, 3] of the original function.
|
||||
// The return value of is a function with the remaining 1 parameters at positions [2] of the original function.
|
||||
func Bind13of3[F ~func(T1, T2, T3) R, T1, T2, T3, R any](f F) func(T1, T3) func(T2) R {
|
||||
return func(t1 T1, t3 T3) func(T2) R {
|
||||
return func(t2 T2) R {
|
||||
return f(t1, t2, t3)
|
||||
}
|
||||
}
|
||||
return func(t1 T1, t3 T3) func(T2) R {
|
||||
return func(t2 T2) R {
|
||||
return f(t1, t2, t3)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore13of3 takes a function with 1 parameters and returns a new function with 3 parameters that will ignore the values at positions [1, 3] and pass the remaining 1 parameters to the original function
|
||||
func Ignore13of3[T1, T3 any, F ~func(T2) R, T2, R any](f F) func(T1, T2, T3) R {
|
||||
return func(t1 T1, t2 T2, t3 T3) R {
|
||||
return f(t2)
|
||||
}
|
||||
return func(t1 T1, t2 T2, t3 T3) R {
|
||||
return f(t2)
|
||||
}
|
||||
}
|
||||
|
||||
// Bind23of3 takes a function with 3 parameters and returns a new function with 2 parameters that will bind these parameters to the positions [2, 3] of the original function.
|
||||
// The return value of is a function with the remaining 1 parameters at positions [1] of the original function.
|
||||
func Bind23of3[F ~func(T1, T2, T3) R, T1, T2, T3, R any](f F) func(T2, T3) func(T1) R {
|
||||
return func(t2 T2, t3 T3) func(T1) R {
|
||||
return func(t1 T1) R {
|
||||
return f(t1, t2, t3)
|
||||
}
|
||||
}
|
||||
return func(t2 T2, t3 T3) func(T1) R {
|
||||
return func(t1 T1) R {
|
||||
return f(t1, t2, t3)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore23of3 takes a function with 1 parameters and returns a new function with 3 parameters that will ignore the values at positions [2, 3] and pass the remaining 1 parameters to the original function
|
||||
func Ignore23of3[T2, T3 any, F ~func(T1) R, T1, R any](f F) func(T1, T2, T3) R {
|
||||
return func(t1 T1, t2 T2, t3 T3) R {
|
||||
return f(t1)
|
||||
}
|
||||
return func(t1 T1, t2 T2, t3 T3) R {
|
||||
return f(t1)
|
||||
}
|
||||
}
|
||||
|
||||
// Bind123of3 takes a function with 3 parameters and returns a new function with 3 parameters that will bind these parameters to the positions [1, 2, 3] of the original function.
|
||||
// The return value of is a function with the remaining 0 parameters at positions [] of the original function.
|
||||
func Bind123of3[F ~func(T1, T2, T3) R, T1, T2, T3, R any](f F) func(T1, T2, T3) func() R {
|
||||
return func(t1 T1, t2 T2, t3 T3) func() R {
|
||||
return func() R {
|
||||
return f(t1, t2, t3)
|
||||
}
|
||||
}
|
||||
return func(t1 T1, t2 T2, t3 T3) func() R {
|
||||
return func() R {
|
||||
return f(t1, t2, t3)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore123of3 takes a function with 0 parameters and returns a new function with 3 parameters that will ignore the values at positions [1, 2, 3] and pass the remaining 0 parameters to the original function
|
||||
func Ignore123of3[T1, T2, T3 any, F ~func() R, R any](f F) func(T1, T2, T3) R {
|
||||
return func(t1 T1, t2 T2, t3 T3) R {
|
||||
return f()
|
||||
}
|
||||
return func(t1 T1, t2 T2, t3 T3) R {
|
||||
return f()
|
||||
}
|
||||
}
|
||||
|
||||
// Combinations for a total of 4 arguments
|
||||
|
||||
// Bind1of4 takes a function with 4 parameters and returns a new function with 1 parameters that will bind these parameters to the positions [1] of the original function.
|
||||
// The return value of is a function with the remaining 3 parameters at positions [2, 3, 4] of the original function.
|
||||
func Bind1of4[F ~func(T1, T2, T3, T4) R, T1, T2, T3, T4, R any](f F) func(T1) func(T2, T3, T4) R {
|
||||
return func(t1 T1) func(T2, T3, T4) R {
|
||||
return func(t2 T2, t3 T3, t4 T4) R {
|
||||
return f(t1, t2, t3, t4)
|
||||
}
|
||||
}
|
||||
return func(t1 T1) func(T2, T3, T4) R {
|
||||
return func(t2 T2, t3 T3, t4 T4) R {
|
||||
return f(t1, t2, t3, t4)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore1of4 takes a function with 3 parameters and returns a new function with 4 parameters that will ignore the values at positions [1] and pass the remaining 3 parameters to the original function
|
||||
func Ignore1of4[T1 any, F ~func(T2, T3, T4) R, T2, T3, T4, R any](f F) func(T1, T2, T3, T4) R {
|
||||
return func(t1 T1, t2 T2, t3 T3, t4 T4) R {
|
||||
return f(t2, t3, t4)
|
||||
}
|
||||
return func(t1 T1, t2 T2, t3 T3, t4 T4) R {
|
||||
return f(t2, t3, t4)
|
||||
}
|
||||
}
|
||||
|
||||
// Bind2of4 takes a function with 4 parameters and returns a new function with 1 parameters that will bind these parameters to the positions [2] of the original function.
|
||||
// The return value of is a function with the remaining 3 parameters at positions [1, 3, 4] of the original function.
|
||||
func Bind2of4[F ~func(T1, T2, T3, T4) R, T1, T2, T3, T4, R any](f F) func(T2) func(T1, T3, T4) R {
|
||||
return func(t2 T2) func(T1, T3, T4) R {
|
||||
return func(t1 T1, t3 T3, t4 T4) R {
|
||||
return f(t1, t2, t3, t4)
|
||||
}
|
||||
}
|
||||
return func(t2 T2) func(T1, T3, T4) R {
|
||||
return func(t1 T1, t3 T3, t4 T4) R {
|
||||
return f(t1, t2, t3, t4)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore2of4 takes a function with 3 parameters and returns a new function with 4 parameters that will ignore the values at positions [2] and pass the remaining 3 parameters to the original function
|
||||
func Ignore2of4[T2 any, F ~func(T1, T3, T4) R, T1, T3, T4, R any](f F) func(T1, T2, T3, T4) R {
|
||||
return func(t1 T1, t2 T2, t3 T3, t4 T4) R {
|
||||
return f(t1, t3, t4)
|
||||
}
|
||||
return func(t1 T1, t2 T2, t3 T3, t4 T4) R {
|
||||
return f(t1, t3, t4)
|
||||
}
|
||||
}
|
||||
|
||||
// Bind3of4 takes a function with 4 parameters and returns a new function with 1 parameters that will bind these parameters to the positions [3] of the original function.
|
||||
// The return value of is a function with the remaining 3 parameters at positions [1, 2, 4] of the original function.
|
||||
func Bind3of4[F ~func(T1, T2, T3, T4) R, T1, T2, T3, T4, R any](f F) func(T3) func(T1, T2, T4) R {
|
||||
return func(t3 T3) func(T1, T2, T4) R {
|
||||
return func(t1 T1, t2 T2, t4 T4) R {
|
||||
return f(t1, t2, t3, t4)
|
||||
}
|
||||
}
|
||||
return func(t3 T3) func(T1, T2, T4) R {
|
||||
return func(t1 T1, t2 T2, t4 T4) R {
|
||||
return f(t1, t2, t3, t4)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore3of4 takes a function with 3 parameters and returns a new function with 4 parameters that will ignore the values at positions [3] and pass the remaining 3 parameters to the original function
|
||||
func Ignore3of4[T3 any, F ~func(T1, T2, T4) R, T1, T2, T4, R any](f F) func(T1, T2, T3, T4) R {
|
||||
return func(t1 T1, t2 T2, t3 T3, t4 T4) R {
|
||||
return f(t1, t2, t4)
|
||||
}
|
||||
return func(t1 T1, t2 T2, t3 T3, t4 T4) R {
|
||||
return f(t1, t2, t4)
|
||||
}
|
||||
}
|
||||
|
||||
// Bind4of4 takes a function with 4 parameters and returns a new function with 1 parameters that will bind these parameters to the positions [4] of the original function.
|
||||
// The return value of is a function with the remaining 3 parameters at positions [1, 2, 3] of the original function.
|
||||
func Bind4of4[F ~func(T1, T2, T3, T4) R, T1, T2, T3, T4, R any](f F) func(T4) func(T1, T2, T3) R {
|
||||
return func(t4 T4) func(T1, T2, T3) R {
|
||||
return func(t1 T1, t2 T2, t3 T3) R {
|
||||
return f(t1, t2, t3, t4)
|
||||
}
|
||||
}
|
||||
return func(t4 T4) func(T1, T2, T3) R {
|
||||
return func(t1 T1, t2 T2, t3 T3) R {
|
||||
return f(t1, t2, t3, t4)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore4of4 takes a function with 3 parameters and returns a new function with 4 parameters that will ignore the values at positions [4] and pass the remaining 3 parameters to the original function
|
||||
func Ignore4of4[T4 any, F ~func(T1, T2, T3) R, T1, T2, T3, R any](f F) func(T1, T2, T3, T4) R {
|
||||
return func(t1 T1, t2 T2, t3 T3, t4 T4) R {
|
||||
return f(t1, t2, t3)
|
||||
}
|
||||
return func(t1 T1, t2 T2, t3 T3, t4 T4) R {
|
||||
return f(t1, t2, t3)
|
||||
}
|
||||
}
|
||||
|
||||
// Bind12of4 takes a function with 4 parameters and returns a new function with 2 parameters that will bind these parameters to the positions [1, 2] of the original function.
|
||||
// The return value of is a function with the remaining 2 parameters at positions [3, 4] of the original function.
|
||||
func Bind12of4[F ~func(T1, T2, T3, T4) R, T1, T2, T3, T4, R any](f F) func(T1, T2) func(T3, T4) R {
|
||||
return func(t1 T1, t2 T2) func(T3, T4) R {
|
||||
return func(t3 T3, t4 T4) R {
|
||||
return f(t1, t2, t3, t4)
|
||||
}
|
||||
}
|
||||
return func(t1 T1, t2 T2) func(T3, T4) R {
|
||||
return func(t3 T3, t4 T4) R {
|
||||
return f(t1, t2, t3, t4)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore12of4 takes a function with 2 parameters and returns a new function with 4 parameters that will ignore the values at positions [1, 2] and pass the remaining 2 parameters to the original function
|
||||
func Ignore12of4[T1, T2 any, F ~func(T3, T4) R, T3, T4, R any](f F) func(T1, T2, T3, T4) R {
|
||||
return func(t1 T1, t2 T2, t3 T3, t4 T4) R {
|
||||
return f(t3, t4)
|
||||
}
|
||||
return func(t1 T1, t2 T2, t3 T3, t4 T4) R {
|
||||
return f(t3, t4)
|
||||
}
|
||||
}
|
||||
|
||||
// Bind13of4 takes a function with 4 parameters and returns a new function with 2 parameters that will bind these parameters to the positions [1, 3] of the original function.
|
||||
// The return value of is a function with the remaining 2 parameters at positions [2, 4] of the original function.
|
||||
func Bind13of4[F ~func(T1, T2, T3, T4) R, T1, T2, T3, T4, R any](f F) func(T1, T3) func(T2, T4) R {
|
||||
return func(t1 T1, t3 T3) func(T2, T4) R {
|
||||
return func(t2 T2, t4 T4) R {
|
||||
return f(t1, t2, t3, t4)
|
||||
}
|
||||
}
|
||||
return func(t1 T1, t3 T3) func(T2, T4) R {
|
||||
return func(t2 T2, t4 T4) R {
|
||||
return f(t1, t2, t3, t4)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore13of4 takes a function with 2 parameters and returns a new function with 4 parameters that will ignore the values at positions [1, 3] and pass the remaining 2 parameters to the original function
|
||||
func Ignore13of4[T1, T3 any, F ~func(T2, T4) R, T2, T4, R any](f F) func(T1, T2, T3, T4) R {
|
||||
return func(t1 T1, t2 T2, t3 T3, t4 T4) R {
|
||||
return f(t2, t4)
|
||||
}
|
||||
return func(t1 T1, t2 T2, t3 T3, t4 T4) R {
|
||||
return f(t2, t4)
|
||||
}
|
||||
}
|
||||
|
||||
// Bind14of4 takes a function with 4 parameters and returns a new function with 2 parameters that will bind these parameters to the positions [1, 4] of the original function.
|
||||
// The return value of is a function with the remaining 2 parameters at positions [2, 3] of the original function.
|
||||
func Bind14of4[F ~func(T1, T2, T3, T4) R, T1, T2, T3, T4, R any](f F) func(T1, T4) func(T2, T3) R {
|
||||
return func(t1 T1, t4 T4) func(T2, T3) R {
|
||||
return func(t2 T2, t3 T3) R {
|
||||
return f(t1, t2, t3, t4)
|
||||
}
|
||||
}
|
||||
return func(t1 T1, t4 T4) func(T2, T3) R {
|
||||
return func(t2 T2, t3 T3) R {
|
||||
return f(t1, t2, t3, t4)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore14of4 takes a function with 2 parameters and returns a new function with 4 parameters that will ignore the values at positions [1, 4] and pass the remaining 2 parameters to the original function
|
||||
func Ignore14of4[T1, T4 any, F ~func(T2, T3) R, T2, T3, R any](f F) func(T1, T2, T3, T4) R {
|
||||
return func(t1 T1, t2 T2, t3 T3, t4 T4) R {
|
||||
return f(t2, t3)
|
||||
}
|
||||
return func(t1 T1, t2 T2, t3 T3, t4 T4) R {
|
||||
return f(t2, t3)
|
||||
}
|
||||
}
|
||||
|
||||
// Bind23of4 takes a function with 4 parameters and returns a new function with 2 parameters that will bind these parameters to the positions [2, 3] of the original function.
|
||||
// The return value of is a function with the remaining 2 parameters at positions [1, 4] of the original function.
|
||||
func Bind23of4[F ~func(T1, T2, T3, T4) R, T1, T2, T3, T4, R any](f F) func(T2, T3) func(T1, T4) R {
|
||||
return func(t2 T2, t3 T3) func(T1, T4) R {
|
||||
return func(t1 T1, t4 T4) R {
|
||||
return f(t1, t2, t3, t4)
|
||||
}
|
||||
}
|
||||
return func(t2 T2, t3 T3) func(T1, T4) R {
|
||||
return func(t1 T1, t4 T4) R {
|
||||
return f(t1, t2, t3, t4)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore23of4 takes a function with 2 parameters and returns a new function with 4 parameters that will ignore the values at positions [2, 3] and pass the remaining 2 parameters to the original function
|
||||
func Ignore23of4[T2, T3 any, F ~func(T1, T4) R, T1, T4, R any](f F) func(T1, T2, T3, T4) R {
|
||||
return func(t1 T1, t2 T2, t3 T3, t4 T4) R {
|
||||
return f(t1, t4)
|
||||
}
|
||||
return func(t1 T1, t2 T2, t3 T3, t4 T4) R {
|
||||
return f(t1, t4)
|
||||
}
|
||||
}
|
||||
|
||||
// Bind24of4 takes a function with 4 parameters and returns a new function with 2 parameters that will bind these parameters to the positions [2, 4] of the original function.
|
||||
// The return value of is a function with the remaining 2 parameters at positions [1, 3] of the original function.
|
||||
func Bind24of4[F ~func(T1, T2, T3, T4) R, T1, T2, T3, T4, R any](f F) func(T2, T4) func(T1, T3) R {
|
||||
return func(t2 T2, t4 T4) func(T1, T3) R {
|
||||
return func(t1 T1, t3 T3) R {
|
||||
return f(t1, t2, t3, t4)
|
||||
}
|
||||
}
|
||||
return func(t2 T2, t4 T4) func(T1, T3) R {
|
||||
return func(t1 T1, t3 T3) R {
|
||||
return f(t1, t2, t3, t4)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore24of4 takes a function with 2 parameters and returns a new function with 4 parameters that will ignore the values at positions [2, 4] and pass the remaining 2 parameters to the original function
|
||||
func Ignore24of4[T2, T4 any, F ~func(T1, T3) R, T1, T3, R any](f F) func(T1, T2, T3, T4) R {
|
||||
return func(t1 T1, t2 T2, t3 T3, t4 T4) R {
|
||||
return f(t1, t3)
|
||||
}
|
||||
return func(t1 T1, t2 T2, t3 T3, t4 T4) R {
|
||||
return f(t1, t3)
|
||||
}
|
||||
}
|
||||
|
||||
// Bind34of4 takes a function with 4 parameters and returns a new function with 2 parameters that will bind these parameters to the positions [3, 4] of the original function.
|
||||
// The return value of is a function with the remaining 2 parameters at positions [1, 2] of the original function.
|
||||
func Bind34of4[F ~func(T1, T2, T3, T4) R, T1, T2, T3, T4, R any](f F) func(T3, T4) func(T1, T2) R {
|
||||
return func(t3 T3, t4 T4) func(T1, T2) R {
|
||||
return func(t1 T1, t2 T2) R {
|
||||
return f(t1, t2, t3, t4)
|
||||
}
|
||||
}
|
||||
return func(t3 T3, t4 T4) func(T1, T2) R {
|
||||
return func(t1 T1, t2 T2) R {
|
||||
return f(t1, t2, t3, t4)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore34of4 takes a function with 2 parameters and returns a new function with 4 parameters that will ignore the values at positions [3, 4] and pass the remaining 2 parameters to the original function
|
||||
func Ignore34of4[T3, T4 any, F ~func(T1, T2) R, T1, T2, R any](f F) func(T1, T2, T3, T4) R {
|
||||
return func(t1 T1, t2 T2, t3 T3, t4 T4) R {
|
||||
return f(t1, t2)
|
||||
}
|
||||
return func(t1 T1, t2 T2, t3 T3, t4 T4) R {
|
||||
return f(t1, t2)
|
||||
}
|
||||
}
|
||||
|
||||
// Bind123of4 takes a function with 4 parameters and returns a new function with 3 parameters that will bind these parameters to the positions [1, 2, 3] of the original function.
|
||||
// The return value of is a function with the remaining 1 parameters at positions [4] of the original function.
|
||||
func Bind123of4[F ~func(T1, T2, T3, T4) R, T1, T2, T3, T4, R any](f F) func(T1, T2, T3) func(T4) R {
|
||||
return func(t1 T1, t2 T2, t3 T3) func(T4) R {
|
||||
return func(t4 T4) R {
|
||||
return f(t1, t2, t3, t4)
|
||||
}
|
||||
}
|
||||
return func(t1 T1, t2 T2, t3 T3) func(T4) R {
|
||||
return func(t4 T4) R {
|
||||
return f(t1, t2, t3, t4)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore123of4 takes a function with 1 parameters and returns a new function with 4 parameters that will ignore the values at positions [1, 2, 3] and pass the remaining 1 parameters to the original function
|
||||
func Ignore123of4[T1, T2, T3 any, F ~func(T4) R, T4, R any](f F) func(T1, T2, T3, T4) R {
|
||||
return func(t1 T1, t2 T2, t3 T3, t4 T4) R {
|
||||
return f(t4)
|
||||
}
|
||||
return func(t1 T1, t2 T2, t3 T3, t4 T4) R {
|
||||
return f(t4)
|
||||
}
|
||||
}
|
||||
|
||||
// Bind124of4 takes a function with 4 parameters and returns a new function with 3 parameters that will bind these parameters to the positions [1, 2, 4] of the original function.
|
||||
// The return value of is a function with the remaining 1 parameters at positions [3] of the original function.
|
||||
func Bind124of4[F ~func(T1, T2, T3, T4) R, T1, T2, T3, T4, R any](f F) func(T1, T2, T4) func(T3) R {
|
||||
return func(t1 T1, t2 T2, t4 T4) func(T3) R {
|
||||
return func(t3 T3) R {
|
||||
return f(t1, t2, t3, t4)
|
||||
}
|
||||
}
|
||||
return func(t1 T1, t2 T2, t4 T4) func(T3) R {
|
||||
return func(t3 T3) R {
|
||||
return f(t1, t2, t3, t4)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore124of4 takes a function with 1 parameters and returns a new function with 4 parameters that will ignore the values at positions [1, 2, 4] and pass the remaining 1 parameters to the original function
|
||||
func Ignore124of4[T1, T2, T4 any, F ~func(T3) R, T3, R any](f F) func(T1, T2, T3, T4) R {
|
||||
return func(t1 T1, t2 T2, t3 T3, t4 T4) R {
|
||||
return f(t3)
|
||||
}
|
||||
return func(t1 T1, t2 T2, t3 T3, t4 T4) R {
|
||||
return f(t3)
|
||||
}
|
||||
}
|
||||
|
||||
// Bind134of4 takes a function with 4 parameters and returns a new function with 3 parameters that will bind these parameters to the positions [1, 3, 4] of the original function.
|
||||
// The return value of is a function with the remaining 1 parameters at positions [2] of the original function.
|
||||
func Bind134of4[F ~func(T1, T2, T3, T4) R, T1, T2, T3, T4, R any](f F) func(T1, T3, T4) func(T2) R {
|
||||
return func(t1 T1, t3 T3, t4 T4) func(T2) R {
|
||||
return func(t2 T2) R {
|
||||
return f(t1, t2, t3, t4)
|
||||
}
|
||||
}
|
||||
return func(t1 T1, t3 T3, t4 T4) func(T2) R {
|
||||
return func(t2 T2) R {
|
||||
return f(t1, t2, t3, t4)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore134of4 takes a function with 1 parameters and returns a new function with 4 parameters that will ignore the values at positions [1, 3, 4] and pass the remaining 1 parameters to the original function
|
||||
func Ignore134of4[T1, T3, T4 any, F ~func(T2) R, T2, R any](f F) func(T1, T2, T3, T4) R {
|
||||
return func(t1 T1, t2 T2, t3 T3, t4 T4) R {
|
||||
return f(t2)
|
||||
}
|
||||
return func(t1 T1, t2 T2, t3 T3, t4 T4) R {
|
||||
return f(t2)
|
||||
}
|
||||
}
|
||||
|
||||
// Bind234of4 takes a function with 4 parameters and returns a new function with 3 parameters that will bind these parameters to the positions [2, 3, 4] of the original function.
|
||||
// The return value of is a function with the remaining 1 parameters at positions [1] of the original function.
|
||||
func Bind234of4[F ~func(T1, T2, T3, T4) R, T1, T2, T3, T4, R any](f F) func(T2, T3, T4) func(T1) R {
|
||||
return func(t2 T2, t3 T3, t4 T4) func(T1) R {
|
||||
return func(t1 T1) R {
|
||||
return f(t1, t2, t3, t4)
|
||||
}
|
||||
}
|
||||
return func(t2 T2, t3 T3, t4 T4) func(T1) R {
|
||||
return func(t1 T1) R {
|
||||
return f(t1, t2, t3, t4)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore234of4 takes a function with 1 parameters and returns a new function with 4 parameters that will ignore the values at positions [2, 3, 4] and pass the remaining 1 parameters to the original function
|
||||
func Ignore234of4[T2, T3, T4 any, F ~func(T1) R, T1, R any](f F) func(T1, T2, T3, T4) R {
|
||||
return func(t1 T1, t2 T2, t3 T3, t4 T4) R {
|
||||
return f(t1)
|
||||
}
|
||||
return func(t1 T1, t2 T2, t3 T3, t4 T4) R {
|
||||
return f(t1)
|
||||
}
|
||||
}
|
||||
|
||||
// Bind1234of4 takes a function with 4 parameters and returns a new function with 4 parameters that will bind these parameters to the positions [1, 2, 3, 4] of the original function.
|
||||
// The return value of is a function with the remaining 0 parameters at positions [] of the original function.
|
||||
func Bind1234of4[F ~func(T1, T2, T3, T4) R, T1, T2, T3, T4, R any](f F) func(T1, T2, T3, T4) func() R {
|
||||
return func(t1 T1, t2 T2, t3 T3, t4 T4) func() R {
|
||||
return func() R {
|
||||
return f(t1, t2, t3, t4)
|
||||
}
|
||||
}
|
||||
return func(t1 T1, t2 T2, t3 T3, t4 T4) func() R {
|
||||
return func() R {
|
||||
return f(t1, t2, t3, t4)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore1234of4 takes a function with 0 parameters and returns a new function with 4 parameters that will ignore the values at positions [1, 2, 3, 4] and pass the remaining 0 parameters to the original function
|
||||
func Ignore1234of4[T1, T2, T3, T4 any, F ~func() R, R any](f F) func(T1, T2, T3, T4) R {
|
||||
return func(t1 T1, t2 T2, t3 T3, t4 T4) R {
|
||||
return f()
|
||||
}
|
||||
return func(t1 T1, t2 T2, t3 T3, t4 T4) R {
|
||||
return f()
|
||||
}
|
||||
}
|
||||
|
@@ -28,3 +28,14 @@ func Memoize[K comparable, T any](f func(K) T) func(K) T {
|
||||
func ContramapMemoize[A any, K comparable, T any](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[
|
||||
A any, K comparable, T any](kf func(A) K, getOrCreate func(K, func() func() T) func() T) func(func(A) T) func(A) T {
|
||||
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)
|
||||
}
|
||||
|
1952
function/gen.go
1952
function/gen.go
File diff suppressed because it is too large
Load Diff
@@ -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.26.0
|
||||
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
|
||||
)
|
||||
|
10
go.sum
10
go.sum
@@ -8,12 +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/urfave/cli/v2 v2.26.0 h1:3f3AMg3HpThFNT4I++TKOejZO8yU55t3JnnSr4S4QEI=
|
||||
github.com/urfave/cli/v2 v2.26.0/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=
|
||||
|
311
http/builder/builder.go
Normal file
311
http/builder/builder.go
Normal file
@@ -0,0 +1,311 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package builder
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
E "github.com/IBM/fp-go/either"
|
||||
ENDO "github.com/IBM/fp-go/endomorphism"
|
||||
F "github.com/IBM/fp-go/function"
|
||||
C "github.com/IBM/fp-go/http/content"
|
||||
FM "github.com/IBM/fp-go/http/form"
|
||||
H "github.com/IBM/fp-go/http/headers"
|
||||
J "github.com/IBM/fp-go/json"
|
||||
LZ "github.com/IBM/fp-go/lazy"
|
||||
L "github.com/IBM/fp-go/optics/lens"
|
||||
O "github.com/IBM/fp-go/option"
|
||||
S "github.com/IBM/fp-go/string"
|
||||
T "github.com/IBM/fp-go/tuple"
|
||||
)
|
||||
|
||||
type (
|
||||
Builder struct {
|
||||
method O.Option[string]
|
||||
url string
|
||||
headers http.Header
|
||||
body O.Option[E.Either[error, []byte]]
|
||||
query url.Values
|
||||
}
|
||||
|
||||
// Endomorphism returns an [ENDO.Endomorphism] that transforms a builder
|
||||
Endomorphism = ENDO.Endomorphism[*Builder]
|
||||
)
|
||||
|
||||
var (
|
||||
// Default is the default builder
|
||||
Default = &Builder{method: O.Some(defaultMethod()), headers: make(http.Header), body: noBody}
|
||||
|
||||
defaultMethod = F.Constant(http.MethodGet)
|
||||
|
||||
// Monoid is the [M.Monoid] for the [Endomorphism]
|
||||
Monoid = ENDO.Monoid[*Builder]()
|
||||
|
||||
// Url is a [L.Lens] for the URL
|
||||
Url = L.MakeLensRef((*Builder).GetUrl, (*Builder).SetUrl)
|
||||
// Method is a [L.Lens] for the HTTP method
|
||||
Method = L.MakeLensRef((*Builder).GetMethod, (*Builder).SetMethod)
|
||||
// Body is a [L.Lens] for the request body
|
||||
Body = L.MakeLensRef((*Builder).GetBody, (*Builder).SetBody)
|
||||
// Headers is a [L.Lens] for the complete set of request headers
|
||||
Headers = L.MakeLensRef((*Builder).GetHeaders, (*Builder).SetHeaders)
|
||||
// Query is a [L.Lens] for the set of query parameters
|
||||
Query = L.MakeLensRef((*Builder).GetQuery, (*Builder).SetQuery)
|
||||
|
||||
rawQuery = L.MakeLensRef(getRawQuery, setRawQuery)
|
||||
|
||||
getHeader = F.Bind2of2((*Builder).GetHeader)
|
||||
delHeader = F.Bind2of2((*Builder).DelHeader)
|
||||
setHeader = F.Bind2of3((*Builder).SetHeader)
|
||||
|
||||
noHeader = O.None[string]()
|
||||
noBody = O.None[E.Either[error, []byte]]()
|
||||
noQueryArg = O.None[string]()
|
||||
|
||||
parseUrl = E.Eitherize1(url.Parse)
|
||||
parseQuery = E.Eitherize1(url.ParseQuery)
|
||||
|
||||
// WithQuery creates a [Endomorphism] for a complete set of query parameters
|
||||
WithQuery = Query.Set
|
||||
// WithMethod creates a [Endomorphism] for a certain method
|
||||
WithMethod = Method.Set
|
||||
// WithUrl creates a [Endomorphism] for a certain method
|
||||
WithUrl = Url.Set
|
||||
// WithHeaders creates a [Endomorphism] for a set of headers
|
||||
WithHeaders = Headers.Set
|
||||
// WithBody creates a [Endomorphism] for a request body
|
||||
WithBody = F.Flow2(
|
||||
O.Of[E.Either[error, []byte]],
|
||||
Body.Set,
|
||||
)
|
||||
// WithBytes creates a [Endomorphism] for a request body using bytes
|
||||
WithBytes = F.Flow2(
|
||||
E.Of[error, []byte],
|
||||
WithBody,
|
||||
)
|
||||
// WithContentType adds the [H.ContentType] header
|
||||
WithContentType = WithHeader(H.ContentType)
|
||||
// WithAuthorization adds the [H.Authorization] header
|
||||
WithAuthorization = WithHeader(H.Authorization)
|
||||
|
||||
// WithGet adds the [http.MethodGet] method
|
||||
WithGet = WithMethod(http.MethodGet)
|
||||
// WithPost adds the [http.MethodPost] method
|
||||
WithPost = WithMethod(http.MethodPost)
|
||||
// WithPut adds the [http.MethodPut] method
|
||||
WithPut = WithMethod(http.MethodPut)
|
||||
// WithDelete adds the [http.MethodDelete] method
|
||||
WithDelete = WithMethod(http.MethodDelete)
|
||||
|
||||
// WithBearer creates a [Endomorphism] to add a Bearer [H.Authorization] header
|
||||
WithBearer = F.Flow2(
|
||||
S.Format[string]("Bearer %s"),
|
||||
WithAuthorization,
|
||||
)
|
||||
|
||||
// WithoutBody creates a [Endomorphism] to remove the body
|
||||
WithoutBody = F.Pipe1(
|
||||
noBody,
|
||||
Body.Set,
|
||||
)
|
||||
|
||||
// WithFormData creates a [Endomorphism] to send form data payload
|
||||
WithFormData = F.Flow4(
|
||||
url.Values.Encode,
|
||||
S.ToBytes,
|
||||
WithBytes,
|
||||
ENDO.Chain(WithContentType(C.FormEncoded)),
|
||||
)
|
||||
)
|
||||
|
||||
func setRawQuery(u *url.URL, raw string) *url.URL {
|
||||
u.RawQuery = raw
|
||||
return u
|
||||
}
|
||||
|
||||
func getRawQuery(u *url.URL) string {
|
||||
return u.RawQuery
|
||||
}
|
||||
|
||||
func (builder *Builder) clone() *Builder {
|
||||
cpy := *builder
|
||||
cpy.headers = cpy.headers.Clone()
|
||||
return &cpy
|
||||
}
|
||||
|
||||
// GetTargetUrl constructs a full URL with query parameters on top of the provided URL string
|
||||
func (builder *Builder) GetTargetUrl() E.Either[error, string] {
|
||||
// construct the final URL
|
||||
return F.Pipe3(
|
||||
builder,
|
||||
Url.Get,
|
||||
parseUrl,
|
||||
E.Chain(F.Flow4(
|
||||
T.Replicate2[*url.URL],
|
||||
T.Map2(
|
||||
F.Flow2(
|
||||
F.Curry2(setRawQuery),
|
||||
E.Of[error, func(string) *url.URL],
|
||||
),
|
||||
F.Flow3(
|
||||
rawQuery.Get,
|
||||
parseQuery,
|
||||
E.Map[error](F.Flow2(
|
||||
F.Curry2(FM.ValuesMonoid.Concat)(builder.GetQuery()),
|
||||
(url.Values).Encode,
|
||||
)),
|
||||
),
|
||||
),
|
||||
T.Tupled2(E.MonadAp[*url.URL, error, string]),
|
||||
E.Map[error]((*url.URL).String),
|
||||
)),
|
||||
)
|
||||
}
|
||||
|
||||
func (builder *Builder) GetUrl() string {
|
||||
return builder.url
|
||||
}
|
||||
|
||||
func (builder *Builder) GetMethod() string {
|
||||
return F.Pipe1(
|
||||
builder.method,
|
||||
O.GetOrElse(defaultMethod),
|
||||
)
|
||||
}
|
||||
|
||||
func (builder *Builder) GetHeaders() http.Header {
|
||||
return builder.headers
|
||||
}
|
||||
|
||||
func (builder *Builder) GetQuery() url.Values {
|
||||
return builder.query
|
||||
}
|
||||
|
||||
func (builder *Builder) SetQuery(query url.Values) *Builder {
|
||||
builder.query = query
|
||||
return builder
|
||||
}
|
||||
|
||||
func (builder *Builder) GetBody() O.Option[E.Either[error, []byte]] {
|
||||
return builder.body
|
||||
}
|
||||
|
||||
func (builder *Builder) SetMethod(method string) *Builder {
|
||||
builder.method = O.Some(method)
|
||||
return builder
|
||||
}
|
||||
|
||||
func (builder *Builder) SetUrl(url string) *Builder {
|
||||
builder.url = url
|
||||
return builder
|
||||
}
|
||||
|
||||
func (builder *Builder) SetHeaders(headers http.Header) *Builder {
|
||||
builder.headers = headers
|
||||
return builder
|
||||
}
|
||||
|
||||
func (builder *Builder) SetBody(body O.Option[E.Either[error, []byte]]) *Builder {
|
||||
builder.body = body
|
||||
return builder
|
||||
}
|
||||
|
||||
func (builder *Builder) SetHeader(name, value string) *Builder {
|
||||
builder.headers.Set(name, value)
|
||||
return builder
|
||||
}
|
||||
|
||||
func (builder *Builder) DelHeader(name string) *Builder {
|
||||
builder.headers.Del(name)
|
||||
return builder
|
||||
}
|
||||
|
||||
func (builder *Builder) GetHeader(name string) O.Option[string] {
|
||||
return F.Pipe2(
|
||||
name,
|
||||
builder.headers.Get,
|
||||
O.FromPredicate(S.IsNonEmpty),
|
||||
)
|
||||
}
|
||||
|
||||
func (builder *Builder) GetHeaderValues(name string) []string {
|
||||
return builder.headers.Values(name)
|
||||
}
|
||||
|
||||
// Header returns a [L.Lens] for a single header
|
||||
func Header(name string) L.Lens[*Builder, O.Option[string]] {
|
||||
get := getHeader(name)
|
||||
set := F.Bind1of2(setHeader(name))
|
||||
del := F.Flow2(
|
||||
LZ.Of[*Builder],
|
||||
LZ.Map(delHeader(name)),
|
||||
)
|
||||
|
||||
return L.MakeLens(get, func(b *Builder, value O.Option[string]) *Builder {
|
||||
cpy := b.clone()
|
||||
return F.Pipe1(
|
||||
value,
|
||||
O.Fold(del(cpy), set(cpy)),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
// WithHeader creates a [Endomorphism] for a certain header
|
||||
func WithHeader(name string) func(value string) Endomorphism {
|
||||
return F.Flow2(
|
||||
O.Of[string],
|
||||
Header(name).Set,
|
||||
)
|
||||
}
|
||||
|
||||
// WithoutHeader creates a [Endomorphism] to remove a certain header
|
||||
func WithoutHeader(name string) Endomorphism {
|
||||
return Header(name).Set(noHeader)
|
||||
}
|
||||
|
||||
// WithJson creates a [Endomorphism] to send JSON payload
|
||||
func WithJson[T any](data T) Endomorphism {
|
||||
return Monoid.Concat(
|
||||
F.Pipe2(
|
||||
data,
|
||||
J.Marshal[T],
|
||||
WithBody,
|
||||
),
|
||||
WithContentType(C.Json),
|
||||
)
|
||||
}
|
||||
|
||||
// QueryArg is a [L.Lens] for the first value of a query argument
|
||||
func QueryArg(name string) L.Lens[*Builder, O.Option[string]] {
|
||||
return F.Pipe1(
|
||||
Query,
|
||||
L.Compose[*Builder](FM.AtValue(name)),
|
||||
)
|
||||
}
|
||||
|
||||
// WithQueryArg creates a [Endomorphism] for a certain query argument
|
||||
func WithQueryArg(name string) func(value string) Endomorphism {
|
||||
return F.Flow2(
|
||||
O.Of[string],
|
||||
QueryArg(name).Set,
|
||||
)
|
||||
}
|
||||
|
||||
// WithoutQueryArg creates a [Endomorphism] that removes a query argument
|
||||
func WithoutQueryArg(name string) Endomorphism {
|
||||
return QueryArg(name).Set(noQueryArg)
|
||||
}
|
68
http/builder/builder_test.go
Normal file
68
http/builder/builder_test.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 builder
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
F "github.com/IBM/fp-go/function"
|
||||
C "github.com/IBM/fp-go/http/content"
|
||||
FD "github.com/IBM/fp-go/http/form"
|
||||
H "github.com/IBM/fp-go/http/headers"
|
||||
O "github.com/IBM/fp-go/option"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestBuilder(t *testing.T) {
|
||||
|
||||
name := H.ContentType
|
||||
withContentType := WithHeader(name)
|
||||
withoutContentType := WithoutHeader(name)
|
||||
|
||||
b1 := F.Pipe1(
|
||||
Default,
|
||||
withContentType(C.Json),
|
||||
)
|
||||
|
||||
b2 := F.Pipe1(
|
||||
b1,
|
||||
withContentType(C.TextPlain),
|
||||
)
|
||||
|
||||
b3 := F.Pipe1(
|
||||
b2,
|
||||
withoutContentType,
|
||||
)
|
||||
|
||||
assert.Equal(t, O.None[string](), Default.GetHeader(name))
|
||||
assert.Equal(t, O.Of(C.Json), b1.GetHeader(name))
|
||||
assert.Equal(t, O.Of(C.TextPlain), b2.GetHeader(name))
|
||||
assert.Equal(t, O.None[string](), b3.GetHeader(name))
|
||||
}
|
||||
|
||||
func TestWithFormData(t *testing.T) {
|
||||
data := F.Pipe1(
|
||||
FD.Default,
|
||||
FD.WithValue("a")("b"),
|
||||
)
|
||||
|
||||
res := F.Pipe1(
|
||||
Default,
|
||||
WithFormData(data),
|
||||
)
|
||||
|
||||
assert.Equal(t, C.FormEncoded, Headers.Get(res).Get(H.ContentType))
|
||||
}
|
22
http/content/content.go
Normal file
22
http/content/content.go
Normal file
@@ -0,0 +1,22 @@
|
||||
// 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"
|
||||
FormEncoded = "application/x-www-form-urlencoded"
|
||||
)
|
@@ -25,11 +25,12 @@ import (
|
||||
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 (
|
||||
// FormEndomorphism returns an [ENDO.Endomorphism] that transforms a form
|
||||
FormEndomorphism = ENDO.Endomorphism[url.Values]
|
||||
// Endomorphism returns an [ENDO.Endomorphism] that transforms a form
|
||||
Endomorphism = ENDO.Endomorphism[url.Values]
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -38,8 +39,11 @@ var (
|
||||
|
||||
noField = O.None[string]()
|
||||
|
||||
// FormMonoid is the [M.Monoid] for the [FormEndomorphism]
|
||||
FormMonoid = ENDO.Monoid[url.Values]()
|
||||
// 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]
|
||||
@@ -57,15 +61,14 @@ var (
|
||||
)
|
||||
|
||||
// WithValue creates a [FormBuilder] for a certain field
|
||||
func WithValue(name string) func(value string) FormEndomorphism {
|
||||
return F.Flow3(
|
||||
func WithValue(name string) func(value string) Endomorphism {
|
||||
return F.Flow2(
|
||||
O.Of[string],
|
||||
AtValue(name).Set,
|
||||
ENDO.Of[func(url.Values) url.Values],
|
||||
)
|
||||
}
|
||||
|
||||
// WithoutValue creates a [FormBuilder] that removes a field
|
||||
func WithoutValue(name string) FormEndomorphism {
|
||||
func WithoutValue(name string) Endomorphism {
|
||||
return AtValue(name).Set(noField)
|
||||
}
|
||||
|
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))
|
||||
}
|
@@ -20,6 +20,7 @@ import (
|
||||
"io"
|
||||
"mime"
|
||||
H "net/http"
|
||||
"net/url"
|
||||
"regexp"
|
||||
|
||||
A "github.com/IBM/fp-go/array"
|
||||
@@ -33,6 +34,13 @@ import (
|
||||
|
||||
type (
|
||||
ParsedMediaType = T.Tuple2[string, map[string]string]
|
||||
|
||||
HttpError struct {
|
||||
statusCode int
|
||||
headers H.Header
|
||||
body []byte
|
||||
url *url.URL
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -72,6 +80,31 @@ func ParseMediaType(mediaType string) E.Either[error, ParsedMediaType] {
|
||||
return E.TryCatchError(T.MakeTuple2(m, p), err)
|
||||
}
|
||||
|
||||
// Error fulfills the error interface
|
||||
func (r *HttpError) Error() string {
|
||||
return fmt.Sprintf("invalid status code [%d] when accessing URL [%s]", r.statusCode, r.url)
|
||||
}
|
||||
|
||||
func (r *HttpError) String() string {
|
||||
return r.Error()
|
||||
}
|
||||
|
||||
func (r *HttpError) StatusCode() int {
|
||||
return r.statusCode
|
||||
}
|
||||
|
||||
func (r *HttpError) Headers() H.Header {
|
||||
return r.headers
|
||||
}
|
||||
|
||||
func (r *HttpError) URL() *url.URL {
|
||||
return r.url
|
||||
}
|
||||
|
||||
func (r *HttpError) Body() []byte {
|
||||
return r.body
|
||||
}
|
||||
|
||||
func GetHeader(resp *H.Response) H.Header {
|
||||
return resp.Header
|
||||
}
|
||||
@@ -84,6 +117,13 @@ func isValidStatus(resp *H.Response) bool {
|
||||
return resp.StatusCode >= H.StatusOK && resp.StatusCode < H.StatusMultipleChoices
|
||||
}
|
||||
|
||||
// StatusCodeError creates an instance of [HttpError] filled with information from the response
|
||||
func StatusCodeError(resp *H.Response) error {
|
||||
return fmt.Errorf("invalid status code [%d] when accessing URL [%s]", resp.StatusCode, resp.Request.URL)
|
||||
// read the body
|
||||
bodyRdr := GetBody(resp)
|
||||
defer bodyRdr.Close()
|
||||
// try to access body content
|
||||
body, _ := io.ReadAll(bodyRdr)
|
||||
// return an error with comprehensive information
|
||||
return &HttpError{statusCode: resp.StatusCode, headers: GetHeader(resp).Clone(), body: body, url: resp.Request.URL}
|
||||
}
|
||||
|
@@ -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),
|
||||
)
|
||||
|
||||
|
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,
|
||||
)
|
||||
}
|
@@ -37,6 +37,16 @@ func MonadAp[HKTGA, HKTGB, HKTGAB, HKTFGAB, HKTFGGAB, HKTFGA, HKTFGB any](
|
||||
return fap(fmap(fab, F.Bind1st(F.Bind1st[HKTGAB, HKTGA, HKTGB], gap)), fa)
|
||||
}
|
||||
|
||||
// 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 +117,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),
|
||||
)
|
||||
}
|
||||
|
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]),
|
||||
))
|
||||
}
|
@@ -52,10 +52,59 @@ func ChainFirst[A, B, HKTA, HKTB any](
|
||||
}
|
||||
|
||||
func Chain[A, B, HKTA, HKTB any](
|
||||
mchain func(HKTA, func(A) HKTB) HKTB,
|
||||
mchain func(func(A) HKTB) func(HKTA) HKTB,
|
||||
f func(A) HKTB,
|
||||
) 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)
|
||||
}
|
||||
|
@@ -47,7 +47,7 @@ func MonadChainIOK[GR ~func() B, A, B, HKTA, HKTB any](
|
||||
}
|
||||
|
||||
func ChainIOK[GR ~func() B, A, B, HKTA, HKTB any](
|
||||
mchain func(HKTA, func(A) HKTB) HKTB,
|
||||
mchain func(func(A) HKTB) func(HKTA) HKTB,
|
||||
fromio func(GR) HKTB,
|
||||
f func(A) GR) func(HKTA) HKTB {
|
||||
// chain
|
||||
|
@@ -48,7 +48,7 @@ func MonadChainIOEitherK[GIOB ~func() ET.Either[E, B], E, A, B, HKTA, HKTB any](
|
||||
}
|
||||
|
||||
func ChainIOEitherK[GIOB ~func() ET.Either[E, B], E, A, B, HKTA, HKTB any](
|
||||
mchain func(HKTA, func(A) HKTB) HKTB,
|
||||
mchain func(func(A) HKTB) func(HKTA) HKTB,
|
||||
fromio func(GIOB) HKTB,
|
||||
f func(A) GIOB) func(HKTA) HKTB {
|
||||
// chain
|
||||
|
@@ -21,6 +21,54 @@ import (
|
||||
|
||||
// HKTFGA = HKT[F, HKT[G, A]]
|
||||
// HKTFGB = HKT[F, HKT[G, B]]
|
||||
func MonadMap[A, B, HKTGA, HKTGB, HKTFGA, HKTFGB any](fmap func(HKTFGA, func(HKTGA) HKTGB) HKTFGB, gmap func(HKTGA, func(A) B) HKTGB, fa HKTFGA, f func(A) B) HKTFGB {
|
||||
func MonadMap[A, B, HKTGA, HKTGB, HKTFGA, HKTFGB any](
|
||||
fmap func(HKTFGA, func(HKTGA) HKTGB) HKTFGB,
|
||||
gmap func(HKTGA, func(A) B) HKTGB,
|
||||
fa HKTFGA,
|
||||
f func(A) B) HKTFGB {
|
||||
return fmap(fa, F.Bind2nd(gmap, f))
|
||||
}
|
||||
|
||||
func Map[A, B, HKTGA, HKTGB, HKTFGA, HKTFGB any](
|
||||
fmap func(func(HKTGA) HKTGB) func(HKTFGA) HKTFGB,
|
||||
gmap func(func(A) B) func(HKTGA) HKTGB,
|
||||
f func(A) B) func(HKTFGA) HKTFGB {
|
||||
return F.Pipe2(
|
||||
f,
|
||||
gmap,
|
||||
fmap,
|
||||
)
|
||||
}
|
||||
|
||||
func MonadLet[S1, S2, B, HKTS1, HKTS2 any](
|
||||
mmap func(HKTS1, func(S1) S2) HKTS2,
|
||||
first HKTS1,
|
||||
key func(B) func(S1) S2,
|
||||
f func(S1) B,
|
||||
) HKTS2 {
|
||||
return mmap(first, func(s1 S1) S2 {
|
||||
return key(f(s1))(s1)
|
||||
})
|
||||
}
|
||||
|
||||
func Let[S1, S2, B, HKTS1, HKTS2 any](
|
||||
mmap func(func(S1) S2) func(HKTS1) HKTS2,
|
||||
key func(B) func(S1) S2,
|
||||
f func(S1) B,
|
||||
) func(HKTS1) HKTS2 {
|
||||
return mmap(func(s1 S1) S2 {
|
||||
return key(f(s1))(s1)
|
||||
})
|
||||
}
|
||||
|
||||
func LetTo[S1, S2, B, HKTS1, HKTS2 any](
|
||||
mmap func(func(S1) S2) func(HKTS1) HKTS2,
|
||||
key func(B) func(S1) S2,
|
||||
b B,
|
||||
) func(HKTS1) HKTS2 {
|
||||
return F.Pipe2(
|
||||
b,
|
||||
key,
|
||||
mmap,
|
||||
)
|
||||
}
|
||||
|
@@ -64,9 +64,6 @@ func MonadAp[A, B, HKTFAB, HKTFGAB, HKTFA, HKTFB any](
|
||||
fmap func(HKTFAB, func(O.Option[func(A) B]) func(O.Option[A]) O.Option[B]) HKTFGAB,
|
||||
fab HKTFAB,
|
||||
fa HKTFA) HKTFB {
|
||||
// HKTGA = O.Option[A]
|
||||
// HKTGB = O.Option[B]
|
||||
// HKTGAB = O.Option[func(a A) B]
|
||||
return apply.MonadAp(fap, fmap, O.MonadAp[B, A], fab, fa)
|
||||
}
|
||||
|
||||
|
@@ -23,10 +23,25 @@ import (
|
||||
// here we implement the monadic operations using callbacks from
|
||||
// higher kinded types, as good a golang allows use to do this
|
||||
|
||||
func MonadMap[GEA ~func(E) HKTA, GEB ~func(E) HKTB, E, A, B, HKTA, HKTB any](fmap func(HKTA, func(A) B) HKTB, fa GEA, f func(A) B) GEB {
|
||||
func MonadMap[GEA ~func(E) HKTA, GEB ~func(E) HKTB, E, A, B, HKTA, HKTB any](
|
||||
fmap func(HKTA, func(A) B) HKTB,
|
||||
fa GEA,
|
||||
f func(A) B,
|
||||
) GEB {
|
||||
return R.MonadMap[GEA, GEB](fa, F.Bind2nd(fmap, f))
|
||||
}
|
||||
|
||||
func Map[GEA ~func(E) HKTA, GEB ~func(E) HKTB, E, A, B, HKTA, HKTB any](
|
||||
fmap func(func(A) B) func(HKTA) HKTB,
|
||||
f func(A) B,
|
||||
) func(GEA) GEB {
|
||||
return F.Pipe2(
|
||||
f,
|
||||
fmap,
|
||||
R.Map[GEA, GEB, E, HKTA, HKTB],
|
||||
)
|
||||
}
|
||||
|
||||
func MonadChain[GEA ~func(E) HKTA, GEB ~func(E) HKTB, A, E, HKTA, HKTB any](fchain func(HKTA, func(A) HKTB) HKTB, ma GEA, f func(A) GEB) GEB {
|
||||
return R.MakeReader(func(r E) HKTB {
|
||||
return fchain(ma(r), func(a A) HKTB {
|
||||
|
59
internal/utils/do.go
Normal file
59
internal/utils/do.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 utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
F "github.com/IBM/fp-go/function"
|
||||
)
|
||||
|
||||
type (
|
||||
Initial struct {
|
||||
}
|
||||
|
||||
WithLastName struct {
|
||||
Initial
|
||||
LastName string
|
||||
}
|
||||
|
||||
WithGivenName struct {
|
||||
WithLastName
|
||||
GivenName string
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
Empty = Initial{}
|
||||
|
||||
SetLastName = F.Curry2(func(name string, s1 Initial) WithLastName {
|
||||
return WithLastName{
|
||||
Initial: s1,
|
||||
LastName: name,
|
||||
}
|
||||
})
|
||||
|
||||
SetGivenName = F.Curry2(func(name string, s1 WithLastName) WithGivenName {
|
||||
return WithGivenName{
|
||||
WithLastName: s1,
|
||||
GivenName: name,
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
func GetFullName(s WithGivenName) string {
|
||||
return fmt.Sprintf("%s %s", s.GivenName, s.LastName)
|
||||
}
|
66
io/bind.go
Normal file
66
io/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 io
|
||||
|
||||
import (
|
||||
G "github.com/IBM/fp-go/io/generic"
|
||||
)
|
||||
|
||||
// Bind creates an empty context of type [S] to be used with the [Bind] operation
|
||||
func Do[S any](
|
||||
empty S,
|
||||
) IO[S] {
|
||||
return G.Do[IO[S], S](empty)
|
||||
}
|
||||
|
||||
// Bind attaches the result of a computation to a context [S1] to produce a context [S2]
|
||||
func Bind[S1, S2, T any](
|
||||
setter func(T) func(S1) S2,
|
||||
f func(S1) IO[T],
|
||||
) func(IO[S1]) IO[S2] {
|
||||
return G.Bind[IO[S1], IO[S2], IO[T], S1, S2, T](setter, f)
|
||||
}
|
||||
|
||||
// Let attaches the result of a computation to a context [S1] to produce a context [S2]
|
||||
func Let[S1, S2, T any](
|
||||
setter func(T) func(S1) S2,
|
||||
f func(S1) T,
|
||||
) func(IO[S1]) IO[S2] {
|
||||
return G.Let[IO[S1], IO[S2], S1, S2, T](setter, f)
|
||||
}
|
||||
|
||||
// LetTo attaches the a value to a context [S1] to produce a context [S2]
|
||||
func LetTo[S1, S2, T any](
|
||||
setter func(T) func(S1) S2,
|
||||
b T,
|
||||
) func(IO[S1]) IO[S2] {
|
||||
return G.LetTo[IO[S1], IO[S2], S1, S2, T](setter, b)
|
||||
}
|
||||
|
||||
// BindTo initializes a new state [S1] from a value [T]
|
||||
func BindTo[S1, T any](
|
||||
setter func(T) S1,
|
||||
) func(IO[T]) IO[S1] {
|
||||
return G.BindTo[IO[S1], IO[T], S1, T](setter)
|
||||
}
|
||||
|
||||
// ApS attaches a value to a context [S1] to produce a context [S2] by considering the context and the value concurrently
|
||||
func ApS[S1, S2, T any](
|
||||
setter func(T) func(S1) S2,
|
||||
fa IO[T],
|
||||
) func(IO[S1]) IO[S2] {
|
||||
return G.ApS[IO[S1], IO[S2], IO[T], S1, S2, T](setter, fa)
|
||||
}
|
56
io/bind_test.go
Normal file
56
io/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 io
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
F "github.com/IBM/fp-go/function"
|
||||
"github.com/IBM/fp-go/internal/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func getLastName(s utils.Initial) IO[string] {
|
||||
return Of("Doe")
|
||||
}
|
||||
|
||||
func getGivenName(s utils.WithLastName) IO[string] {
|
||||
return Of("John")
|
||||
}
|
||||
|
||||
func TestBind(t *testing.T) {
|
||||
|
||||
res := F.Pipe3(
|
||||
Do(utils.Empty),
|
||||
Bind(utils.SetLastName, getLastName),
|
||||
Bind(utils.SetGivenName, getGivenName),
|
||||
Map(utils.GetFullName),
|
||||
)
|
||||
|
||||
assert.Equal(t, res(), "John Doe")
|
||||
}
|
||||
|
||||
func TestApS(t *testing.T) {
|
||||
|
||||
res := F.Pipe3(
|
||||
Do(utils.Empty),
|
||||
ApS(utils.SetLastName, Of("Doe")),
|
||||
ApS(utils.SetGivenName, Of("John")),
|
||||
Map(utils.GetFullName),
|
||||
)
|
||||
|
||||
assert.Equal(t, res(), "John Doe")
|
||||
}
|
89
io/generic/bind.go
Normal file
89
io/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 ~func() S, S any](
|
||||
empty S,
|
||||
) GS {
|
||||
return Of[GS](empty)
|
||||
}
|
||||
|
||||
// Bind attaches the result of a computation to a context [S1] to produce a context [S2]
|
||||
func Bind[GS1 ~func() S1, GS2 ~func() S2, GT ~func() T, S1, S2, T any](
|
||||
setter func(T) func(S1) S2,
|
||||
f func(S1) GT,
|
||||
) func(GS1) GS2 {
|
||||
return C.Bind(
|
||||
Chain[GS1, GS2, S1, S2],
|
||||
Map[GT, GS2, T, S2],
|
||||
setter,
|
||||
f,
|
||||
)
|
||||
}
|
||||
|
||||
// Let attaches the result of a computation to a context [S1] to produce a context [S2]
|
||||
func Let[GS1 ~func() S1, GS2 ~func() S2, S1, S2, T any](
|
||||
key func(T) func(S1) S2,
|
||||
f func(S1) T,
|
||||
) func(GS1) GS2 {
|
||||
return F.Let(
|
||||
Map[GS1, GS2, S1, S2],
|
||||
key,
|
||||
f,
|
||||
)
|
||||
}
|
||||
|
||||
// LetTo attaches the a value to a context [S1] to produce a context [S2]
|
||||
func LetTo[GS1 ~func() S1, GS2 ~func() S2, S1, S2, B any](
|
||||
key func(B) func(S1) S2,
|
||||
b B,
|
||||
) func(GS1) GS2 {
|
||||
return F.LetTo(
|
||||
Map[GS1, GS2, S1, S2],
|
||||
key,
|
||||
b,
|
||||
)
|
||||
}
|
||||
|
||||
// BindTo initializes a new state [S1] from a value [T]
|
||||
func BindTo[GS1 ~func() S1, GT ~func() T, S1, T any](
|
||||
setter func(T) S1,
|
||||
) func(GT) GS1 {
|
||||
return C.BindTo(
|
||||
Map[GT, GS1, T, S1],
|
||||
setter,
|
||||
)
|
||||
}
|
||||
|
||||
// ApS attaches a value to a context [S1] to produce a context [S2] by considering the context and the value concurrently
|
||||
func ApS[GS1 ~func() S1, GS2 ~func() S2, GT ~func() T, S1, S2, T any](
|
||||
setter func(T) func(S1) S2,
|
||||
fa GT,
|
||||
) func(GS1) GS2 {
|
||||
return A.ApS(
|
||||
Ap[GS2, func() func(T) S2, GT, S2, T],
|
||||
Map[GS1, func() func(T) S2, S1, func(T) S2],
|
||||
setter,
|
||||
fa,
|
||||
)
|
||||
}
|
@@ -22,6 +22,7 @@ import (
|
||||
C "github.com/IBM/fp-go/internal/chain"
|
||||
FC "github.com/IBM/fp-go/internal/functor"
|
||||
L "github.com/IBM/fp-go/internal/lazy"
|
||||
T "github.com/IBM/fp-go/tuple"
|
||||
)
|
||||
|
||||
// type IO[A any] = func() A
|
||||
@@ -133,6 +134,29 @@ func Delay[GA ~func() A, A any](delay time.Duration) func(GA) GA {
|
||||
}
|
||||
}
|
||||
|
||||
func after(timestamp time.Time) func() {
|
||||
return func() {
|
||||
// check if we need to wait
|
||||
current := time.Now()
|
||||
if current.Before(timestamp) {
|
||||
time.Sleep(timestamp.Sub(current))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// After creates an operation that passes after the given timestamp
|
||||
func After[GA ~func() A, A any](timestamp time.Time) func(GA) GA {
|
||||
aft := after(timestamp)
|
||||
return func(ga GA) GA {
|
||||
return MakeIO[GA](func() A {
|
||||
// wait as long as necessary
|
||||
aft()
|
||||
// execute after wait
|
||||
return ga()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Now returns the current timestamp
|
||||
func Now[GA ~func() time.Time]() GA {
|
||||
return MakeIO[GA](time.Now)
|
||||
@@ -152,3 +176,23 @@ func MonadFlap[FAB ~func(A) B, GFAB ~func() FAB, GB ~func() B, A, B any](fab GFA
|
||||
func Flap[FAB ~func(A) B, GFAB ~func() FAB, GB ~func() B, A, B any](a A) func(GFAB) GB {
|
||||
return F.Bind2nd(MonadFlap[FAB, GFAB, GB, A, B], a)
|
||||
}
|
||||
|
||||
// WithTime returns an operation that measures the start and end timestamp of the operation
|
||||
func WithTime[GTA ~func() T.Tuple3[A, time.Time, time.Time], GA ~func() A, A any](a GA) GTA {
|
||||
return MakeIO[GTA](func() T.Tuple3[A, time.Time, time.Time] {
|
||||
t0 := time.Now()
|
||||
res := a()
|
||||
t1 := time.Now()
|
||||
return T.MakeTuple3(res, t0, t1)
|
||||
})
|
||||
}
|
||||
|
||||
// WithDuration returns an operation that measures the duration of the operation
|
||||
func WithDuration[GTA ~func() T.Tuple2[A, time.Duration], GA ~func() A, A any](a GA) GTA {
|
||||
return MakeIO[GTA](func() T.Tuple2[A, time.Duration] {
|
||||
t0 := time.Now()
|
||||
res := a()
|
||||
t1 := time.Now()
|
||||
return T.MakeTuple2(res, t1.Sub(t0))
|
||||
})
|
||||
}
|
||||
|
23
io/io.go
23
io/io.go
@@ -19,6 +19,7 @@ import (
|
||||
"time"
|
||||
|
||||
G "github.com/IBM/fp-go/io/generic"
|
||||
T "github.com/IBM/fp-go/tuple"
|
||||
)
|
||||
|
||||
// IO represents a synchronous computation that cannot fail
|
||||
@@ -143,6 +144,26 @@ func MonadFlap[B, A any](fab IO[func(A) B], a A) IO[B] {
|
||||
return G.MonadFlap[func(A) B, IO[func(A) B], IO[B], A, B](fab, a)
|
||||
}
|
||||
|
||||
func Flap[FAB ~func(A) B, GFAB ~func() FAB, GB ~func() B, A, B any](a A) func(IO[func(A) B]) IO[B] {
|
||||
func Flap[B, A any](a A) func(IO[func(A) B]) IO[B] {
|
||||
return G.Flap[func(A) B, IO[func(A) B], IO[B], A, B](a)
|
||||
}
|
||||
|
||||
// Delay creates an operation that passes in the value after some delay
|
||||
func Delay[A any](delay time.Duration) func(IO[A]) IO[A] {
|
||||
return G.Delay[IO[A]](delay)
|
||||
}
|
||||
|
||||
// After creates an operation that passes after the given timestamp
|
||||
func After[A any](timestamp time.Time) func(IO[A]) IO[A] {
|
||||
return G.After[IO[A]](timestamp)
|
||||
}
|
||||
|
||||
// WithTime returns an operation that measures the start and end [time.Time] of the operation
|
||||
func WithTime[A any](a IO[A]) IO[T.Tuple3[A, time.Time, time.Time]] {
|
||||
return G.WithTime[IO[T.Tuple3[A, time.Time, time.Time]], IO[A]](a)
|
||||
}
|
||||
|
||||
// WithDuration returns an operation that measures the [time.Duration]
|
||||
func WithDuration[A any](a IO[A]) IO[T.Tuple2[A, time.Duration]] {
|
||||
return G.WithDuration[IO[T.Tuple2[A, time.Duration]], IO[A]](a)
|
||||
}
|
||||
|
@@ -19,26 +19,48 @@ import (
|
||||
G "github.com/IBM/fp-go/ioeither/generic"
|
||||
)
|
||||
|
||||
// Bind applies a function to an input state and merges the result into that state
|
||||
func Bind[E, A, S1, S2 any](s func(A) func(S1) S2, f func(S1) IOEither[E, A]) func(IOEither[E, S1]) IOEither[E, S2] {
|
||||
return G.Bind[IOEither[E, S1], IOEither[E, S2], IOEither[E, A], func(S1) IOEither[E, A]](s, f)
|
||||
// Bind creates an empty context of type [S] to be used with the [Bind] operation
|
||||
func Do[E, S any](
|
||||
empty S,
|
||||
) IOEither[E, S] {
|
||||
return G.Do[IOEither[E, S], E, S](empty)
|
||||
}
|
||||
|
||||
// BindTo initializes some state based on a value
|
||||
func BindTo[
|
||||
E, A, S2 any](s func(A) S2) func(IOEither[E, A]) IOEither[E, S2] {
|
||||
return G.BindTo[IOEither[E, S2], IOEither[E, A]](s)
|
||||
}
|
||||
|
||||
func ApS[
|
||||
E, A, S1, S2 any,
|
||||
](s func(A) func(S1) S2, fa IOEither[E, A]) func(IOEither[E, S1]) IOEither[E, S2] {
|
||||
return G.ApS[IOEither[E, S1], IOEither[E, S2], IOEither[E, A], IOEither[E, func(S1) S2]](s, fa)
|
||||
}
|
||||
|
||||
func Let[E, A, S1, S2 any](
|
||||
s func(A) func(S1) S2,
|
||||
f func(S1) A,
|
||||
// Bind attaches the result of a computation to a context [S1] to produce a context [S2]
|
||||
func Bind[E, S1, S2, T any](
|
||||
setter func(T) func(S1) S2,
|
||||
f func(S1) IOEither[E, T],
|
||||
) func(IOEither[E, S1]) IOEither[E, S2] {
|
||||
return G.Let[IOEither[E, S1], IOEither[E, S2]](s, f)
|
||||
return G.Bind[IOEither[E, S1], IOEither[E, S2], IOEither[E, T], E, S1, S2, T](setter, f)
|
||||
}
|
||||
|
||||
// Let attaches the result of a computation to a context [S1] to produce a context [S2]
|
||||
func Let[E, S1, S2, T any](
|
||||
setter func(T) func(S1) S2,
|
||||
f func(S1) T,
|
||||
) func(IOEither[E, S1]) IOEither[E, S2] {
|
||||
return G.Let[IOEither[E, S1], IOEither[E, S2], E, S1, S2, T](setter, f)
|
||||
}
|
||||
|
||||
// LetTo attaches the a value to a context [S1] to produce a context [S2]
|
||||
func LetTo[E, S1, S2, T any](
|
||||
setter func(T) func(S1) S2,
|
||||
b T,
|
||||
) func(IOEither[E, S1]) IOEither[E, S2] {
|
||||
return G.LetTo[IOEither[E, S1], IOEither[E, S2], E, S1, S2, T](setter, b)
|
||||
}
|
||||
|
||||
// BindTo initializes a new state [S1] from a value [T]
|
||||
func BindTo[E, S1, T any](
|
||||
setter func(T) S1,
|
||||
) func(IOEither[E, T]) IOEither[E, S1] {
|
||||
return G.BindTo[IOEither[E, S1], IOEither[E, T], E, S1, T](setter)
|
||||
}
|
||||
|
||||
// ApS attaches a value to a context [S1] to produce a context [S2] by considering the context and the value concurrently
|
||||
func ApS[E, S1, S2, T any](
|
||||
setter func(T) func(S1) S2,
|
||||
fa IOEither[E, T],
|
||||
) func(IOEither[E, S1]) IOEither[E, S2] {
|
||||
return G.ApS[IOEither[E, S1], IOEither[E, S2], IOEither[E, T], E, S1, S2, T](setter, fa)
|
||||
}
|
||||
|
57
ioeither/bind_test.go
Normal file
57
ioeither/bind_test.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 ioeither
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
E "github.com/IBM/fp-go/either"
|
||||
F "github.com/IBM/fp-go/function"
|
||||
"github.com/IBM/fp-go/internal/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func getLastName(s utils.Initial) IOEither[error, string] {
|
||||
return Of[error]("Doe")
|
||||
}
|
||||
|
||||
func getGivenName(s utils.WithLastName) IOEither[error, string] {
|
||||
return Of[error]("John")
|
||||
}
|
||||
|
||||
func TestBind(t *testing.T) {
|
||||
|
||||
res := F.Pipe3(
|
||||
Do[error](utils.Empty),
|
||||
Bind(utils.SetLastName, getLastName),
|
||||
Bind(utils.SetGivenName, getGivenName),
|
||||
Map[error](utils.GetFullName),
|
||||
)
|
||||
|
||||
assert.Equal(t, res(), E.Of[error]("John Doe"))
|
||||
}
|
||||
|
||||
func TestApS(t *testing.T) {
|
||||
|
||||
res := F.Pipe3(
|
||||
Do[error](utils.Empty),
|
||||
ApS(utils.SetLastName, Of[error]("Doe")),
|
||||
ApS(utils.SetGivenName, Of[error]("John")),
|
||||
Map[error](utils.GetFullName),
|
||||
)
|
||||
|
||||
assert.Equal(t, res(), E.Of[error]("John Doe"))
|
||||
}
|
@@ -26,7 +26,7 @@ func Eq[E, A any](eq EQ.Eq[ET.Either[E, A]]) EQ.Eq[IOEither[E, A]] {
|
||||
return G.Eq[IOEither[E, A]](eq)
|
||||
}
|
||||
|
||||
// FromStrictEquals constructs an `Eq` from the canonical comparison function
|
||||
// FromStrictEquals constructs an [EQ.Eq] from the canonical comparison function
|
||||
func FromStrictEquals[E, A comparable]() EQ.Eq[IOEither[E, A]] {
|
||||
return G.FromStrictEquals[IOEither[E, A]]()
|
||||
}
|
||||
|
36
ioeither/file/dir.go
Normal file
36
ioeither/file/dir.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 file
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
IOE "github.com/IBM/fp-go/ioeither"
|
||||
)
|
||||
|
||||
// MkdirAll create a sequence of directories, see [os.MkdirAll]
|
||||
func MkdirAll(path string, perm os.FileMode) IOE.IOEither[error, string] {
|
||||
return IOE.TryCatchError(func() (string, error) {
|
||||
return path, os.MkdirAll(path, perm)
|
||||
})
|
||||
}
|
||||
|
||||
// Mkdir create a directory, see [os.Mkdir]
|
||||
func Mkdir(path string, perm os.FileMode) IOE.IOEither[error, string] {
|
||||
return IOE.TryCatchError(func() (string, error) {
|
||||
return path, os.Mkdir(path, perm)
|
||||
})
|
||||
}
|
@@ -17,63 +17,74 @@ package generic
|
||||
|
||||
import (
|
||||
ET "github.com/IBM/fp-go/either"
|
||||
G "github.com/IBM/fp-go/internal/bindt"
|
||||
A "github.com/IBM/fp-go/internal/apply"
|
||||
C "github.com/IBM/fp-go/internal/chain"
|
||||
F "github.com/IBM/fp-go/internal/functor"
|
||||
)
|
||||
|
||||
// Bind applies a function to an input state and merges the result into that state
|
||||
func Bind[
|
||||
GS1 ~func() ET.Either[E, S1],
|
||||
GS2 ~func() ET.Either[E, S2],
|
||||
GA ~func() ET.Either[E, A],
|
||||
FCT ~func(S1) GA,
|
||||
E any,
|
||||
SET ~func(A) func(S1) S2,
|
||||
A, S1, S2 any](s SET, f FCT) func(GS1) GS2 {
|
||||
return G.Bind(
|
||||
// Bind creates an empty context of type [S] to be used with the [Bind] operation
|
||||
func Do[GS ~func() ET.Either[E, S], E, S any](
|
||||
empty S,
|
||||
) GS {
|
||||
return Of[GS, E, S](empty)
|
||||
}
|
||||
|
||||
// Bind attaches the result of a computation to a context [S1] to produce a context [S2]
|
||||
func Bind[GS1 ~func() ET.Either[E, S1], GS2 ~func() ET.Either[E, S2], GT ~func() ET.Either[E, T], E, S1, S2, T any](
|
||||
setter func(T) func(S1) S2,
|
||||
f func(S1) GT,
|
||||
) func(GS1) GS2 {
|
||||
return C.Bind(
|
||||
Chain[GS1, GS2, E, S1, S2],
|
||||
Map[GA, GS2, E, A, S2],
|
||||
s,
|
||||
Map[GT, GS2, E, T, S2],
|
||||
setter,
|
||||
f,
|
||||
)
|
||||
}
|
||||
|
||||
// BindTo initializes some state based on a value
|
||||
func BindTo[
|
||||
GS2 ~func() ET.Either[E, S2],
|
||||
GA ~func() ET.Either[E, A],
|
||||
E any,
|
||||
SET ~func(A) S2,
|
||||
A, S2 any](s SET) func(GA) GS2 {
|
||||
return G.BindTo(
|
||||
Map[GA, GS2, E, A, S2],
|
||||
s,
|
||||
)
|
||||
}
|
||||
|
||||
func ApS[
|
||||
GS1 ~func() ET.Either[E, S1],
|
||||
GS2 ~func() ET.Either[E, S2],
|
||||
GB ~func() ET.Either[E, B],
|
||||
GS1S2 ~func() ET.Either[E, func(S1) S2],
|
||||
SET ~func(B) func(S1) S2,
|
||||
E, S1, S2, B any,
|
||||
](s SET, fb GB) func(GS1) GS2 {
|
||||
return G.ApS[SET, S1, S2, B, GS1S2, GS1, GS2, GB](
|
||||
Ap[GS2, GS1S2, GS1, E, S1, S2],
|
||||
Map[GB, GS1S2, E, B, func(S1) S2],
|
||||
s,
|
||||
fb,
|
||||
)
|
||||
}
|
||||
|
||||
func Let[
|
||||
GS1 ~func() ET.Either[E, S1],
|
||||
GS2 ~func() ET.Either[E, S2],
|
||||
SET ~func(B) func(S1) S2,
|
||||
FCT ~func(S1) B,
|
||||
E, S1, S2, B any](
|
||||
s SET,
|
||||
f FCT,
|
||||
// Let attaches the result of a computation to a context [S1] to produce a context [S2]
|
||||
func Let[GS1 ~func() ET.Either[E, S1], GS2 ~func() ET.Either[E, S2], E, S1, S2, T any](
|
||||
key func(T) func(S1) S2,
|
||||
f func(S1) T,
|
||||
) func(GS1) GS2 {
|
||||
return G.Let[SET, FCT, S1, S2, B, GS1, GS2](Map[GS1, GS2, E, S1, S2], s, f)
|
||||
return F.Let(
|
||||
Map[GS1, GS2, E, S1, S2],
|
||||
key,
|
||||
f,
|
||||
)
|
||||
}
|
||||
|
||||
// LetTo attaches the a value to a context [S1] to produce a context [S2]
|
||||
func LetTo[GS1 ~func() ET.Either[E, S1], GS2 ~func() ET.Either[E, S2], E, S1, S2, B any](
|
||||
key func(B) func(S1) S2,
|
||||
b B,
|
||||
) func(GS1) GS2 {
|
||||
return F.LetTo(
|
||||
Map[GS1, GS2, E, S1, S2],
|
||||
key,
|
||||
b,
|
||||
)
|
||||
}
|
||||
|
||||
// BindTo initializes a new state [S1] from a value [T]
|
||||
func BindTo[GS1 ~func() ET.Either[E, S1], GT ~func() ET.Either[E, T], E, S1, S2, T any](
|
||||
setter func(T) S1,
|
||||
) func(GT) GS1 {
|
||||
return C.BindTo(
|
||||
Map[GT, GS1, E, T, S1],
|
||||
setter,
|
||||
)
|
||||
}
|
||||
|
||||
// ApS attaches a value to a context [S1] to produce a context [S2] by considering the context and the value concurrently
|
||||
func ApS[GS1 ~func() ET.Either[E, S1], GS2 ~func() ET.Either[E, S2], GT ~func() ET.Either[E, T], E, S1, S2, T any](
|
||||
setter func(T) func(S1) S2,
|
||||
fa GT,
|
||||
) func(GS1) GS2 {
|
||||
return A.ApS(
|
||||
Ap[GS2, func() ET.Either[E, func(T) S2], GT, E, T, S2],
|
||||
Map[GS1, func() ET.Either[E, func(T) S2], E, S1, func(T) S2],
|
||||
setter,
|
||||
fa,
|
||||
)
|
||||
}
|
||||
|
@@ -26,7 +26,7 @@ func Eq[GA ~func() ET.Either[E, A], E, A any](eq EQ.Eq[ET.Either[E, A]]) EQ.Eq[G
|
||||
return G.Eq[GA](eq)
|
||||
}
|
||||
|
||||
// FromStrictEquals constructs an `Eq` from the canonical comparison function
|
||||
// FromStrictEquals constructs an [EQ.Eq] from the canonical comparison function
|
||||
func FromStrictEquals[GA ~func() ET.Either[E, A], E, A comparable]() EQ.Eq[GA] {
|
||||
return Eq[GA](ET.FromStrictEquals[E, A]())
|
||||
}
|
||||
|
@@ -134,7 +134,7 @@ func MonadChainIOK[GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B], GR ~f
|
||||
|
||||
func ChainIOK[GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B], GR ~func() B, E, A, B any](f func(A) GR) func(GA) GB {
|
||||
return FI.ChainIOK(
|
||||
MonadChain[GA, GB, E, A, B],
|
||||
Chain[GA, GB, E, A, B],
|
||||
FromIO[GB, GR, E, B],
|
||||
f,
|
||||
)
|
||||
@@ -207,11 +207,16 @@ func MapLeft[GA1 ~func() ET.Either[E1, A], GA2 ~func() ET.Either[E2, A], E1, E2,
|
||||
return F.Bind2nd(MonadMapLeft[GA1, GA2, E1, E2, A], f)
|
||||
}
|
||||
|
||||
// Delay creates an operation that passes in the value after some delay
|
||||
// Delay creates an operation that passes in the value after some [time.Duration]
|
||||
func Delay[GA ~func() ET.Either[E, A], E, A any](delay time.Duration) func(GA) GA {
|
||||
return IO.Delay[GA](delay)
|
||||
}
|
||||
|
||||
// After creates an operation that passes after the given [time.Time]
|
||||
func After[GA ~func() ET.Either[E, A], E, A any](timestamp time.Time) func(GA) GA {
|
||||
return IO.After[GA](timestamp)
|
||||
}
|
||||
|
||||
func MonadBiMap[GA ~func() ET.Either[E1, A], GB ~func() ET.Either[E2, B], E1, E2, A, B any](fa GA, f func(E1) E2, g func(A) B) GB {
|
||||
return eithert.MonadBiMap(IO.MonadMap[GA, GB, ET.Either[E1, A], ET.Either[E2, B]], fa, f, g)
|
||||
}
|
||||
|
43
ioeither/generic/logging.go
Normal file
43
ioeither/generic/logging.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 generic
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
|
||||
B "github.com/IBM/fp-go/bytes"
|
||||
ET "github.com/IBM/fp-go/either"
|
||||
F "github.com/IBM/fp-go/function"
|
||||
)
|
||||
|
||||
// LogJson converts the argument to JSON and then logs it via the format string
|
||||
// Can be used with [ChainFirst]
|
||||
func LogJson[GA ~func() ET.Either[error, any], A any](prefix string) func(A) GA {
|
||||
return func(a A) GA {
|
||||
// log this
|
||||
return F.Pipe3(
|
||||
ET.TryCatchError(json.MarshalIndent(a, "", " ")),
|
||||
ET.Map[error](B.ToString),
|
||||
FromEither[func() ET.Either[error, string]],
|
||||
Chain[func() ET.Either[error, string], GA](func(data string) GA {
|
||||
return FromImpure[GA](func() {
|
||||
log.Printf(prefix, data)
|
||||
})
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
@@ -18,250 +18,50 @@ package builder
|
||||
import (
|
||||
"bytes"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
|
||||
ENDO "github.com/IBM/fp-go/endomorphism"
|
||||
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"
|
||||
IOE "github.com/IBM/fp-go/ioeither"
|
||||
IOEH "github.com/IBM/fp-go/ioeither/http"
|
||||
J "github.com/IBM/fp-go/json"
|
||||
LZ "github.com/IBM/fp-go/lazy"
|
||||
L "github.com/IBM/fp-go/optics/lens"
|
||||
O "github.com/IBM/fp-go/option"
|
||||
S "github.com/IBM/fp-go/string"
|
||||
)
|
||||
|
||||
type (
|
||||
Builder struct {
|
||||
method O.Option[string]
|
||||
url string
|
||||
headers http.Header
|
||||
body O.Option[IOE.IOEither[error, []byte]]
|
||||
}
|
||||
func Requester(builder *R.Builder) IOEH.Requester {
|
||||
|
||||
// BuilderEndomorphism returns an [ENDO.Endomorphism] that transforms a builder
|
||||
BuilderEndomorphism = ENDO.Endomorphism[*Builder]
|
||||
)
|
||||
withBody := F.Curry3(func(data []byte, url string, method string) IOE.IOEither[error, *http.Request] {
|
||||
return IOE.TryCatchError(func() (*http.Request, error) {
|
||||
req, err := http.NewRequest(method, url, bytes.NewReader(data))
|
||||
if err == nil {
|
||||
req.Header.Set(H.ContentLength, strconv.Itoa(len(data)))
|
||||
H.Monoid.Concat(req.Header, builder.GetHeaders())
|
||||
}
|
||||
return req, err
|
||||
})
|
||||
})
|
||||
|
||||
var (
|
||||
// Default is the default builder
|
||||
Default = &Builder{method: O.Some(defaultMethod()), headers: make(http.Header), body: noBody}
|
||||
withoutBody := F.Curry2(func(url string, method string) IOE.IOEither[error, *http.Request] {
|
||||
return IOE.TryCatchError(func() (*http.Request, error) {
|
||||
req, err := http.NewRequest(method, url, nil)
|
||||
if err == nil {
|
||||
H.Monoid.Concat(req.Header, builder.GetHeaders())
|
||||
}
|
||||
return req, err
|
||||
})
|
||||
})
|
||||
|
||||
defaultMethod = F.Constant(http.MethodGet)
|
||||
|
||||
// BuilderMonoid is the [M.Monoid] for the [BuilderEndomorphism]
|
||||
BuilderMonoid = ENDO.Monoid[*Builder]()
|
||||
|
||||
// Url is a [L.Lens] for the URL
|
||||
Url = L.MakeLensRef((*Builder).GetUrl, (*Builder).SetUrl)
|
||||
// Method is a [L.Lens] for the HTTP method
|
||||
Method = L.MakeLensRef((*Builder).GetMethod, (*Builder).SetMethod)
|
||||
// Body is a [L.Lens] for the request body
|
||||
Body = L.MakeLensRef((*Builder).GetBody, (*Builder).SetBody)
|
||||
// Headers is a [L.Lens] for the complete set of request headers
|
||||
Headers = L.MakeLensRef((*Builder).GetHeaders, (*Builder).SetHeaders)
|
||||
|
||||
getHeader = F.Bind2of2((*Builder).GetHeader)
|
||||
delHeader = F.Bind2of2((*Builder).DelHeader)
|
||||
setHeader = F.Bind2of3((*Builder).SetHeader)
|
||||
|
||||
noHeader = O.None[string]()
|
||||
noBody = O.None[IOE.IOEither[error, []byte]]()
|
||||
|
||||
// WithMethod creates a [BuilderBuilder] for a certain method
|
||||
WithMethod = F.Flow2(
|
||||
Method.Set,
|
||||
ENDO.Of[func(*Builder) *Builder],
|
||||
)
|
||||
// WithUrl creates a [BuilderBuilder] for a certain method
|
||||
WithUrl = F.Flow2(
|
||||
Url.Set,
|
||||
ENDO.Of[func(*Builder) *Builder],
|
||||
)
|
||||
// WithHeaders creates a [BuilderBuilder] for a set of headers
|
||||
WithHeaders = F.Flow2(
|
||||
Headers.Set,
|
||||
ENDO.Of[func(*Builder) *Builder],
|
||||
)
|
||||
// WithBody creates a [BuilderBuilder] for a request body
|
||||
WithBody = F.Flow3(
|
||||
O.Of[IOE.IOEither[error, []byte]],
|
||||
Body.Set,
|
||||
ENDO.Of[func(*Builder) *Builder],
|
||||
)
|
||||
// WithContentType adds the content type header
|
||||
WithContentType = WithHeader("Content-Type")
|
||||
|
||||
// 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)
|
||||
|
||||
// Requester creates a requester from a builder
|
||||
Requester = (*Builder).Requester
|
||||
|
||||
// WithoutBody creates a [BuilderBuilder] to remove the body
|
||||
WithoutBody = F.Pipe2(
|
||||
noBody,
|
||||
Body.Set,
|
||||
ENDO.Of[func(*Builder) *Builder],
|
||||
)
|
||||
)
|
||||
|
||||
func (builder *Builder) clone() *Builder {
|
||||
cpy := *builder
|
||||
cpy.headers = cpy.headers.Clone()
|
||||
return &cpy
|
||||
}
|
||||
|
||||
func (builder *Builder) GetUrl() string {
|
||||
return builder.url
|
||||
}
|
||||
|
||||
func (builder *Builder) GetMethod() string {
|
||||
return F.Pipe1(
|
||||
builder.method,
|
||||
O.GetOrElse(defaultMethod),
|
||||
)
|
||||
}
|
||||
|
||||
func (builder *Builder) GetHeaders() http.Header {
|
||||
return builder.headers
|
||||
}
|
||||
|
||||
func (builder *Builder) GetBody() O.Option[IOE.IOEither[error, []byte]] {
|
||||
return builder.body
|
||||
}
|
||||
|
||||
func (builder *Builder) SetMethod(method string) *Builder {
|
||||
builder.method = O.Some(method)
|
||||
return builder
|
||||
}
|
||||
|
||||
func (builder *Builder) SetUrl(url string) *Builder {
|
||||
builder.url = url
|
||||
return builder
|
||||
}
|
||||
|
||||
func (builder *Builder) SetHeaders(headers http.Header) *Builder {
|
||||
builder.headers = headers
|
||||
return builder
|
||||
}
|
||||
|
||||
func (builder *Builder) SetBody(body O.Option[IOE.IOEither[error, []byte]]) *Builder {
|
||||
builder.body = body
|
||||
return builder
|
||||
}
|
||||
|
||||
func (builder *Builder) SetHeader(name, value string) *Builder {
|
||||
builder.headers.Set(name, value)
|
||||
return builder
|
||||
}
|
||||
|
||||
func (builder *Builder) DelHeader(name string) *Builder {
|
||||
builder.headers.Del(name)
|
||||
return builder
|
||||
}
|
||||
|
||||
func (builder *Builder) GetHeader(name string) O.Option[string] {
|
||||
return F.Pipe2(
|
||||
name,
|
||||
builder.headers.Get,
|
||||
O.FromPredicate(S.IsNonEmpty),
|
||||
)
|
||||
}
|
||||
|
||||
func (builder *Builder) GetHeaderValues(name string) []string {
|
||||
return builder.headers.Values(name)
|
||||
}
|
||||
|
||||
func (builder *Builder) AddHeaderHeader(name, value string) *Builder {
|
||||
builder.headers.Add(name, value)
|
||||
return builder
|
||||
}
|
||||
|
||||
func (builder *Builder) Requester() IOEH.Requester {
|
||||
return F.Pipe3(
|
||||
return F.Pipe5(
|
||||
builder.GetBody(),
|
||||
O.Map(IOE.Map[error](bytes.NewReader)),
|
||||
O.GetOrElse(F.Constant(IOE.Of[error, *bytes.Reader](nil))),
|
||||
IOE.Chain(func(rdr *bytes.Reader) IOE.IOEither[error, *http.Request] {
|
||||
return IOE.TryCatchError(func() (*http.Request, error) {
|
||||
req, err := http.NewRequest(builder.GetMethod(), builder.GetUrl(), rdr)
|
||||
if err == nil {
|
||||
for name, value := range builder.GetHeaders() {
|
||||
req.Header[name] = value
|
||||
}
|
||||
if rdr != nil {
|
||||
req.Header.Set("Content-Length", strconv.FormatInt(rdr.Size(), 10))
|
||||
}
|
||||
}
|
||||
return req, err
|
||||
})
|
||||
O.Fold(LZ.Of(E.Of[error](withoutBody)), E.Map[error](withBody)),
|
||||
E.Ap[func(string) IOE.IOEither[error, *http.Request]](builder.GetTargetUrl()),
|
||||
E.Flap[error, IOE.IOEither[error, *http.Request]](builder.GetMethod()),
|
||||
E.GetOrElse(IOE.Left[*http.Request, error]),
|
||||
IOE.Map[error](func(req *http.Request) *http.Request {
|
||||
req.Header = H.Monoid.Concat(req.Header, builder.GetHeaders())
|
||||
return req
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
// Header returns a [L.Lens] for a single header
|
||||
func Header(name string) L.Lens[*Builder, O.Option[string]] {
|
||||
get := getHeader(name)
|
||||
set := F.Bind1of2(setHeader(name))
|
||||
del := F.Flow2(
|
||||
LZ.Of[*Builder],
|
||||
LZ.Map(delHeader(name)),
|
||||
)
|
||||
|
||||
return L.MakeLens[*Builder, O.Option[string]](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 [BuilderBuilder] for a certain header
|
||||
func WithHeader(name string) func(value string) BuilderEndomorphism {
|
||||
return F.Flow3(
|
||||
O.Of[string],
|
||||
Header(name).Set,
|
||||
ENDO.Of[func(*Builder) *Builder],
|
||||
)
|
||||
}
|
||||
|
||||
// WithoutHeader creates a [BuilderBuilder] to remove a certain header
|
||||
func WithoutHeader(name string) BuilderEndomorphism {
|
||||
return Header(name).Set(noHeader)
|
||||
}
|
||||
|
||||
// WithFormData creates a [BuilderBuilder] to send form data payload
|
||||
func WithFormData(value url.Values) BuilderEndomorphism {
|
||||
return F.Flow2(
|
||||
F.Pipe4(
|
||||
value,
|
||||
url.Values.Encode,
|
||||
S.ToBytes,
|
||||
IOE.Of[error, []byte],
|
||||
WithBody,
|
||||
),
|
||||
WithContentType("application/x-www-form-urlencoded"),
|
||||
)
|
||||
}
|
||||
|
||||
// WithJson creates a [BuilderBuilder] to send JSON payload
|
||||
func WithJson[T any](data T) BuilderEndomorphism {
|
||||
return F.Flow2(
|
||||
F.Pipe3(
|
||||
data,
|
||||
J.Marshal[T],
|
||||
IOE.FromEither[error, []byte],
|
||||
WithBody,
|
||||
),
|
||||
WithContentType("application/json"),
|
||||
)
|
||||
}
|
||||
|
@@ -16,36 +16,43 @@
|
||||
package builder
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
E "github.com/IBM/fp-go/either"
|
||||
F "github.com/IBM/fp-go/function"
|
||||
O "github.com/IBM/fp-go/option"
|
||||
R "github.com/IBM/fp-go/http/builder"
|
||||
IO "github.com/IBM/fp-go/io"
|
||||
IOE "github.com/IBM/fp-go/ioeither"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestBuiler(t *testing.T) {
|
||||
func TestBuilderWithQuery(t *testing.T) {
|
||||
// add some query
|
||||
withLimit := R.WithQueryArg("limit")("10")
|
||||
withUrl := R.WithUrl("http://www.example.org?a=b")
|
||||
|
||||
name := "Content-type"
|
||||
withContentType := WithHeader(name)
|
||||
withoutContentType := WithoutHeader(name)
|
||||
|
||||
b1 := F.Pipe1(
|
||||
Default,
|
||||
withContentType("application/json"),
|
||||
b := F.Pipe2(
|
||||
R.Default,
|
||||
withLimit,
|
||||
withUrl,
|
||||
)
|
||||
|
||||
b2 := F.Pipe1(
|
||||
b1,
|
||||
withContentType("text/plain"),
|
||||
req := F.Pipe3(
|
||||
b,
|
||||
Requester,
|
||||
IOE.Map[error](func(r *http.Request) *url.URL {
|
||||
return r.URL
|
||||
}),
|
||||
IOE.ChainFirstIOK[error](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"))
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
b3 := F.Pipe1(
|
||||
b2,
|
||||
withoutContentType,
|
||||
)
|
||||
|
||||
assert.Equal(t, O.None[string](), Default.GetHeader(name))
|
||||
assert.Equal(t, O.Of("application/json"), b1.GetHeader(name))
|
||||
assert.Equal(t, O.Of("text/plain"), b2.GetHeader(name))
|
||||
assert.Equal(t, O.None[string](), b3.GetHeader(name))
|
||||
assert.True(t, E.IsRight(req()))
|
||||
}
|
||||
|
@@ -13,20 +13,20 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package http
|
||||
package di
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
DI "github.com/IBM/fp-go/di"
|
||||
IOE "github.com/IBM/fp-go/ioeither"
|
||||
L "github.com/IBM/fp-go/lazy"
|
||||
IOEH "github.com/IBM/fp-go/ioeither/http"
|
||||
)
|
||||
|
||||
var (
|
||||
// InjHttpClient is the injection token for the default http client
|
||||
InjHttpClient = DI.MakeTokenWithDefault0("HTTP_CLIENT", L.Of(IOE.Of[error](http.DefaultClient)))
|
||||
// InjHttpClient is the [DI.InjectionToken] for the [http.DefaultClient]
|
||||
InjHttpClient = DI.MakeTokenWithDefault0("HTTP_CLIENT", IOE.Of[error](http.DefaultClient))
|
||||
|
||||
// InjClient is the injection token for the default [Client]
|
||||
InjClient = DI.MakeTokenWithDefault1("CLIENT", InjHttpClient.IOEither(), IOE.Map[error](MakeClient))
|
||||
// InjClient is the [DI.InjectionToken] for the default [IOEH.Client]
|
||||
InjClient = DI.MakeTokenWithDefault1("CLIENT", InjHttpClient.IOEither(), IOE.Map[error](IOEH.MakeClient))
|
||||
)
|
@@ -16,6 +16,8 @@
|
||||
package ioeither
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
ET "github.com/IBM/fp-go/either"
|
||||
I "github.com/IBM/fp-go/io"
|
||||
G "github.com/IBM/fp-go/ioeither/generic"
|
||||
@@ -173,7 +175,7 @@ func MonadMapLeft[E1, E2, A any](fa IOEither[E1, A], f func(E1) E2) IOEither[E2,
|
||||
return G.MonadMapLeft[IOEither[E1, A], IOEither[E2, A]](fa, f)
|
||||
}
|
||||
|
||||
func MapLeft[E1, E2, A any](f func(E1) E2) func(IOEither[E1, A]) IOEither[E2, A] {
|
||||
func MapLeft[A, E1, E2 any](f func(E1) E2) func(IOEither[E1, A]) IOEither[E2, A] {
|
||||
return G.MapLeft[IOEither[E1, A], IOEither[E2, A]](f)
|
||||
}
|
||||
|
||||
@@ -276,3 +278,13 @@ func Flap[E, B, A any](a A) func(IOEither[E, func(A) B]) IOEither[E, B] {
|
||||
func ToIOOption[E, A any](ioe IOEither[E, A]) IOO.IOOption[A] {
|
||||
return G.ToIOOption[IOO.IOOption[A]](ioe)
|
||||
}
|
||||
|
||||
// Delay creates an operation that passes in the value after some delay
|
||||
func Delay[E, A any](delay time.Duration) func(IOEither[E, A]) IOEither[E, A] {
|
||||
return G.Delay[IOEither[E, A]](delay)
|
||||
}
|
||||
|
||||
// After creates an operation that passes after the given [time.Time]
|
||||
func After[E, A any](timestamp time.Time) func(IOEither[E, A]) IOEither[E, A] {
|
||||
return G.After[IOEither[E, A]](timestamp)
|
||||
}
|
||||
|
26
ioeither/logging.go
Normal file
26
ioeither/logging.go
Normal file
@@ -0,0 +1,26 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ioeither
|
||||
|
||||
import (
|
||||
G "github.com/IBM/fp-go/ioeither/generic"
|
||||
)
|
||||
|
||||
// LogJson converts the argument to pretty printed JSON and then logs it via the format string
|
||||
// Can be used with [ChainFirst]
|
||||
func LogJson[A any](prefix string) func(A) IOEither[error, any] {
|
||||
return G.LogJson[IOEither[error, any], A](prefix)
|
||||
}
|
42
ioeither/logging_test.go
Normal file
42
ioeither/logging_test.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 ioeither
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
E "github.com/IBM/fp-go/either"
|
||||
F "github.com/IBM/fp-go/function"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestLogging(t *testing.T) {
|
||||
|
||||
type SomeData struct {
|
||||
Key string `json:"key"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
src := &SomeData{Key: "key", Value: "value"}
|
||||
|
||||
res := F.Pipe1(
|
||||
Of[error](src),
|
||||
ChainFirst(LogJson[*SomeData]("Data: \n%s")),
|
||||
)
|
||||
|
||||
dst := res()
|
||||
assert.Equal(t, E.Of[error](src), dst)
|
||||
}
|
66
iooption/bind.go
Normal file
66
iooption/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 iooption
|
||||
|
||||
import (
|
||||
G "github.com/IBM/fp-go/iooption/generic"
|
||||
)
|
||||
|
||||
// Bind creates an empty context of type [S] to be used with the [Bind] operation
|
||||
func Do[S any](
|
||||
empty S,
|
||||
) IOOption[S] {
|
||||
return G.Do[IOOption[S], S](empty)
|
||||
}
|
||||
|
||||
// Bind attaches the result of a computation to a context [S1] to produce a context [S2]
|
||||
func Bind[S1, S2, T any](
|
||||
setter func(T) func(S1) S2,
|
||||
f func(S1) IOOption[T],
|
||||
) func(IOOption[S1]) IOOption[S2] {
|
||||
return G.Bind[IOOption[S1], IOOption[S2], IOOption[T], S1, S2, T](setter, f)
|
||||
}
|
||||
|
||||
// Let attaches the result of a computation to a context [S1] to produce a context [S2]
|
||||
func Let[S1, S2, T any](
|
||||
setter func(T) func(S1) S2,
|
||||
f func(S1) T,
|
||||
) func(IOOption[S1]) IOOption[S2] {
|
||||
return G.Let[IOOption[S1], IOOption[S2], S1, S2, T](setter, f)
|
||||
}
|
||||
|
||||
// LetTo attaches the a value to a context [S1] to produce a context [S2]
|
||||
func LetTo[S1, S2, T any](
|
||||
setter func(T) func(S1) S2,
|
||||
b T,
|
||||
) func(IOOption[S1]) IOOption[S2] {
|
||||
return G.LetTo[IOOption[S1], IOOption[S2], S1, S2, T](setter, b)
|
||||
}
|
||||
|
||||
// BindTo initializes a new state [S1] from a value [T]
|
||||
func BindTo[S1, T any](
|
||||
setter func(T) S1,
|
||||
) func(IOOption[T]) IOOption[S1] {
|
||||
return G.BindTo[IOOption[S1], IOOption[T], S1, T](setter)
|
||||
}
|
||||
|
||||
// ApS attaches a value to a context [S1] to produce a context [S2] by considering the context and the value concurrently
|
||||
func ApS[S1, S2, T any](
|
||||
setter func(T) func(S1) S2,
|
||||
fa IOOption[T],
|
||||
) func(IOOption[S1]) IOOption[S2] {
|
||||
return G.ApS[IOOption[S1], IOOption[S2], IOOption[T], S1, S2, T](setter, fa)
|
||||
}
|
57
iooption/bind_test.go
Normal file
57
iooption/bind_test.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 iooption
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
F "github.com/IBM/fp-go/function"
|
||||
"github.com/IBM/fp-go/internal/utils"
|
||||
O "github.com/IBM/fp-go/option"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func getLastName(s utils.Initial) IOOption[string] {
|
||||
return Of("Doe")
|
||||
}
|
||||
|
||||
func getGivenName(s utils.WithLastName) IOOption[string] {
|
||||
return Of("John")
|
||||
}
|
||||
|
||||
func TestBind(t *testing.T) {
|
||||
|
||||
res := F.Pipe3(
|
||||
Do(utils.Empty),
|
||||
Bind(utils.SetLastName, getLastName),
|
||||
Bind(utils.SetGivenName, getGivenName),
|
||||
Map(utils.GetFullName),
|
||||
)
|
||||
|
||||
assert.Equal(t, res(), O.Of("John Doe"))
|
||||
}
|
||||
|
||||
func TestApS(t *testing.T) {
|
||||
|
||||
res := F.Pipe3(
|
||||
Do(utils.Empty),
|
||||
ApS(utils.SetLastName, Of("Doe")),
|
||||
ApS(utils.SetGivenName, Of("John")),
|
||||
Map(utils.GetFullName),
|
||||
)
|
||||
|
||||
assert.Equal(t, res(), O.Of("John Doe"))
|
||||
}
|
31
iooption/eq.go
Normal file
31
iooption/eq.go
Normal file
@@ -0,0 +1,31 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package iooption
|
||||
|
||||
import (
|
||||
EQ "github.com/IBM/fp-go/eq"
|
||||
G "github.com/IBM/fp-go/iooption/generic"
|
||||
)
|
||||
|
||||
// Eq implements the equals predicate for values contained in the IO monad
|
||||
func Eq[A any](e EQ.Eq[A]) EQ.Eq[IOOption[A]] {
|
||||
return G.Eq[IOOption[A]](e)
|
||||
}
|
||||
|
||||
// FromStrictEquals constructs an [EQ.Eq] from the canonical comparison function
|
||||
func FromStrictEquals[A comparable]() EQ.Eq[IOOption[A]] {
|
||||
return G.FromStrictEquals[IOOption[A]]()
|
||||
}
|
90
iooption/generic/bind.go
Normal file
90
iooption/generic/bind.go
Normal file
@@ -0,0 +1,90 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package generic
|
||||
|
||||
import (
|
||||
A "github.com/IBM/fp-go/internal/apply"
|
||||
C "github.com/IBM/fp-go/internal/chain"
|
||||
F "github.com/IBM/fp-go/internal/functor"
|
||||
O "github.com/IBM/fp-go/option"
|
||||
)
|
||||
|
||||
// Bind creates an empty context of type [S] to be used with the [Bind] operation
|
||||
func Do[GS ~func() O.Option[S], S any](
|
||||
empty S,
|
||||
) GS {
|
||||
return Of[GS](empty)
|
||||
}
|
||||
|
||||
// Bind attaches the result of a computation to a context [S1] to produce a context [S2]
|
||||
func Bind[GS1 ~func() O.Option[S1], GS2 ~func() O.Option[S2], GT ~func() O.Option[T], S1, S2, T any](
|
||||
setter func(T) func(S1) S2,
|
||||
f func(S1) GT,
|
||||
) func(GS1) GS2 {
|
||||
return C.Bind(
|
||||
Chain[GS1, GS2, S1, S2],
|
||||
Map[GT, GS2, T, S2],
|
||||
setter,
|
||||
f,
|
||||
)
|
||||
}
|
||||
|
||||
// Let attaches the result of a computation to a context [S1] to produce a context [S2]
|
||||
func Let[GS1 ~func() O.Option[S1], GS2 ~func() O.Option[S2], S1, S2, T any](
|
||||
key func(T) func(S1) S2,
|
||||
f func(S1) T,
|
||||
) func(GS1) GS2 {
|
||||
return F.Let(
|
||||
Map[GS1, GS2, S1, S2],
|
||||
key,
|
||||
f,
|
||||
)
|
||||
}
|
||||
|
||||
// LetTo attaches the a value to a context [S1] to produce a context [S2]
|
||||
func LetTo[GS1 ~func() O.Option[S1], GS2 ~func() O.Option[S2], S1, S2, B any](
|
||||
key func(B) func(S1) S2,
|
||||
b B,
|
||||
) func(GS1) GS2 {
|
||||
return F.LetTo(
|
||||
Map[GS1, GS2, S1, S2],
|
||||
key,
|
||||
b,
|
||||
)
|
||||
}
|
||||
|
||||
// BindTo initializes a new state [S1] from a value [T]
|
||||
func BindTo[GS1 ~func() O.Option[S1], GT ~func() O.Option[T], S1, T any](
|
||||
setter func(T) S1,
|
||||
) func(GT) GS1 {
|
||||
return C.BindTo(
|
||||
Map[GT, GS1, T, S1],
|
||||
setter,
|
||||
)
|
||||
}
|
||||
|
||||
// ApS attaches a value to a context [S1] to produce a context [S2] by considering the context and the value concurrently
|
||||
func ApS[GS1 ~func() O.Option[S1], GS2 ~func() O.Option[S2], GT ~func() O.Option[T], S1, S2, T any](
|
||||
setter func(T) func(S1) S2,
|
||||
fa GT,
|
||||
) func(GS1) GS2 {
|
||||
return A.ApS(
|
||||
Ap[GS2, func() O.Option[func(T) S2], GT, T, S2],
|
||||
Map[GS1, func() O.Option[func(T) S2], S1, func(T) S2],
|
||||
setter,
|
||||
fa,
|
||||
)
|
||||
}
|
32
iooption/generic/eq.go
Normal file
32
iooption/generic/eq.go
Normal file
@@ -0,0 +1,32 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package generic
|
||||
|
||||
import (
|
||||
EQ "github.com/IBM/fp-go/eq"
|
||||
G "github.com/IBM/fp-go/io/generic"
|
||||
O "github.com/IBM/fp-go/option"
|
||||
)
|
||||
|
||||
// Eq implements the equals predicate for values contained in the IO monad
|
||||
func Eq[GA ~func() O.Option[A], A any](e EQ.Eq[A]) EQ.Eq[GA] {
|
||||
return G.Eq[GA](O.Eq(e))
|
||||
}
|
||||
|
||||
// FromStrictEquals constructs an [EQ.Eq] from the canonical comparison function
|
||||
func FromStrictEquals[GA ~func() O.Option[A], A comparable]() EQ.Eq[GA] {
|
||||
return Eq[GA](EQ.FromStrictEquals[A]())
|
||||
}
|
@@ -145,7 +145,7 @@ func MonadChainIOK[GA ~func() O.Option[A], GB ~func() O.Option[B], GR ~func() B,
|
||||
|
||||
func ChainIOK[GA ~func() O.Option[A], GB ~func() O.Option[B], GR ~func() B, A, B any](f func(A) GR) func(GA) GB {
|
||||
return FI.ChainIOK(
|
||||
MonadChain[GA, GB, A, B],
|
||||
Chain[GA, GB, A, B],
|
||||
FromIO[GB, GR, B],
|
||||
f,
|
||||
)
|
||||
@@ -219,6 +219,11 @@ func Delay[GA ~func() O.Option[A], A any](delay time.Duration) func(GA) GA {
|
||||
return IO.Delay[GA](delay)
|
||||
}
|
||||
|
||||
// After creates an operation that passes after the given [time.Time]
|
||||
func After[GA ~func() O.Option[A], A any](timestamp time.Time) func(GA) GA {
|
||||
return IO.After[GA](timestamp)
|
||||
}
|
||||
|
||||
// Fold convers an IOOption into an IO
|
||||
func Fold[GA ~func() O.Option[A], GB ~func() B, A, B any](onNone func() GB, onSome func(A) GB) func(GA) GB {
|
||||
return optiont.MatchE(IO.MonadChain[GA, GB, O.Option[A], B], onNone, onSome)
|
||||
|
@@ -16,6 +16,8 @@
|
||||
package iooption
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
ET "github.com/IBM/fp-go/either"
|
||||
I "github.com/IBM/fp-go/io"
|
||||
IO "github.com/IBM/fp-go/io"
|
||||
@@ -164,3 +166,13 @@ func MonadChainFirstIOK[A, B any](first IOOption[A], f func(A) IO.IO[B]) IOOptio
|
||||
func ChainFirstIOK[A, B any](f func(A) IO.IO[B]) func(IOOption[A]) IOOption[A] {
|
||||
return G.ChainFirstIOK[IOOption[A], IO.IO[B]](f)
|
||||
}
|
||||
|
||||
// Delay creates an operation that passes in the value after some delay
|
||||
func Delay[A any](delay time.Duration) func(IOOption[A]) IOOption[A] {
|
||||
return G.Delay[IOOption[A]](delay)
|
||||
}
|
||||
|
||||
// After creates an operation that passes after the given [time.Time]
|
||||
func After[A any](timestamp time.Time) func(IOOption[A]) IOOption[A] {
|
||||
return G.After[IOOption[A]](timestamp)
|
||||
}
|
||||
|
74
iooption/testing/laws.go
Normal file
74
iooption/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"
|
||||
|
||||
EQ "github.com/IBM/fp-go/eq"
|
||||
L "github.com/IBM/fp-go/internal/monad/testing"
|
||||
IOO "github.com/IBM/fp-go/iooption"
|
||||
)
|
||||
|
||||
// AssertLaws asserts the apply monad laws for the `Either` monad
|
||||
func AssertLaws[A, B, C any](t *testing.T,
|
||||
eqa EQ.Eq[A],
|
||||
eqb EQ.Eq[B],
|
||||
eqc EQ.Eq[C],
|
||||
|
||||
ab func(A) B,
|
||||
bc func(B) C,
|
||||
) func(a A) bool {
|
||||
|
||||
return L.AssertLaws(t,
|
||||
IOO.Eq(eqa),
|
||||
IOO.Eq(eqb),
|
||||
IOO.Eq(eqc),
|
||||
|
||||
IOO.Of[A],
|
||||
IOO.Of[B],
|
||||
IOO.Of[C],
|
||||
|
||||
IOO.Of[func(A) A],
|
||||
IOO.Of[func(A) B],
|
||||
IOO.Of[func(B) C],
|
||||
IOO.Of[func(func(A) B) B],
|
||||
|
||||
IOO.MonadMap[A, A],
|
||||
IOO.MonadMap[A, B],
|
||||
IOO.MonadMap[A, C],
|
||||
IOO.MonadMap[B, C],
|
||||
|
||||
IOO.MonadMap[func(B) C, func(func(A) B) func(A) C],
|
||||
|
||||
IOO.MonadChain[A, A],
|
||||
IOO.MonadChain[A, B],
|
||||
IOO.MonadChain[A, C],
|
||||
IOO.MonadChain[B, C],
|
||||
|
||||
IOO.MonadAp[A, A],
|
||||
IOO.MonadAp[B, A],
|
||||
IOO.MonadAp[C, B],
|
||||
IOO.MonadAp[C, A],
|
||||
|
||||
IOO.MonadAp[B, func(A) B],
|
||||
IOO.MonadAp[func(A) C, func(A) B],
|
||||
|
||||
ab,
|
||||
bc,
|
||||
)
|
||||
|
||||
}
|
47
iooption/testing/laws_test.go
Normal file
47
iooption/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))
|
||||
}
|
66
iterator/stateless/bind.go
Normal file
66
iterator/stateless/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 stateless
|
||||
|
||||
import (
|
||||
G "github.com/IBM/fp-go/iterator/stateless/generic"
|
||||
)
|
||||
|
||||
// Bind creates an empty context of type [S] to be used with the [Bind] operation
|
||||
func Do[S any](
|
||||
empty S,
|
||||
) Iterator[S] {
|
||||
return G.Do[Iterator[S], S](empty)
|
||||
}
|
||||
|
||||
// Bind attaches the result of a computation to a context [S1] to produce a context [S2]
|
||||
func Bind[S1, S2, T any](
|
||||
setter func(T) func(S1) S2,
|
||||
f func(S1) Iterator[T],
|
||||
) func(Iterator[S1]) Iterator[S2] {
|
||||
return G.Bind[Iterator[S1], Iterator[S2], Iterator[T], S1, S2, T](setter, f)
|
||||
}
|
||||
|
||||
// Let attaches the result of a computation to a context [S1] to produce a context [S2]
|
||||
func Let[S1, S2, T any](
|
||||
setter func(T) func(S1) S2,
|
||||
f func(S1) T,
|
||||
) func(Iterator[S1]) Iterator[S2] {
|
||||
return G.Let[Iterator[S1], Iterator[S2], S1, S2, T](setter, f)
|
||||
}
|
||||
|
||||
// LetTo attaches the a value to a context [S1] to produce a context [S2]
|
||||
func LetTo[S1, S2, T any](
|
||||
setter func(T) func(S1) S2,
|
||||
b T,
|
||||
) func(Iterator[S1]) Iterator[S2] {
|
||||
return G.LetTo[Iterator[S1], Iterator[S2], S1, S2, T](setter, b)
|
||||
}
|
||||
|
||||
// BindTo initializes a new state [S1] from a value [T]
|
||||
func BindTo[S1, T any](
|
||||
setter func(T) S1,
|
||||
) func(Iterator[T]) Iterator[S1] {
|
||||
return G.BindTo[Iterator[S1], Iterator[T], S1, T](setter)
|
||||
}
|
||||
|
||||
// ApS attaches a value to a context [S1] to produce a context [S2] by considering the context and the value concurrently
|
||||
func ApS[S1, S2, T any](
|
||||
setter func(T) func(S1) S2,
|
||||
fa Iterator[T],
|
||||
) func(Iterator[S1]) Iterator[S2] {
|
||||
return G.ApS[Iterator[func(T) S2], Iterator[S1], Iterator[S2], Iterator[T], S1, S2, T](setter, fa)
|
||||
}
|
57
iterator/stateless/bind_test.go
Normal file
57
iterator/stateless/bind_test.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 stateless
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
A "github.com/IBM/fp-go/array"
|
||||
F "github.com/IBM/fp-go/function"
|
||||
"github.com/IBM/fp-go/internal/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func getLastName(s utils.Initial) Iterator[string] {
|
||||
return Of("Doe")
|
||||
}
|
||||
|
||||
func getGivenName(s utils.WithLastName) Iterator[string] {
|
||||
return Of("John")
|
||||
}
|
||||
|
||||
func TestBind(t *testing.T) {
|
||||
|
||||
res := F.Pipe3(
|
||||
Do(utils.Empty),
|
||||
Bind(utils.SetLastName, getLastName),
|
||||
Bind(utils.SetGivenName, getGivenName),
|
||||
Map(utils.GetFullName),
|
||||
)
|
||||
|
||||
assert.Equal(t, ToArray(res), A.Of("John Doe"))
|
||||
}
|
||||
|
||||
func TestApS(t *testing.T) {
|
||||
|
||||
res := F.Pipe3(
|
||||
Do(utils.Empty),
|
||||
ApS(utils.SetLastName, Of("Doe")),
|
||||
ApS(utils.SetGivenName, Of("John")),
|
||||
Map(utils.GetFullName),
|
||||
)
|
||||
|
||||
assert.Equal(t, ToArray(res), A.Of("John Doe"))
|
||||
}
|
92
iterator/stateless/generic/bind.go
Normal file
92
iterator/stateless/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 (
|
||||
"github.com/IBM/fp-go/internal/apply"
|
||||
C "github.com/IBM/fp-go/internal/chain"
|
||||
F "github.com/IBM/fp-go/internal/functor"
|
||||
O "github.com/IBM/fp-go/option"
|
||||
T "github.com/IBM/fp-go/tuple"
|
||||
)
|
||||
|
||||
// Bind creates an empty context of type [S] to be used with the [Bind] operation
|
||||
func Do[GS ~func() O.Option[T.Tuple2[GS, S]], S any](
|
||||
empty S,
|
||||
) GS {
|
||||
return Of[GS](empty)
|
||||
}
|
||||
|
||||
// Bind attaches the result of a computation to a context [S1] to produce a context [S2]
|
||||
func Bind[GS1 ~func() O.Option[T.Tuple2[GS1, S1]], GS2 ~func() O.Option[T.Tuple2[GS2, S2]], GA ~func() O.Option[T.Tuple2[GA, A]], S1, S2, A any](
|
||||
setter func(A) func(S1) S2,
|
||||
f func(S1) GA,
|
||||
) func(GS1) GS2 {
|
||||
|
||||
return C.Bind(
|
||||
Chain[GS2, GS1, S1, S2],
|
||||
Map[GS2, GA, func(A) S2, A, S2],
|
||||
setter,
|
||||
f,
|
||||
)
|
||||
}
|
||||
|
||||
// Let attaches the result of a computation to a context [S1] to produce a context [S2]
|
||||
func Let[GS1 ~func() O.Option[T.Tuple2[GS1, S1]], GS2 ~func() O.Option[T.Tuple2[GS2, S2]], S1, S2, A any](
|
||||
key func(A) func(S1) S2,
|
||||
f func(S1) A,
|
||||
) func(GS1) GS2 {
|
||||
return F.Let(
|
||||
Map[GS2, GS1, func(S1) S2, S1, S2],
|
||||
key,
|
||||
f,
|
||||
)
|
||||
}
|
||||
|
||||
// LetTo attaches the a value to a context [S1] to produce a context [S2]
|
||||
func LetTo[GS1 ~func() O.Option[T.Tuple2[GS1, S1]], GS2 ~func() O.Option[T.Tuple2[GS2, S2]], S1, S2, B any](
|
||||
key func(B) func(S1) S2,
|
||||
b B,
|
||||
) func(GS1) GS2 {
|
||||
return F.LetTo(
|
||||
Map[GS2, GS1, func(S1) S2, S1, S2],
|
||||
key,
|
||||
b,
|
||||
)
|
||||
}
|
||||
|
||||
// BindTo initializes a new state [S1] from a value [T]
|
||||
func BindTo[GS1 ~func() O.Option[T.Tuple2[GS1, S1]], GA ~func() O.Option[T.Tuple2[GA, A]], S1, A any](
|
||||
setter func(A) S1,
|
||||
) func(GA) GS1 {
|
||||
return C.BindTo(
|
||||
Map[GS1, GA, func(A) S1, A, S1],
|
||||
setter,
|
||||
)
|
||||
}
|
||||
|
||||
// ApS attaches a value to a context [S1] to produce a context [S2] by considering the context and the value concurrently
|
||||
func ApS[GAS2 ~func() O.Option[T.Tuple2[GAS2, func(A) S2]], GS1 ~func() O.Option[T.Tuple2[GS1, S1]], GS2 ~func() O.Option[T.Tuple2[GS2, S2]], GA ~func() O.Option[T.Tuple2[GA, A]], S1, S2, A any](
|
||||
setter func(A) func(S1) S2,
|
||||
fa GA,
|
||||
) func(GS1) GS2 {
|
||||
return apply.ApS(
|
||||
Ap[GAS2, GS2, GA, A, S2],
|
||||
Map[GAS2, GS1, func(S1) func(A) S2, S1, func(A) S2],
|
||||
setter,
|
||||
fa,
|
||||
)
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user