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

Compare commits

...

35 Commits

Author SHA1 Message Date
Dr. Carsten Leue
813b83b423 fix: add WithQueryArg to request builder
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-01-08 21:35:39 +01:00
Dr. Carsten Leue
9139dedbbe fix: add lazy support for iterators
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-01-08 14:22:08 +01:00
Elliot Chen
5e15119653 run go mod tidy (#97)
Co-authored-by: zichang.chen <zichange.chen@grabtaxi.com>
2024-01-08 08:08:59 +01:00
Elliot Chen
986aa21055 add idea filter in gitignore (#94)
Co-authored-by: zichang.chen <zichange.chen@grabtaxi.com>
2024-01-02 21:56:40 +01:00
renovate[bot]
3a4c46ec1e fix(deps): update module github.com/urfave/cli/v2 to v2.27.1 2023-12-30 18:29:29 +00:00
renovate[bot]
96c3ee20ff fix(deps): update module github.com/urfave/cli/v2 to v2.27.0 2023-12-27 02:11:30 +00:00
Dr. Carsten Leue
7af9acfd99 fix: Chain for Endomorphism
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-12-22 16:43:19 +01:00
Dr. Carsten Leue
36eefbcd27 fix: name the endomorphism for the Y combinator
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-12-22 14:41:55 +01:00
Dr. Carsten Leue
973138c822 fix: add prepend method
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-12-21 16:46:47 +01:00
Dr. Carsten Leue
12ef79184b fix: typing for Y combinator
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-12-21 16:25:35 +01:00
Dr. Carsten Leue
5ac47440a1 fix: add a single element cache
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-12-21 14:10:46 +01:00
Dr. Carsten Leue
3aa55c74d4 fix: add WithBearer
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-12-19 14:33:52 +01:00
Dr. Carsten Leue
a6f55a199c fix: experiment with doc links
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-12-19 12:18:07 +01:00
Dr. Carsten Leue
2b500d15da Merge branch 'main' of github.com:IBM/fp-go 2023-12-18 21:40:39 +01:00
Dr. Carsten Leue
599b8256b6 fix: generate DI variations
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-12-18 21:40:30 +01:00
renovate[bot]
cf70b47984 chore(deps): update actions/setup-node action to v4.0.1 2023-12-18 15:59:48 +00:00
Dr. Carsten Leue
7bceb856f8 fix: move DI to separate package
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-12-18 10:06:47 +01:00
Dr. Carsten Leue
49e89de783 fix: make Curry operations a bit more generic
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-12-18 09:46:46 +01:00
Dr. Carsten Leue
a87de2f644 fix: use endomorphism in optics
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-12-18 09:28:36 +01:00
Dr. Carsten Leue
6d043d2752 fix: common functions for endomorphism
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-12-17 22:59:25 +01:00
Dr. Carsten Leue
1d02f21ff5 fix: rename FormEndomorphism and FormBuilder
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-12-17 22:38:55 +01:00
Dr. Carsten Leue
e82575fe08 fix: consistent endomorphism
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-12-17 13:24:05 +01:00
Dr. Carsten Leue
5fcd0b1595 fix: use endomorphism
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-12-17 12:34:15 +01:00
Dr. Carsten Leue
5caabf478c fix: add Lens for FormData
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-12-16 23:38:03 +01:00
Dr. Carsten Leue
b7ec18c83e fix: Content-Length header in Requester
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-12-16 22:06:40 +01:00
Dr. Carsten Leue
96686425fb fix: add WithFormData and WithJson
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-12-16 19:29:20 +01:00
Dr. Carsten Leue
1f675e08fa fix: add support for request builder
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-12-16 16:49:57 +01:00
Dr. Carsten Leue
4d2f410c98 fix: add MakeBodyRequest
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-12-15 16:00:58 +01:00
Dr. Carsten Leue
8f49c1328c fix: add provider factories with more dependencies
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-12-15 15:31:01 +01:00
Dr. Carsten Leue
2a1d5821db fix: expose http client as injection token
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-12-15 15:21:07 +01:00
renovate[bot]
6abbdc5ee1 chore(deps): update actions/setup-go action to v5 2023-12-06 19:24:14 +00:00
renovate[bot]
5fea9858a9 fix(deps): update module github.com/urfave/cli/v2 to v2.26.0 2023-12-03 00:19:17 +00:00
Carsten Leue
e6426c90c0 fix: add WithResource to IO and IOOption (#90)
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-12-01 09:38:30 +01:00
Carsten Leue
54ce59105e fix: add ChainFirstIOK (#89)
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-11-30 14:50:29 +01:00
Carsten Leue
8bb006c741 fix: add a uniq method to arrays (#88)
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-11-30 09:03:16 +01:00
71 changed files with 3205 additions and 316 deletions

View File

@@ -33,7 +33,7 @@ jobs:
with:
fetch-depth: 0
- name: Set up go ${{ matrix.go-version }}
uses: actions/setup-go@v4
uses: actions/setup-go@v5
with:
go-version: ${{ matrix.go-version }}
-
@@ -60,12 +60,12 @@ jobs:
fetch-depth: 0
- name: Set up Node.js ${{ env.NODE_VERSION }}
uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4.0.0
uses: actions/setup-node@b39b52d1213e96004bfcb1c61a8a6fa8ab84f3e8 # v4.0.1
with:
node-version: ${{ env.NODE_VERSION }}
- name: Set up go ${{env.GO_VERSION}}
uses: actions/setup-go@v4
uses: actions/setup-go@v5
with:
go-version: ${{env.GO_VERSION}}

3
.gitignore vendored
View File

@@ -1,3 +1,4 @@
fp-go.exe
main.exe
build/
build/
.idea

View File

@@ -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,14 +305,20 @@ func ConstNil[A any]() []A {
return array.ConstNil[[]A]()
}
func SliceRight[A any](start int) func([]A) []A {
func SliceRight[A any](start int) EM.Endomorphism[[]A] {
return G.SliceRight[[]A](start)
}
// Copy creates a shallow copy of the array
func Copy[A any](b []A) []A {
return G.Copy(b)
}
// Clone creates a deep copy of the array using the provided endomorphism to clone the values
func Clone[A any](f func(A) A) func(as []A) []A {
return G.Clone[[]A](f)
}
// FoldMap maps and folds an array. Map the Array passing each value to the iterating function. Then fold the results using the provided Monoid.
func FoldMap[A, B any](m M.Monoid[B]) func(func(A) B) func([]A) B {
return G.FoldMap[[]A](m)
@@ -327,8 +334,8 @@ func Fold[A any](m M.Monoid[A]) func([]A) A {
return G.Fold[[]A](m)
}
func Push[A any](a A) func([]A) []A {
return G.Push[[]A](a)
func Push[A any](a A) EM.Endomorphism[[]A] {
return G.Push[EM.Endomorphism[[]A]](a)
}
func MonadFlap[B, A any](fab []func(A) B, a A) []B {
@@ -338,3 +345,7 @@ func MonadFlap[B, A any](fab []func(A) B, a A) []B {
func Flap[B, A any](a A) func([]func(A) B) []B {
return G.Flap[func(A) B, []func(A) B, []B, A, B](a)
}
func Prepend[A any](head A) EM.Endomorphism[[]A] {
return G.Prepend[EM.Endomorphism[[]A]](head)
}

View File

@@ -304,6 +304,11 @@ func Copy[AS ~[]A, A any](b AS) AS {
return buf
}
func Clone[AS ~[]A, A any](f func(A) A) func(as AS) AS {
// implementation assumes that map does not optimize for the empty array
return Map[AS, AS](f)
}
func FoldMap[AS ~[]A, A, B any](m M.Monoid[B]) func(func(A) B) func(AS) B {
return func(f func(A) B) func(AS) B {
return func(as AS) B {
@@ -330,7 +335,7 @@ func Fold[AS ~[]A, A any](m M.Monoid[A]) func(AS) A {
}
}
func Push[GA ~[]A, A any](a A) func(GA) GA {
func Push[ENDO ~func(GA) GA, GA ~[]A, A any](a A) ENDO {
return F.Bind2nd(array.Push[GA, A], a)
}
@@ -341,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)
}

32
array/generic/uniq.go Normal file
View File

@@ -0,0 +1,32 @@
package generic
import F "github.com/IBM/fp-go/function"
// StrictUniq converts an array of arbitrary items into an array or unique items
// where uniqueness is determined by the built-in uniqueness constraint
func StrictUniq[AS ~[]A, A comparable](as AS) AS {
return Uniq[AS](F.Identity[A])(as)
}
// uniquePredUnsafe returns a predicate on a map for uniqueness
func uniquePredUnsafe[PRED ~func(A) K, A any, K comparable](f PRED) func(int, A) bool {
lookup := make(map[K]bool)
return func(_ int, a A) bool {
k := f(a)
_, has := lookup[k]
if has {
return false
}
lookup[k] = true
return true
}
}
// Uniq converts an array of arbitrary items into an array or unique items
// where uniqueness is determined based on a key extractor function
func Uniq[AS ~[]A, PRED ~func(A) K, A any, K comparable](f PRED) func(as AS) AS {
return func(as AS) AS {
// we need to create a new predicate for each iteration
return filterWithIndex(as, uniquePredUnsafe(f))
}
}

View File

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

17
array/uniq.go Normal file
View File

@@ -0,0 +1,17 @@
package array
import (
G "github.com/IBM/fp-go/array/generic"
)
// StrictUniq converts an array of arbitrary items into an array or unique items
// where uniqueness is determined by the built-in uniqueness constraint
func StrictUniq[A comparable](as []A) []A {
return G.StrictUniq[[]A](as)
}
// Uniq converts an array of arbitrary items into an array or unique items
// where uniqueness is determined based on a key extractor function
func Uniq[A any, K comparable](f func(A) K) func(as []A) []A {
return G.Uniq[[]A](f)
}

14
array/uniq_test.go Normal file
View File

@@ -0,0 +1,14 @@
package array
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestUniq(t *testing.T) {
data := From(1, 2, 3, 2, 4, 1)
uniq := StrictUniq(data)
assert.Equal(t, From(1, 2, 3, 4), uniq)
}

View File

@@ -34,5 +34,6 @@ func Commands() []*C.Command {
IOEitherCommand(),
IOCommand(),
IOOptionCommand(),
DICommand(),
}
}

231
cli/di.go Normal file
View 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),
)
},
}
}

View File

@@ -295,15 +295,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 +316,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, ", ")

38
di/app.go Normal file
View 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
View File

@@ -0,0 +1,43 @@
// Copyright (c) 2023 IBM Corp.
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package 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

View File

@@ -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]

View File

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

1013
di/gen.go Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -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.

View File

@@ -12,6 +12,7 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package di
import (
@@ -21,7 +22,6 @@ import (
"github.com/IBM/fp-go/errors"
F "github.com/IBM/fp-go/function"
IOE "github.com/IBM/fp-go/ioeither"
T "github.com/IBM/fp-go/tuple"
)
func lookupAt[T any](idx int, token Dependency[T]) func(params []any) E.Either[error, T] {
@@ -32,49 +32,25 @@ func lookupAt[T any](idx int, token Dependency[T]) func(params []any) E.Either[e
)
}
func eraseProviderFactory0[R any](f func() IOE.IOEither[error, R]) func(params ...any) IOE.IOEither[error, any] {
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),
f,
IOE.Map[error](F.ToAny[R]),
)
}
}
func MakeProviderFactory0[R any](
fct func() IOE.IOEither[error, R],
fct IOE.IOEither[error, R],
) DIE.ProviderFactory {
return DIE.MakeProviderFactory(
A.Empty[DIE.Dependency](),
@@ -82,14 +58,14 @@ func MakeProviderFactory0[R any](
)
}
// MakeTokenWithDefault0 create a unique `InjectionToken` for a specific type with an attached default provider
func MakeTokenWithDefault0[R any](name string, fct func() IOE.IOEither[error, R]) InjectionToken[R] {
// MakeTokenWithDefault0 creates a unique [InjectionToken] for a specific type with an attached default [DIE.Provider]
func MakeTokenWithDefault0[R any](name string, fct IOE.IOEither[error, R]) InjectionToken[R] {
return MakeTokenWithDefault[R](name, MakeProviderFactory0(fct))
}
func MakeProvider0[R any](
token InjectionToken[R],
fct func() IOE.IOEither[error, R],
fct IOE.IOEither[error, R],
) DIE.Provider {
return DIE.MakeProvider(
token,
@@ -97,70 +73,7 @@ func MakeProvider0[R any](
)
}
func MakeProviderFactory1[T1, R any](
d1 Dependency[T1],
fct func(T1) IOE.IOEither[error, R],
) DIE.ProviderFactory {
return DIE.MakeProviderFactory(
A.From[DIE.Dependency](d1),
eraseProviderFactory1(d1, fct),
)
}
// MakeTokenWithDefault1 create a unique `InjectionToken` for a specific type with an attached default provider
func MakeTokenWithDefault1[T1, R any](name string,
d1 Dependency[T1],
fct func(T1) IOE.IOEither[error, R]) InjectionToken[R] {
return MakeTokenWithDefault[R](name, MakeProviderFactory1(d1, fct))
}
func MakeProvider1[T1, R any](
token InjectionToken[R],
d1 Dependency[T1],
fct func(T1) IOE.IOEither[error, R],
) DIE.Provider {
return DIE.MakeProvider(
token,
MakeProviderFactory1(d1, fct),
)
}
func MakeProviderFactory2[T1, T2, R any](
d1 Dependency[T1],
d2 Dependency[T2],
fct func(T1, T2) IOE.IOEither[error, R],
) DIE.ProviderFactory {
return DIE.MakeProviderFactory(
A.From[DIE.Dependency](d1, d2),
eraseProviderFactory2(d1, d2, fct),
)
}
// MakeTokenWithDefault2 create a unique `InjectionToken` for a specific type with an attached default provider
func MakeTokenWithDefault2[T1, T2, R any](name string,
d1 Dependency[T1],
d2 Dependency[T2],
fct func(T1, T2) IOE.IOEither[error, R]) InjectionToken[R] {
return MakeTokenWithDefault[R](name, MakeProviderFactory2(d1, d2, fct))
}
func MakeProvider2[T1, T2, R any](
token InjectionToken[R],
d1 Dependency[T1],
d2 Dependency[T2],
fct func(T1, T2) IOE.IOEither[error, R],
) DIE.Provider {
return DIE.MakeProvider(
token,
MakeProviderFactory2(d1, d2, fct),
)
}
// ConstProvider simple implementation for a provider with a constant value
func ConstProvider[R any](token InjectionToken[R], value R) DIE.Provider {
return MakeProvider0[R](token, F.Constant(IOE.Of[error](value)))
return MakeProvider0[R](token, IOE.Of[error](value))
}

View File

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

View File

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

View File

@@ -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 (

View File

@@ -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 (

30
endomorphism/curry.go Normal file
View File

@@ -0,0 +1,30 @@
// Copyright (c) 2023 IBM Corp.
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package 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
View File

@@ -0,0 +1,36 @@
// Copyright (c) 2023 IBM Corp.
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package 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)
}

View File

@@ -0,0 +1,36 @@
// Copyright (c) 2023 IBM Corp.
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package 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)
}
}
}
}

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

View File

@@ -0,0 +1,47 @@
// Copyright (c) 2023 IBM Corp.
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package generic
import (
F "github.com/IBM/fp-go/function"
M "github.com/IBM/fp-go/monoid"
S "github.com/IBM/fp-go/semigroup"
)
// Of converts any function to an [Endomorphism]
func Of[ENDO ~func(A) A, F ~func(A) A, A any](f F) ENDO {
return func(a A) A {
return f(a)
}
}
func Identity[ENDO ~func(A) A, A any]() ENDO {
return Of[ENDO](F.Identity[A])
}
func Compose[ENDO ~func(A) A, A any](f1, f2 ENDO) ENDO {
return Of[ENDO](F.Flow2(f1, f2))
}
// Semigroup for the Endomorphism where the `concat` operation is the usual function composition.
func Semigroup[ENDO ~func(A) A, A any]() S.Semigroup[ENDO] {
return S.MakeSemigroup(Compose[ENDO])
}
// Monoid for the Endomorphism where the `concat` operation is the usual function composition.
func Monoid[ENDO ~func(A) A, A any]() M.Monoid[ENDO] {
return M.MakeMonoid(Compose[ENDO], Identity[ENDO]())
}

View File

@@ -16,17 +16,30 @@
package endomorphism
import (
F "github.com/IBM/fp-go/function"
G "github.com/IBM/fp-go/endomorphism/generic"
M "github.com/IBM/fp-go/monoid"
S "github.com/IBM/fp-go/semigroup"
)
// Endomorphism is a function that
type Endomorphism[A any] func(A) A
// Of converts any function to an [Endomorphism]
func Of[F ~func(A) A, A any](f F) Endomorphism[A] {
return G.Of[Endomorphism[A]](f)
}
// Identity returns the identity [Endomorphism]
func Identity[A any]() Endomorphism[A] {
return G.Identity[Endomorphism[A]]()
}
// Semigroup for the Endomorphism where the `concat` operation is the usual function composition.
func Semigroup[A any]() S.Semigroup[func(A) A] {
return S.MakeSemigroup(F.Flow2[func(A) A, func(A) A])
func Semigroup[A any]() S.Semigroup[Endomorphism[A]] {
return G.Semigroup[Endomorphism[A]]()
}
// Monoid for the Endomorphism where the `concat` operation is the usual function composition.
func Monoid[A any]() M.Monoid[func(A) A] {
return M.MakeMonoid(F.Flow2[func(A) A, func(A) A], F.Identity[A])
func Monoid[A any]() M.Monoid[Endomorphism[A]] {
return G.Monoid[Endomorphism[A]]()
}

View File

@@ -15,7 +15,10 @@
package file
import "path/filepath"
import (
"io"
"path/filepath"
)
// Join appends a filename to a root path
func Join(name string) func(root string) string {
@@ -23,3 +26,13 @@ func Join(name string) func(root string) string {
return filepath.Join(root, name)
}
}
// ToReader converts a [io.Reader]
func ToReader[R io.Reader](r R) io.Reader {
return r
}
// ToCloser converts a [io.Closer]
func ToCloser[C io.Closer](c C) io.Closer {
return c
}

View File

@@ -1,6 +1,6 @@
// Code generated by go generate; DO NOT EDIT.
// This file was generated by robots at
// 2023-10-23 08:30:44.6474482 +0200 CEST m=+0.150851901
// 2023-12-18 09:38:59.1616876 +0100 CET m=+0.008641801
package function

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
// Code generated by go generate; DO NOT EDIT.
// This file was generated by robots at
// 2023-10-23 08:30:41.7972101 +0200 CEST m=+0.008029101
// 2023-12-18 09:38:51.4946446 +0100 CET m=+0.008838401
package function
@@ -55,7 +55,7 @@ func Nullary1[F1 ~func() T1, T1 any](f1 F1) func() T1 {
// Curry1 takes a function with 1 parameters and returns a cascade of functions each taking only one parameter.
// The inverse function is [Uncurry1]
func Curry1[T0, T1 any](f func(T0) T1) func(T0) T1 {
func Curry1[FCT ~func(T0) T1, T0, T1 any](f FCT) func(T0) T1 {
return func(t0 T0) T1 {
return f(t0)
}
@@ -63,7 +63,7 @@ func Curry1[T0, T1 any](f func(T0) T1) func(T0) T1 {
// Uncurry1 takes a cascade of 1 functions each taking only one parameter and returns a function with 1 parameters .
// The inverse function is [Curry1]
func Uncurry1[T0, T1 any](f func(T0) T1) func(T0) T1 {
func Uncurry1[FCT ~func(T0) T1, T0, T1 any](f FCT) func(T0) T1 {
return func(t0 T0) T1 {
return f(t0)
}
@@ -115,7 +115,7 @@ func Nullary2[F1 ~func() T1, F2 ~func(T1) T2, T1, T2 any](f1 F1, f2 F2) func() T
// Curry2 takes a function with 2 parameters and returns a cascade of functions each taking only one parameter.
// The inverse function is [Uncurry2]
func Curry2[T0, T1, T2 any](f func(T0, T1) T2) func(T0) func(T1) T2 {
func Curry2[FCT ~func(T0, T1) T2, T0, T1, T2 any](f FCT) func(T0) func(T1) T2 {
return func(t0 T0) func(t1 T1) T2 {
return func(t1 T1) T2 {
return f(t0, t1)
@@ -125,7 +125,7 @@ func Curry2[T0, T1, T2 any](f func(T0, T1) T2) func(T0) func(T1) T2 {
// Uncurry2 takes a cascade of 2 functions each taking only one parameter and returns a function with 2 parameters .
// The inverse function is [Curry2]
func Uncurry2[T0, T1, T2 any](f func(T0) func(T1) T2) func(T0, T1) T2 {
func Uncurry2[FCT ~func(T0) func(T1) T2, T0, T1, T2 any](f FCT) func(T0, T1) T2 {
return func(t0 T0, t1 T1) T2 {
return f(t0)(t1)
}
@@ -178,7 +178,7 @@ func Nullary3[F1 ~func() T1, F2 ~func(T1) T2, F3 ~func(T2) T3, T1, T2, T3 any](f
// Curry3 takes a function with 3 parameters and returns a cascade of functions each taking only one parameter.
// The inverse function is [Uncurry3]
func Curry3[T0, T1, T2, T3 any](f func(T0, T1, T2) T3) func(T0) func(T1) func(T2) T3 {
func Curry3[FCT ~func(T0, T1, T2) T3, T0, T1, T2, T3 any](f FCT) func(T0) func(T1) func(T2) T3 {
return func(t0 T0) func(t1 T1) func(t2 T2) T3 {
return func(t1 T1) func(t2 T2) T3 {
return func(t2 T2) T3 {
@@ -190,7 +190,7 @@ func Curry3[T0, T1, T2, T3 any](f func(T0, T1, T2) T3) func(T0) func(T1) func(T2
// Uncurry3 takes a cascade of 3 functions each taking only one parameter and returns a function with 3 parameters .
// The inverse function is [Curry3]
func Uncurry3[T0, T1, T2, T3 any](f func(T0) func(T1) func(T2) T3) func(T0, T1, T2) T3 {
func Uncurry3[FCT ~func(T0) func(T1) func(T2) T3, T0, T1, T2, T3 any](f FCT) func(T0, T1, T2) T3 {
return func(t0 T0, t1 T1, t2 T2) T3 {
return f(t0)(t1)(t2)
}
@@ -244,7 +244,7 @@ func Nullary4[F1 ~func() T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4,
// Curry4 takes a function with 4 parameters and returns a cascade of functions each taking only one parameter.
// The inverse function is [Uncurry4]
func Curry4[T0, T1, T2, T3, T4 any](f func(T0, T1, T2, T3) T4) func(T0) func(T1) func(T2) func(T3) T4 {
func Curry4[FCT ~func(T0, T1, T2, T3) T4, T0, T1, T2, T3, T4 any](f FCT) func(T0) func(T1) func(T2) func(T3) T4 {
return func(t0 T0) func(t1 T1) func(t2 T2) func(t3 T3) T4 {
return func(t1 T1) func(t2 T2) func(t3 T3) T4 {
return func(t2 T2) func(t3 T3) T4 {
@@ -258,7 +258,7 @@ func Curry4[T0, T1, T2, T3, T4 any](f func(T0, T1, T2, T3) T4) func(T0) func(T1)
// Uncurry4 takes a cascade of 4 functions each taking only one parameter and returns a function with 4 parameters .
// The inverse function is [Curry4]
func Uncurry4[T0, T1, T2, T3, T4 any](f func(T0) func(T1) func(T2) func(T3) T4) func(T0, T1, T2, T3) T4 {
func Uncurry4[FCT ~func(T0) func(T1) func(T2) func(T3) T4, T0, T1, T2, T3, T4 any](f FCT) func(T0, T1, T2, T3) T4 {
return func(t0 T0, t1 T1, t2 T2, t3 T3) T4 {
return f(t0)(t1)(t2)(t3)
}
@@ -313,7 +313,7 @@ func Nullary5[F1 ~func() T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4,
// Curry5 takes a function with 5 parameters and returns a cascade of functions each taking only one parameter.
// The inverse function is [Uncurry5]
func Curry5[T0, T1, T2, T3, T4, T5 any](f func(T0, T1, T2, T3, T4) T5) func(T0) func(T1) func(T2) func(T3) func(T4) T5 {
func Curry5[FCT ~func(T0, T1, T2, T3, T4) T5, T0, T1, T2, T3, T4, T5 any](f FCT) func(T0) func(T1) func(T2) func(T3) func(T4) T5 {
return func(t0 T0) func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) T5 {
return func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) T5 {
return func(t2 T2) func(t3 T3) func(t4 T4) T5 {
@@ -329,7 +329,7 @@ func Curry5[T0, T1, T2, T3, T4, T5 any](f func(T0, T1, T2, T3, T4) T5) func(T0)
// Uncurry5 takes a cascade of 5 functions each taking only one parameter and returns a function with 5 parameters .
// The inverse function is [Curry5]
func Uncurry5[T0, T1, T2, T3, T4, T5 any](f func(T0) func(T1) func(T2) func(T3) func(T4) T5) func(T0, T1, T2, T3, T4) T5 {
func Uncurry5[FCT ~func(T0) func(T1) func(T2) func(T3) func(T4) T5, T0, T1, T2, T3, T4, T5 any](f FCT) func(T0, T1, T2, T3, T4) T5 {
return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4) T5 {
return f(t0)(t1)(t2)(t3)(t4)
}
@@ -385,7 +385,7 @@ func Nullary6[F1 ~func() T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4,
// Curry6 takes a function with 6 parameters and returns a cascade of functions each taking only one parameter.
// The inverse function is [Uncurry6]
func Curry6[T0, T1, T2, T3, T4, T5, T6 any](f func(T0, T1, T2, T3, T4, T5) T6) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) T6 {
func Curry6[FCT ~func(T0, T1, T2, T3, T4, T5) T6, T0, T1, T2, T3, T4, T5, T6 any](f FCT) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) T6 {
return func(t0 T0) func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) T6 {
return func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) T6 {
return func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) T6 {
@@ -403,7 +403,7 @@ func Curry6[T0, T1, T2, T3, T4, T5, T6 any](f func(T0, T1, T2, T3, T4, T5) T6) f
// Uncurry6 takes a cascade of 6 functions each taking only one parameter and returns a function with 6 parameters .
// The inverse function is [Curry6]
func Uncurry6[T0, T1, T2, T3, T4, T5, T6 any](f func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) T6) func(T0, T1, T2, T3, T4, T5) T6 {
func Uncurry6[FCT ~func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) T6, T0, T1, T2, T3, T4, T5, T6 any](f FCT) func(T0, T1, T2, T3, T4, T5) T6 {
return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5) T6 {
return f(t0)(t1)(t2)(t3)(t4)(t5)
}
@@ -460,7 +460,7 @@ func Nullary7[F1 ~func() T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4,
// Curry7 takes a function with 7 parameters and returns a cascade of functions each taking only one parameter.
// The inverse function is [Uncurry7]
func Curry7[T0, T1, T2, T3, T4, T5, T6, T7 any](f func(T0, T1, T2, T3, T4, T5, T6) T7) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) T7 {
func Curry7[FCT ~func(T0, T1, T2, T3, T4, T5, T6) T7, T0, T1, T2, T3, T4, T5, T6, T7 any](f FCT) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) T7 {
return func(t0 T0) func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) T7 {
return func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) T7 {
return func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) T7 {
@@ -480,7 +480,7 @@ func Curry7[T0, T1, T2, T3, T4, T5, T6, T7 any](f func(T0, T1, T2, T3, T4, T5, T
// Uncurry7 takes a cascade of 7 functions each taking only one parameter and returns a function with 7 parameters .
// The inverse function is [Curry7]
func Uncurry7[T0, T1, T2, T3, T4, T5, T6, T7 any](f func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) T7) func(T0, T1, T2, T3, T4, T5, T6) T7 {
func Uncurry7[FCT ~func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) T7, T0, T1, T2, T3, T4, T5, T6, T7 any](f FCT) func(T0, T1, T2, T3, T4, T5, T6) T7 {
return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6) T7 {
return f(t0)(t1)(t2)(t3)(t4)(t5)(t6)
}
@@ -538,7 +538,7 @@ func Nullary8[F1 ~func() T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4,
// Curry8 takes a function with 8 parameters and returns a cascade of functions each taking only one parameter.
// The inverse function is [Uncurry8]
func Curry8[T0, T1, T2, T3, T4, T5, T6, T7, T8 any](f func(T0, T1, T2, T3, T4, T5, T6, T7) T8) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T8 {
func Curry8[FCT ~func(T0, T1, T2, T3, T4, T5, T6, T7) T8, T0, T1, T2, T3, T4, T5, T6, T7, T8 any](f FCT) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T8 {
return func(t0 T0) func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) T8 {
return func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) T8 {
return func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) T8 {
@@ -560,7 +560,7 @@ func Curry8[T0, T1, T2, T3, T4, T5, T6, T7, T8 any](f func(T0, T1, T2, T3, T4, T
// Uncurry8 takes a cascade of 8 functions each taking only one parameter and returns a function with 8 parameters .
// The inverse function is [Curry8]
func Uncurry8[T0, T1, T2, T3, T4, T5, T6, T7, T8 any](f func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T8) func(T0, T1, T2, T3, T4, T5, T6, T7) T8 {
func Uncurry8[FCT ~func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T8, T0, T1, T2, T3, T4, T5, T6, T7, T8 any](f FCT) func(T0, T1, T2, T3, T4, T5, T6, T7) T8 {
return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7) T8 {
return f(t0)(t1)(t2)(t3)(t4)(t5)(t6)(t7)
}
@@ -619,7 +619,7 @@ func Nullary9[F1 ~func() T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4,
// Curry9 takes a function with 9 parameters and returns a cascade of functions each taking only one parameter.
// The inverse function is [Uncurry9]
func Curry9[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9 any](f func(T0, T1, T2, T3, T4, T5, T6, T7, T8) T9) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T9 {
func Curry9[FCT ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8) T9, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9 any](f FCT) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T9 {
return func(t0 T0) func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) T9 {
return func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) T9 {
return func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) T9 {
@@ -643,7 +643,7 @@ func Curry9[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9 any](f func(T0, T1, T2, T3, T
// Uncurry9 takes a cascade of 9 functions each taking only one parameter and returns a function with 9 parameters .
// The inverse function is [Curry9]
func Uncurry9[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9 any](f func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T9) func(T0, T1, T2, T3, T4, T5, T6, T7, T8) T9 {
func Uncurry9[FCT ~func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T9, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9 any](f FCT) func(T0, T1, T2, T3, T4, T5, T6, T7, T8) T9 {
return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8) T9 {
return f(t0)(t1)(t2)(t3)(t4)(t5)(t6)(t7)(t8)
}
@@ -703,7 +703,7 @@ func Nullary10[F1 ~func() T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4,
// Curry10 takes a function with 10 parameters and returns a cascade of functions each taking only one parameter.
// The inverse function is [Uncurry10]
func Curry10[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any](f func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) T10) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T10 {
func Curry10[FCT ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) T10, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any](f FCT) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T10 {
return func(t0 T0) func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) T10 {
return func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) T10 {
return func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) T10 {
@@ -729,7 +729,7 @@ func Curry10[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any](f func(T0, T1, T2,
// Uncurry10 takes a cascade of 10 functions each taking only one parameter and returns a function with 10 parameters .
// The inverse function is [Curry10]
func Uncurry10[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any](f func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T10) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) T10 {
func Uncurry10[FCT ~func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T10, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any](f FCT) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) T10 {
return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9) T10 {
return f(t0)(t1)(t2)(t3)(t4)(t5)(t6)(t7)(t8)(t9)
}
@@ -790,7 +790,7 @@ func Nullary11[F1 ~func() T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4,
// Curry11 takes a function with 11 parameters and returns a cascade of functions each taking only one parameter.
// The inverse function is [Uncurry11]
func Curry11[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11 any](f func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10) T11) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T11 {
func Curry11[FCT ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10) T11, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11 any](f FCT) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T11 {
return func(t0 T0) func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) T11 {
return func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) T11 {
return func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) T11 {
@@ -818,7 +818,7 @@ func Curry11[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11 any](f func(T0, T1
// Uncurry11 takes a cascade of 11 functions each taking only one parameter and returns a function with 11 parameters .
// The inverse function is [Curry11]
func Uncurry11[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11 any](f func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T11) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10) T11 {
func Uncurry11[FCT ~func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T11, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11 any](f FCT) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10) T11 {
return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10) T11 {
return f(t0)(t1)(t2)(t3)(t4)(t5)(t6)(t7)(t8)(t9)(t10)
}
@@ -880,7 +880,7 @@ func Nullary12[F1 ~func() T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4,
// Curry12 takes a function with 12 parameters and returns a cascade of functions each taking only one parameter.
// The inverse function is [Uncurry12]
func Curry12[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12 any](f func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11) T12) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) T12 {
func Curry12[FCT ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11) T12, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12 any](f FCT) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) T12 {
return func(t0 T0) func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) T12 {
return func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) T12 {
return func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) T12 {
@@ -910,7 +910,7 @@ func Curry12[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12 any](f func(T
// Uncurry12 takes a cascade of 12 functions each taking only one parameter and returns a function with 12 parameters .
// The inverse function is [Curry12]
func Uncurry12[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12 any](f func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) T12) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11) T12 {
func Uncurry12[FCT ~func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) T12, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12 any](f FCT) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11) T12 {
return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11) T12 {
return f(t0)(t1)(t2)(t3)(t4)(t5)(t6)(t7)(t8)(t9)(t10)(t11)
}
@@ -973,7 +973,7 @@ func Nullary13[F1 ~func() T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4,
// Curry13 takes a function with 13 parameters and returns a cascade of functions each taking only one parameter.
// The inverse function is [Uncurry13]
func Curry13[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13 any](f func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12) T13) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) T13 {
func Curry13[FCT ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12) T13, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13 any](f FCT) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) T13 {
return func(t0 T0) func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) T13 {
return func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) T13 {
return func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) T13 {
@@ -1005,7 +1005,7 @@ func Curry13[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13 any](f f
// Uncurry13 takes a cascade of 13 functions each taking only one parameter and returns a function with 13 parameters .
// The inverse function is [Curry13]
func Uncurry13[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13 any](f func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) T13) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12) T13 {
func Uncurry13[FCT ~func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) T13, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13 any](f FCT) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12) T13 {
return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12) T13 {
return f(t0)(t1)(t2)(t3)(t4)(t5)(t6)(t7)(t8)(t9)(t10)(t11)(t12)
}
@@ -1069,7 +1069,7 @@ func Nullary14[F1 ~func() T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4,
// Curry14 takes a function with 14 parameters and returns a cascade of functions each taking only one parameter.
// The inverse function is [Uncurry14]
func Curry14[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14 any](f func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13) T14) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) T14 {
func Curry14[FCT ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13) T14, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14 any](f FCT) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) T14 {
return func(t0 T0) func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) T14 {
return func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) T14 {
return func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) T14 {
@@ -1103,7 +1103,7 @@ func Curry14[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14 any
// Uncurry14 takes a cascade of 14 functions each taking only one parameter and returns a function with 14 parameters .
// The inverse function is [Curry14]
func Uncurry14[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14 any](f func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) T14) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13) T14 {
func Uncurry14[FCT ~func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) T14, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14 any](f FCT) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13) T14 {
return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12, t13 T13) T14 {
return f(t0)(t1)(t2)(t3)(t4)(t5)(t6)(t7)(t8)(t9)(t10)(t11)(t12)(t13)
}
@@ -1168,7 +1168,7 @@ func Nullary15[F1 ~func() T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4,
// Curry15 takes a function with 15 parameters and returns a cascade of functions each taking only one parameter.
// The inverse function is [Uncurry15]
func Curry15[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15 any](f func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14) T15) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) T15 {
func Curry15[FCT ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14) T15, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15 any](f FCT) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) T15 {
return func(t0 T0) func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) T15 {
return func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) T15 {
return func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) T15 {
@@ -1204,7 +1204,7 @@ func Curry15[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T1
// Uncurry15 takes a cascade of 15 functions each taking only one parameter and returns a function with 15 parameters .
// The inverse function is [Curry15]
func Uncurry15[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15 any](f func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) T15) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14) T15 {
func Uncurry15[FCT ~func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) T15, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15 any](f FCT) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14) T15 {
return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12, t13 T13, t14 T14) T15 {
return f(t0)(t1)(t2)(t3)(t4)(t5)(t6)(t7)(t8)(t9)(t10)(t11)(t12)(t13)(t14)
}
@@ -1270,7 +1270,7 @@ func Nullary16[F1 ~func() T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4,
// Curry16 takes a function with 16 parameters and returns a cascade of functions each taking only one parameter.
// The inverse function is [Uncurry16]
func Curry16[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16 any](f func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15) T16) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) T16 {
func Curry16[FCT ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15) T16, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16 any](f FCT) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) T16 {
return func(t0 T0) func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) T16 {
return func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) T16 {
return func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) T16 {
@@ -1308,7 +1308,7 @@ func Curry16[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T1
// Uncurry16 takes a cascade of 16 functions each taking only one parameter and returns a function with 16 parameters .
// The inverse function is [Curry16]
func Uncurry16[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16 any](f func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) T16) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15) T16 {
func Uncurry16[FCT ~func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) T16, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16 any](f FCT) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15) T16 {
return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12, t13 T13, t14 T14, t15 T15) T16 {
return f(t0)(t1)(t2)(t3)(t4)(t5)(t6)(t7)(t8)(t9)(t10)(t11)(t12)(t13)(t14)(t15)
}
@@ -1375,7 +1375,7 @@ func Nullary17[F1 ~func() T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4,
// Curry17 takes a function with 17 parameters and returns a cascade of functions each taking only one parameter.
// The inverse function is [Uncurry17]
func Curry17[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17 any](f func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16) T17) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) func(T16) T17 {
func Curry17[FCT ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16) T17, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17 any](f FCT) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) func(T16) T17 {
return func(t0 T0) func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) T17 {
return func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) T17 {
return func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) T17 {
@@ -1415,7 +1415,7 @@ func Curry17[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T1
// Uncurry17 takes a cascade of 17 functions each taking only one parameter and returns a function with 17 parameters .
// The inverse function is [Curry17]
func Uncurry17[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17 any](f func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) func(T16) T17) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16) T17 {
func Uncurry17[FCT ~func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) func(T16) T17, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17 any](f FCT) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16) T17 {
return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12, t13 T13, t14 T14, t15 T15, t16 T16) T17 {
return f(t0)(t1)(t2)(t3)(t4)(t5)(t6)(t7)(t8)(t9)(t10)(t11)(t12)(t13)(t14)(t15)(t16)
}
@@ -1483,7 +1483,7 @@ func Nullary18[F1 ~func() T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4,
// Curry18 takes a function with 18 parameters and returns a cascade of functions each taking only one parameter.
// The inverse function is [Uncurry18]
func Curry18[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18 any](f func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17) T18) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) func(T16) func(T17) T18 {
func Curry18[FCT ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17) T18, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18 any](f FCT) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) func(T16) func(T17) T18 {
return func(t0 T0) func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) func(t17 T17) T18 {
return func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) func(t17 T17) T18 {
return func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) func(t17 T17) T18 {
@@ -1525,7 +1525,7 @@ func Curry18[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T1
// Uncurry18 takes a cascade of 18 functions each taking only one parameter and returns a function with 18 parameters .
// The inverse function is [Curry18]
func Uncurry18[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18 any](f func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) func(T16) func(T17) T18) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17) T18 {
func Uncurry18[FCT ~func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) func(T16) func(T17) T18, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18 any](f FCT) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17) T18 {
return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12, t13 T13, t14 T14, t15 T15, t16 T16, t17 T17) T18 {
return f(t0)(t1)(t2)(t3)(t4)(t5)(t6)(t7)(t8)(t9)(t10)(t11)(t12)(t13)(t14)(t15)(t16)(t17)
}
@@ -1594,7 +1594,7 @@ func Nullary19[F1 ~func() T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4,
// Curry19 takes a function with 19 parameters and returns a cascade of functions each taking only one parameter.
// The inverse function is [Uncurry19]
func Curry19[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19 any](f func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18) T19) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) func(T16) func(T17) func(T18) T19 {
func Curry19[FCT ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18) T19, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19 any](f FCT) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) func(T16) func(T17) func(T18) T19 {
return func(t0 T0) func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) func(t17 T17) func(t18 T18) T19 {
return func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) func(t17 T17) func(t18 T18) T19 {
return func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) func(t17 T17) func(t18 T18) T19 {
@@ -1638,7 +1638,7 @@ func Curry19[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T1
// Uncurry19 takes a cascade of 19 functions each taking only one parameter and returns a function with 19 parameters .
// The inverse function is [Curry19]
func Uncurry19[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19 any](f func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) func(T16) func(T17) func(T18) T19) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18) T19 {
func Uncurry19[FCT ~func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) func(T16) func(T17) func(T18) T19, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19 any](f FCT) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18) T19 {
return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12, t13 T13, t14 T14, t15 T15, t16 T16, t17 T17, t18 T18) T19 {
return f(t0)(t1)(t2)(t3)(t4)(t5)(t6)(t7)(t8)(t9)(t10)(t11)(t12)(t13)(t14)(t15)(t16)(t17)(t18)
}
@@ -1708,7 +1708,7 @@ func Nullary20[F1 ~func() T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4,
// Curry20 takes a function with 20 parameters and returns a cascade of functions each taking only one parameter.
// The inverse function is [Uncurry20]
func Curry20[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20 any](f func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19) T20) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) func(T16) func(T17) func(T18) func(T19) T20 {
func Curry20[FCT ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19) T20, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20 any](f FCT) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) func(T16) func(T17) func(T18) func(T19) T20 {
return func(t0 T0) func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) func(t17 T17) func(t18 T18) func(t19 T19) T20 {
return func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) func(t17 T17) func(t18 T18) func(t19 T19) T20 {
return func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) func(t17 T17) func(t18 T18) func(t19 T19) T20 {
@@ -1754,7 +1754,7 @@ func Curry20[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T1
// Uncurry20 takes a cascade of 20 functions each taking only one parameter and returns a function with 20 parameters .
// The inverse function is [Curry20]
func Uncurry20[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20 any](f func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) func(T16) func(T17) func(T18) func(T19) T20) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19) T20 {
func Uncurry20[FCT ~func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) func(T16) func(T17) func(T18) func(T19) T20, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20 any](f FCT) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19) T20 {
return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12, t13 T13, t14 T14, t15 T15, t16 T16, t17 T17, t18 T18, t19 T19) T20 {
return f(t0)(t1)(t2)(t3)(t4)(t5)(t6)(t7)(t8)(t9)(t10)(t11)(t12)(t13)(t14)(t15)(t16)(t17)(t18)(t19)
}

View File

@@ -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
View File

@@ -4,7 +4,7 @@ go 1.20
require (
github.com/stretchr/testify v1.8.4
github.com/urfave/cli/v2 v2.25.7
github.com/urfave/cli/v2 v2.27.1
)
require (
@@ -12,6 +12,6 @@ require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
github.com/xrash/smetrics v0.0.0-20231213231151-1d8dd44e695e // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

8
go.sum
View File

@@ -8,10 +8,10 @@ github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs=
github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
github.com/urfave/cli/v2 v2.27.1 h1:8xSQ6szndafKVRmfyeUMxkNUJQMjL1F2zmsZ+qHpfho=
github.com/urfave/cli/v2 v2.27.1/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ=
github.com/xrash/smetrics v0.0.0-20231213231151-1d8dd44e695e h1:+SOyEddqYF09QP7vr7CgJ1eti3pY9Fn3LHO1M1r/0sI=
github.com/xrash/smetrics v0.0.0-20231213231151-1d8dd44e695e/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

22
http/content/content.go Normal file
View 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"
)

74
http/form/form.go Normal file
View File

@@ -0,0 +1,74 @@
// Copyright (c) 2023 IBM Corp.
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package form
import (
"net/url"
A "github.com/IBM/fp-go/array"
ENDO "github.com/IBM/fp-go/endomorphism"
F "github.com/IBM/fp-go/function"
L "github.com/IBM/fp-go/optics/lens"
LA "github.com/IBM/fp-go/optics/lens/array"
LRG "github.com/IBM/fp-go/optics/lens/record/generic"
O "github.com/IBM/fp-go/option"
RG "github.com/IBM/fp-go/record/generic"
)
type (
// Endomorphism returns an [ENDO.Endomorphism] that transforms a form
Endomorphism = ENDO.Endomorphism[url.Values]
)
var (
// Default is the default form field
Default = make(url.Values)
noField = O.None[string]()
// FormMonoid is the [M.Monoid] for the [Endomorphism]
Monoid = ENDO.Monoid[url.Values]()
// ValuesMonoid is a [M.Monoid] to concatenate [url.Values] maps
ValuesMonoid = RG.UnionMonoid[url.Values](A.Semigroup[string]())
// AtValues is a [L.Lens] that focusses on the values of a form field
AtValues = LRG.AtRecord[url.Values, []string]
composeHead = F.Pipe1(
LA.AtHead[string](),
L.ComposeOptions[url.Values, string](A.Empty[string]()),
)
// AtValue is a [L.Lens] that focusses on first value in form fields
AtValue = F.Flow2(
AtValues,
composeHead,
)
)
// WithValue creates a [FormBuilder] for a certain field
func WithValue(name string) func(value string) Endomorphism {
return F.Flow2(
O.Of[string],
AtValue(name).Set,
)
}
// WithoutValue creates a [FormBuilder] that removes a field
func WithoutValue(name string) Endomorphism {
return AtValue(name).Set(noField)
}

93
http/form/form_test.go Normal file
View File

@@ -0,0 +1,93 @@
// Copyright (c) 2023 IBM Corp.
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package form
import (
"net/url"
"testing"
A "github.com/IBM/fp-go/array"
"github.com/IBM/fp-go/eq"
F "github.com/IBM/fp-go/function"
LT "github.com/IBM/fp-go/optics/lens/testing"
O "github.com/IBM/fp-go/option"
RG "github.com/IBM/fp-go/record/generic"
S "github.com/IBM/fp-go/string"
"github.com/stretchr/testify/assert"
)
var (
sEq = eq.FromEquals(S.Eq)
valuesEq = RG.Eq[url.Values](A.Eq(sEq))
)
func TestLaws(t *testing.T) {
name := "Content-Type"
fieldLaws := LT.AssertLaws[url.Values, O.Option[string]](t, O.Eq(sEq), valuesEq)(AtValue(name))
n := O.None[string]()
s1 := O.Some("s1")
v1 := F.Pipe1(
Default,
WithValue(name)("v1"),
)
v2 := F.Pipe1(
Default,
WithValue("Other-Header")("v2"),
)
assert.True(t, fieldLaws(Default, n))
assert.True(t, fieldLaws(v1, n))
assert.True(t, fieldLaws(v2, n))
assert.True(t, fieldLaws(Default, s1))
assert.True(t, fieldLaws(v1, s1))
assert.True(t, fieldLaws(v2, s1))
}
func TestFormField(t *testing.T) {
v1 := F.Pipe1(
Default,
WithValue("h1")("v1"),
)
v2 := F.Pipe1(
v1,
WithValue("h2")("v2"),
)
// make sure the code does not change structures
assert.False(t, valuesEq.Equals(Default, v1))
assert.False(t, valuesEq.Equals(Default, v2))
assert.False(t, valuesEq.Equals(v1, v2))
// check for existence of values
assert.Equal(t, "v1", v1.Get("h1"))
assert.Equal(t, "v1", v2.Get("h1"))
assert.Equal(t, "v2", v2.Get("h2"))
// check getter on lens
l1 := AtValue("h1")
l2 := AtValue("h2")
assert.Equal(t, O.Of("v1"), l1.Get(v1))
assert.Equal(t, O.Of("v1"), l1.Get(v2))
assert.Equal(t, O.Of("v2"), l2.Get(v2))
}

58
http/headers/headers.go Normal file
View File

@@ -0,0 +1,58 @@
// Copyright (c) 2023 IBM Corp.
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package 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,
)
)

View File

@@ -0,0 +1,58 @@
// Copyright (c) 2023 IBM Corp.
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package 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))
}

View File

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

28
internal/array/prepend.go Normal file
View 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
}
}

View File

@@ -49,6 +49,16 @@ func MonadChain[A, B, HKTFA, HKTFB any](
return fchain(ma, O.Fold(F.Nullary2(O.None[B], fof), f))
}
func Chain[A, B, HKTFA, HKTFB any](
fchain func(HKTFA, func(O.Option[A]) HKTFB) HKTFB,
fof func(O.Option[B]) HKTFB,
f func(A) HKTFB) func(ma HKTFA) HKTFB {
// dispatch to the even more generic implementation
return func(ma HKTFA) HKTFB {
return MonadChain(fchain, fof, ma, f)
}
}
func MonadAp[A, B, HKTFAB, HKTFGAB, HKTFA, HKTFB any](
fap func(HKTFGAB, HKTFA) HKTFB,
fmap func(HKTFAB, func(O.Option[func(A) B]) func(O.Option[A]) O.Option[B]) HKTFGAB,

30
io/generic/resource.go Normal file
View File

@@ -0,0 +1,30 @@
// Copyright (c) 2023 IBM Corp.
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package generic
import (
F "github.com/IBM/fp-go/function"
)
// WithResource constructs a function that creates a resource, then operates on it and then releases the resource
func WithResource[
GA ~func() A,
GR ~func() R,
GANY ~func() ANY,
R, A, ANY any](onCreate GR, onRelease func(R) GANY) func(func(R) GA) GA {
// simply map to implementation of bracket
return F.Bind13of3(Bracket[GR, GA, GANY, R, A, ANY])(onCreate, F.Ignore2of2[A](onRelease))
}

27
io/resource.go Normal file
View File

@@ -0,0 +1,27 @@
// 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"
)
// WithResource constructs a function that creates a resource, then operates on it and then releases the resource
func WithResource[
R, A, ANY any](onCreate IO[R], onRelease func(R) IO[ANY]) func(func(R) IO[A]) IO[A] {
// just dispatch
return G.WithResource[IO[A], IO[R], IO[ANY]](onCreate, onRelease)
}

View File

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

View File

@@ -0,0 +1,86 @@
// Copyright (c) 2023 IBM Corp.
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package builder
import (
"net/http"
"net/url"
"testing"
E "github.com/IBM/fp-go/either"
F "github.com/IBM/fp-go/function"
C "github.com/IBM/fp-go/http/content"
H "github.com/IBM/fp-go/http/headers"
IO "github.com/IBM/fp-go/io"
IOE "github.com/IBM/fp-go/ioeither"
O "github.com/IBM/fp-go/option"
"github.com/stretchr/testify/assert"
)
func TestBuilder(t *testing.T) {
name := H.ContentType
withContentType := WithHeader(name)
withoutContentType := WithoutHeader(name)
b1 := F.Pipe1(
Default,
withContentType(C.Json),
)
b2 := F.Pipe1(
b1,
withContentType(C.TextPlain),
)
b3 := F.Pipe1(
b2,
withoutContentType,
)
assert.Equal(t, O.None[string](), Default.GetHeader(name))
assert.Equal(t, O.Of(C.Json), b1.GetHeader(name))
assert.Equal(t, O.Of(C.TextPlain), b2.GetHeader(name))
assert.Equal(t, O.None[string](), b3.GetHeader(name))
}
func TestBuilderWithQuery(t *testing.T) {
// add some query
withLimit := WithQueryArg("limit")("10")
withUrl := WithUrl("http://www.example.org?a=b")
b := F.Pipe2(
Default,
withLimit,
withUrl,
)
req := F.Pipe2(
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"))
})
}),
)
assert.True(t, E.IsRight(req()))
}

32
ioeither/http/di/di.go Normal file
View File

@@ -0,0 +1,32 @@
// Copyright (c) 2023 IBM Corp.
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package di
import (
"net/http"
DI "github.com/IBM/fp-go/di"
IOE "github.com/IBM/fp-go/ioeither"
IOEH "github.com/IBM/fp-go/ioeither/http"
)
var (
// InjHttpClient is the [DI.InjectionToken] for the [http.DefaultClient]
InjHttpClient = DI.MakeTokenWithDefault0("HTTP_CLIENT", IOE.Of[error](http.DefaultClient))
// InjClient is the [DI.InjectionToken] for the default [IOEH.Client]
InjClient = DI.MakeTokenWithDefault1("CLIENT", InjHttpClient.IOEither(), IOE.Map[error](IOEH.MakeClient))
)

View File

@@ -16,10 +16,12 @@
package http
import (
"bytes"
"io"
"net/http"
B "github.com/IBM/fp-go/bytes"
FL "github.com/IBM/fp-go/file"
F "github.com/IBM/fp-go/function"
H "github.com/IBM/fp-go/http"
IOE "github.com/IBM/fp-go/ioeither"
@@ -51,6 +53,24 @@ var (
MakeGetRequest = makeRequest("GET", nil)
)
// MakeBodyRequest creates a request that carries a body
func MakeBodyRequest(method string, body IOE.IOEither[error, []byte]) func(url string) IOE.IOEither[error, *http.Request] {
onBody := F.Pipe1(
body,
IOE.Map[error](F.Flow2(
bytes.NewReader,
FL.ToReader[*bytes.Reader],
)),
)
onRelease := IOE.Of[error, io.Reader]
withMethod := F.Bind1of3(MakeRequest)(method)
return F.Flow2(
F.Bind1of2(withMethod),
IOE.WithResource[*http.Request](onBody, onRelease),
)
}
func (client client) Do(req Requester) IOE.IOEither[error, *http.Response] {
return F.Pipe1(
req,

View File

@@ -20,6 +20,7 @@ import (
ET "github.com/IBM/fp-go/either"
F "github.com/IBM/fp-go/function"
C "github.com/IBM/fp-go/internal/chain"
FI "github.com/IBM/fp-go/internal/fromio"
"github.com/IBM/fp-go/internal/optiont"
IO "github.com/IBM/fp-go/io/generic"
@@ -76,8 +77,48 @@ func MonadChain[GA ~func() O.Option[A], GB ~func() O.Option[B], A, B any](fa GA,
return optiont.MonadChain(IO.MonadChain[GA, GB, O.Option[A], O.Option[B]], IO.MonadOf[GB, O.Option[B]], fa, f)
}
// MonadChainFirst runs the monad returned by the function but returns the result of the original monad
func MonadChainFirst[GA ~func() O.Option[A], GB ~func() O.Option[B], A, B any](ma GA, f func(A) GB) GA {
return C.MonadChainFirst(
MonadChain[GA, GA, A, A],
MonadMap[GB, GA, B, A],
ma,
f,
)
}
// ChainFirst runs the monad returned by the function but returns the result of the original monad
func ChainFirst[GA ~func() O.Option[A], GB ~func() O.Option[B], A, B any](f func(A) GB) func(GA) GA {
return C.ChainFirst(
MonadChain[GA, GA, A, A],
MonadMap[GB, GA, B, A],
f,
)
}
// MonadChainFirstIOK runs the monad returned by the function but returns the result of the original monad
func MonadChainFirstIOK[GA ~func() O.Option[A], GIOB ~func() B, A, B any](first GA, f func(A) GIOB) GA {
return FI.MonadChainFirstIOK(
MonadChain[GA, GA, A, A],
MonadMap[func() O.Option[B], GA, B, A],
FromIO[func() O.Option[B], GIOB, B],
first,
f,
)
}
// ChainFirstIOK runs the monad returned by the function but returns the result of the original monad
func ChainFirstIOK[GA ~func() O.Option[A], GIOB ~func() B, A, B any](f func(A) GIOB) func(GA) GA {
return FI.ChainFirstIOK(
MonadChain[GA, GA, A, A],
MonadMap[func() O.Option[B], GA, B, A],
FromIO[func() O.Option[B], GIOB, B],
f,
)
}
func Chain[GA ~func() O.Option[A], GB ~func() O.Option[B], A, B any](f func(A) GB) func(GA) GB {
return F.Bind2nd(MonadChain[GA, GB, A, B], f)
return optiont.Chain(IO.MonadChain[GA, GB, O.Option[A], O.Option[B]], IO.MonadOf[GB, O.Option[B]], f)
}
func MonadChainOptionK[GA ~func() O.Option[A], GB ~func() O.Option[B], A, B any](ma GA, f func(A) O.Option[B]) GB {

View File

@@ -0,0 +1,31 @@
// Copyright (c) 2023 IBM Corp.
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package generic
import (
F "github.com/IBM/fp-go/function"
O "github.com/IBM/fp-go/option"
)
// WithResource constructs a function that creates a resource, then operates on it and then releases the resource
func WithResource[
GA ~func() O.Option[A],
GR ~func() O.Option[R],
GANY ~func() O.Option[ANY],
R, A, ANY any](onCreate GR, onRelease func(R) GANY) func(func(R) GA) GA {
// simply map to implementation of bracket
return F.Bind13of3(Bracket[GR, GA, GANY, R, A, ANY])(onCreate, F.Ignore2of2[O.Option[A]](onRelease))
}

View File

@@ -18,6 +18,7 @@ package iooption
import (
ET "github.com/IBM/fp-go/either"
I "github.com/IBM/fp-go/io"
IO "github.com/IBM/fp-go/io"
G "github.com/IBM/fp-go/iooption/generic"
L "github.com/IBM/fp-go/lazy"
O "github.com/IBM/fp-go/option"
@@ -143,3 +144,23 @@ func MonadAlt[A any](first IOOption[A], second L.Lazy[IOOption[A]]) IOOption[A]
func Alt[A any](second L.Lazy[IOOption[A]]) func(IOOption[A]) IOOption[A] {
return G.Alt(second)
}
// MonadChainFirst runs the monad returned by the function but returns the result of the original monad
func MonadChainFirst[A, B any](ma IOOption[A], f func(A) IOOption[B]) IOOption[A] {
return G.MonadChainFirst[IOOption[A], IOOption[B]](ma, f)
}
// ChainFirst runs the monad returned by the function but returns the result of the original monad
func ChainFirst[A, B any](f func(A) IOOption[B]) func(IOOption[A]) IOOption[A] {
return G.ChainFirst[IOOption[A], IOOption[B]](f)
}
// MonadChainFirstIOK runs the monad returned by the function but returns the result of the original monad
func MonadChainFirstIOK[A, B any](first IOOption[A], f func(A) IO.IO[B]) IOOption[A] {
return G.MonadChainFirstIOK[IOOption[A], IO.IO[B]](first, f)
}
// ChainFirstIOK runs the monad returned by the function but returns the result of the original monad
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)
}

27
iooption/resource.go Normal file
View File

@@ -0,0 +1,27 @@
// 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"
)
// WithResource constructs a function that creates a resource, then operates on it and then releases the resource
func WithResource[
R, A, ANY any](onCreate IOOption[R], onRelease func(R) IOOption[ANY]) func(func(R) IOOption[A]) IOOption[A] {
// just dispatch
return G.WithResource[IOOption[A], IOOption[R], IOOption[ANY]](onCreate, onRelease)
}

View File

@@ -0,0 +1,34 @@
// 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"
L "github.com/IBM/fp-go/io/generic"
O "github.com/IBM/fp-go/option"
T "github.com/IBM/fp-go/tuple"
)
// FromLazy returns an iterator on top of a lazy function
func FromLazy[GU ~func() O.Option[T.Tuple2[GU, U]], LZ ~func() U, U any](l LZ) GU {
return F.Pipe1(
l,
L.Map[LZ, GU](F.Flow2(
F.Bind1st(T.MakeTuple2[GU, U], Empty[GU]()),
O.Of[T.Tuple2[GU, U]],
)),
)
}

32
iterator/stateless/io.go Normal file
View File

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

View 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 stateless
import (
"math/rand"
"testing"
"github.com/stretchr/testify/assert"
)
func TestIteratorFromLazy(t *testing.T) {
num := rand.Int
cit := FromLazy(num)
// create arrays twice
c1 := ToArray(cit)
c2 := ToArray(cit)
assert.Equal(t, 1, len(c1))
assert.Equal(t, 1, len(c2))
assert.NotEqual(t, c1, c2)
}

View File

@@ -19,13 +19,13 @@ import (
G "github.com/IBM/fp-go/iterator/stateless/generic"
)
// StrictUniq converts an [Iterator] or arbitrary items into an [Iterator] or unique items
// StrictUniq converts an [Iterator] of arbitrary items into an [Iterator] or unique items
// where uniqueness is determined by the built-in uniqueness constraint
func StrictUniq[A comparable](as Iterator[A]) Iterator[A] {
return G.StrictUniq[Iterator[A]](as)
}
// Uniq converts an [Iterator] or arbitrary items into an [Iterator] or unique items
// Uniq converts an [Iterator] of arbitrary items into an [Iterator] or unique items
// where uniqueness is determined based on a key extractor function
func Uniq[A any, K comparable](f func(A) K) func(as Iterator[A]) Iterator[A] {
return G.Uniq[Iterator[A], K](f)

View File

@@ -15,16 +15,12 @@
package lambda
type (
// RecFct is the function called recursively
RecFct[T, R any] func(T) R
internalCombinator[T, R any] func(internalCombinator[T, R]) RecFct[T, R]
)
// Y is the Y-combinator based on https://dreamsongs.com/Files/WhyOfY.pdf
func Y[TRFRM ~func(RecFct[T, R]) RecFct[T, R], T, R any](f TRFRM) RecFct[T, R] {
g := func(h internalCombinator[T, R]) RecFct[T, R] {
func Y[Endo ~func(RecFct) RecFct, RecFct ~func(T) R, T, R any](f Endo) RecFct {
type internal[RecFct ~func(T) R, T, R any] func(internal[RecFct, T, R]) RecFct
g := func(h internal[RecFct, T, R]) RecFct {
return func(t T) R {
return f(h(h))(t)
}

View File

@@ -16,12 +16,13 @@
package lambda
import (
"fmt"
"testing"
"github.com/stretchr/testify/assert"
)
func TestFactorial(t *testing.T) {
fct := Y(func(r RecFct[int, int]) RecFct[int, int] {
fct := Y(func(r func(int) int) func(int) int {
return func(n int) int {
if n <= 0 {
return 1
@@ -29,6 +30,5 @@ func TestFactorial(t *testing.T) {
return n * r(n-1)
}
})
fmt.Println(fct(10))
assert.Equal(t, 3628800, fct(10))
}

View File

@@ -17,6 +17,7 @@
package iso
import (
EM "github.com/IBM/fp-go/endomorphism"
F "github.com/IBM/fp-go/function"
)
@@ -52,7 +53,7 @@ func Reverse[S, A any](sa Iso[S, A]) Iso[A, S] {
)
}
func modify[S, A any](f func(A) A, sa Iso[S, A], s S) S {
func modify[FCT ~func(A) A, S, A any](f FCT, sa Iso[S, A], s S) S {
return F.Pipe3(
s,
sa.Get,
@@ -62,8 +63,8 @@ func modify[S, A any](f func(A) A, sa Iso[S, A], s S) S {
}
// Modify applies a transformation
func Modify[S, A any](f func(A) A) func(Iso[S, A]) func(S) S {
return F.Curry3(modify[S, A])(f)
func Modify[S any, FCT ~func(A) A, A any](f FCT) func(Iso[S, A]) EM.Endomorphism[S] {
return EM.Curry3(modify[FCT, S, A])(f)
}
// Wrap wraps the value

View File

@@ -16,6 +16,7 @@
package lens
import (
EM "github.com/IBM/fp-go/endomorphism"
F "github.com/IBM/fp-go/function"
I "github.com/IBM/fp-go/optics/iso"
L "github.com/IBM/fp-go/optics/lens"
@@ -23,10 +24,10 @@ import (
// IsoAsLens converts an `Iso` to a `Lens`
func IsoAsLens[S, A any](sa I.Iso[S, A]) L.Lens[S, A] {
return L.MakeLensCurried(sa.Get, F.Flow2(sa.ReverseGet, F.Constant1[S, S]))
return L.MakeLensCurried(sa.Get, F.Flow2(sa.ReverseGet, F.Flow2(F.Constant1[S, S], EM.Of[func(S) S])))
}
// IsoAsLensRef converts an `Iso` to a `Lens`
func IsoAsLensRef[S, A any](sa I.Iso[*S, A]) L.Lens[*S, A] {
return L.MakeLensRefCurried(sa.Get, F.Flow2(sa.ReverseGet, F.Constant1[*S, *S]))
return L.MakeLensRefCurried(sa.Get, F.Flow2(sa.ReverseGet, F.Flow2(F.Constant1[*S, *S], EM.Of[func(*S) *S])))
}

View File

@@ -0,0 +1,39 @@
// 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 (
AA "github.com/IBM/fp-go/array/generic"
L "github.com/IBM/fp-go/optics/lens"
O "github.com/IBM/fp-go/option"
)
// AtHead focusses on the head of an array. The setter works as follows
// - if the new value is none, the result will be an empty array
// - if the new value is some and the array is empty, it creates a new array with one element
// - if the new value is some and the array is not empty, it replaces the head
func AtHead[AS []A, A any]() L.Lens[AS, O.Option[A]] {
return L.MakeLens(AA.Head[AS, A], func(as AS, a O.Option[A]) AS {
return O.MonadFold(a, AA.Empty[AS], func(v A) AS {
if AA.IsEmpty(as) {
return AA.Of[AS, A](v)
}
cpy := AA.Copy(as)
cpy[0] = v
return cpy
})
})
}

30
optics/lens/array/head.go Normal file
View File

@@ -0,0 +1,30 @@
// Copyright (c) 2023 IBM Corp.
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package array
import (
L "github.com/IBM/fp-go/optics/lens"
G "github.com/IBM/fp-go/optics/lens/array/generic"
O "github.com/IBM/fp-go/option"
)
// AtHead focusses on the head of an array. The setter works as follows
// - if the new value is none, the result will be an empty array
// - if the new value is some and the array is empty, it creates a new array with one element
// - if the new value is some and the array is not empty, it replaces the head
func AtHead[A any]() L.Lens[[]A, O.Option[A]] {
return G.AtHead[[]A]()
}

View File

@@ -0,0 +1,40 @@
// 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"
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"
S "github.com/IBM/fp-go/string"
"github.com/stretchr/testify/assert"
)
var (
sEq = eq.FromEquals(S.Eq)
)
func TestLaws(t *testing.T) {
headLaws := LT.AssertLaws(t, O.Eq(sEq), A.Eq(sEq))(AtHead[string]())
assert.True(t, headLaws(A.Empty[string](), O.None[string]()))
assert.True(t, headLaws(A.Empty[string](), O.Of("a")))
assert.True(t, headLaws(A.From("a", "b"), O.None[string]()))
assert.True(t, headLaws(A.From("a", "b"), O.Of("c")))
}

View File

@@ -17,8 +17,8 @@
package lens
import (
EM "github.com/IBM/fp-go/endomorphism"
F "github.com/IBM/fp-go/function"
I "github.com/IBM/fp-go/identity"
O "github.com/IBM/fp-go/option"
)
@@ -26,13 +26,13 @@ type (
// Lens is a reference to a subpart of a data type
Lens[S, A any] struct {
Get func(s S) A
Set func(a A) func(S) S
Set func(a A) EM.Endomorphism[S]
}
)
// setCopy wraps a setter for a pointer into a setter that first creates a copy before
// modifying that copy
func setCopy[S, A any](setter func(*S, A) *S) func(s *S, a A) *S {
func setCopy[SET ~func(*S, A) *S, S, A any](setter SET) func(s *S, a A) *S {
return func(s *S, a A) *S {
copy := *s
return setter(&copy, a)
@@ -41,8 +41,8 @@ func setCopy[S, A any](setter func(*S, A) *S) func(s *S, a A) *S {
// setCopyCurried wraps a setter for a pointer into a setter that first creates a copy before
// modifying that copy
func setCopyCurried[S, A any](setter func(A) func(*S) *S) func(a A) func(*S) *S {
return func(a A) func(*S) *S {
func setCopyCurried[SET ~func(A) EM.Endomorphism[*S], S, A any](setter SET) func(a A) EM.Endomorphism[*S] {
return func(a A) EM.Endomorphism[*S] {
seta := setter(a)
return func(s *S) *S {
copy := *s
@@ -51,53 +51,53 @@ func setCopyCurried[S, A any](setter func(A) func(*S) *S) func(a A) func(*S) *S
}
}
// MakeLens creates a lens based on a getter and a setter function. Make sure that the setter creates a (shallow) copy of the
// MakeLens creates a [Lens] based on a getter and a setter function. Make sure that the setter creates a (shallow) copy of the
// data. This happens automatically if the data is passed by value. For pointers consider to use `MakeLensRef`
// and for other kinds of data structures that are copied by reference make sure the setter creates the copy.
func MakeLens[S, A any](get func(S) A, set func(S, A) S) Lens[S, A] {
return MakeLensCurried(get, F.Curry2(F.Swap(set)))
func MakeLens[GET ~func(S) A, SET ~func(S, A) S, S, A any](get GET, set SET) Lens[S, A] {
return MakeLensCurried(get, EM.Curry2(F.Swap(set)))
}
// MakeLensCurried creates a lens based on a getter and a setter function. Make sure that the setter creates a (shallow) copy of the
// MakeLensCurried creates a [Lens] based on a getter and a setter function. Make sure that the setter creates a (shallow) copy of the
// data. This happens automatically if the data is passed by value. For pointers consider to use `MakeLensRef`
// and for other kinds of data structures that are copied by reference make sure the setter creates the copy.
func MakeLensCurried[S, A any](get func(S) A, set func(A) func(S) S) Lens[S, A] {
func MakeLensCurried[GET ~func(S) A, SET ~func(A) EM.Endomorphism[S], S, A any](get GET, set SET) Lens[S, A] {
return Lens[S, A]{Get: get, Set: set}
}
// MakeLensRef creates a lens based on a getter and a setter function. The setter passed in does not have to create a shallow
// MakeLensRef creates a [Lens] based on a getter and a setter function. The setter passed in does not have to create a shallow
// copy, the implementation wraps the setter into one that copies the pointer before modifying it
//
// Such a lens assumes that property A of S always exists
func MakeLensRef[S, A any](get func(*S) A, set func(*S, A) *S) Lens[*S, A] {
// Such a [Lens] assumes that property A of S always exists
func MakeLensRef[GET ~func(*S) A, SET func(*S, A) *S, S, A any](get GET, set SET) Lens[*S, A] {
return MakeLens(get, setCopy(set))
}
// MakeLensRefCurried creates a lens based on a getter and a setter function. The setter passed in does not have to create a shallow
// MakeLensRefCurried creates a [Lens] based on a getter and a setter function. The setter passed in does not have to create a shallow
// copy, the implementation wraps the setter into one that copies the pointer before modifying it
//
// Such a lens assumes that property A of S always exists
func MakeLensRefCurried[S, A any](get func(*S) A, set func(A) func(*S) *S) Lens[*S, A] {
// Such a [Lens] assumes that property A of S always exists
func MakeLensRefCurried[S, A any](get func(*S) A, set func(A) EM.Endomorphism[*S]) Lens[*S, A] {
return MakeLensCurried(get, setCopyCurried(set))
}
// Id returns a lens implementing the identity operation
func id[S any](creator func(get func(S) S, set func(S, S) S) Lens[S, S]) Lens[S, S] {
// id returns a [Lens] implementing the identity operation
func id[GET ~func(S) S, SET ~func(S, S) S, S any](creator func(get GET, set SET) Lens[S, S]) Lens[S, S] {
return creator(F.Identity[S], F.Second[S, S])
}
// Id returns a lens implementing the identity operation
// Id returns a [Lens] implementing the identity operation
func Id[S any]() Lens[S, S] {
return id(MakeLens[S, S])
return id(MakeLens[EM.Endomorphism[S], func(S, S) S])
}
// IdRef returns a lens implementing the identity operation
// IdRef returns a [Lens] implementing the identity operation
func IdRef[S any]() Lens[*S, *S] {
return id(MakeLensRef[S, *S])
return id(MakeLensRef[EM.Endomorphism[*S], func(*S, *S) *S])
}
// Compose combines two lenses and allows to narrow down the focus to a sub-lens
func compose[S, A, B any](creator func(get func(S) B, set func(S, B) S) Lens[S, B], ab Lens[A, B]) func(Lens[S, A]) Lens[S, B] {
func compose[GET ~func(S) B, SET ~func(S, B) S, S, A, B any](creator func(get GET, set SET) Lens[S, B], ab Lens[A, B]) func(Lens[S, A]) Lens[S, B] {
abget := ab.Get
abset := ab.Set
return func(sa Lens[S, A]) Lens[S, B] {
@@ -114,7 +114,7 @@ func compose[S, A, B any](creator func(get func(S) B, set func(S, B) S) Lens[S,
// Compose combines two lenses and allows to narrow down the focus to a sub-lens
func Compose[S, A, B any](ab Lens[A, B]) func(Lens[S, A]) Lens[S, B] {
return compose(MakeLens[S, B], ab)
return compose(MakeLens[func(S) B, func(S, B) S], ab)
}
// ComposeOption combines a `Lens` that returns an optional value with a `Lens` that returns a definite value
@@ -141,7 +141,7 @@ func ComposeOption[S, B, A any](defaultA A) func(ab Lens[A, B]) func(Lens[S, O.O
func(s S, ob O.Option[B]) S {
return F.Pipe2(
ob,
O.Fold(unseta, func(b B) func(S) S {
O.Fold(unseta, func(b B) EM.Endomorphism[S] {
setbona := F.Flow2(
ab.Set(b),
seta,
@@ -158,7 +158,7 @@ func ComposeOption[S, B, A any](defaultA A) func(ab Lens[A, B]) func(Lens[S, O.O
),
)
}),
I.Ap[S, S](s),
EM.Ap(s),
)
},
)
@@ -174,7 +174,7 @@ func ComposeOption[S, B, A any](defaultA A) func(ab Lens[A, B]) func(Lens[S, O.O
// if the setter is called with `None[B]` and `A` does exist, 'B' is removed from 'A'
func ComposeOptions[S, B, A any](defaultA A) func(ab Lens[A, O.Option[B]]) func(Lens[S, O.Option[A]]) Lens[S, O.Option[B]] {
defa := F.Constant(defaultA)
noops := F.Constant(F.Identity[S])
noops := EM.Identity[S]
noneb := O.None[B]()
return func(ab Lens[A, O.Option[B]]) func(Lens[S, O.Option[A]]) Lens[S, O.Option[B]] {
unsetb := ab.Set(noneb)
@@ -189,15 +189,15 @@ func ComposeOptions[S, B, A any](defaultA A) func(ab Lens[A, O.Option[B]]) func(
sa.Get,
O.Chain(ab.Get),
),
func(b O.Option[B]) func(S) S {
func(b O.Option[B]) EM.Endomorphism[S] {
return func(s S) S {
return O.MonadFold(b, func() func(S) S {
return O.MonadFold(b, func() EM.Endomorphism[S] {
return F.Pipe2(
s,
sa.Get,
O.Fold(noops, F.Flow2(unsetb, seta)),
)
}, func(b B) func(S) S {
}, func(b B) EM.Endomorphism[S] {
// sets a B onto an A
setb := F.Flow2(
ab.Set(O.Some(b)),
@@ -218,20 +218,20 @@ func ComposeOptions[S, B, A any](defaultA A) func(ab Lens[A, O.Option[B]]) func(
// Compose combines two lenses and allows to narrow down the focus to a sub-lens
func ComposeRef[S, A, B any](ab Lens[A, B]) func(Lens[*S, A]) Lens[*S, B] {
return compose(MakeLensRef[S, B], ab)
return compose(MakeLensRef[func(*S) B, func(*S, B) *S], ab)
}
func modify[S, A any](f func(A) A, sa Lens[S, A], s S) S {
func modify[FCT ~func(A) A, S, A any](f FCT, sa Lens[S, A], s S) S {
return sa.Set(f(sa.Get(s)))(s)
}
// Modify changes a property of a lens by invoking a transformation function
// Modify changes a property of a [Lens] by invoking a transformation function
// if the transformed property has not changes, the method returns the original state
func Modify[S, A any](f func(A) A) func(Lens[S, A]) func(S) S {
return F.Curry3(modify[S, A])(f)
func Modify[S any, FCT ~func(A) A, A any](f FCT) func(Lens[S, A]) EM.Endomorphism[S] {
return EM.Curry3(modify[FCT, S, A])(f)
}
func IMap[E, A, B any](ab func(A) B, ba func(B) A) func(Lens[E, A]) Lens[E, B] {
func IMap[E any, AB ~func(A) B, BA ~func(B) A, A, B any](ab AB, ba BA) func(Lens[E, A]) Lens[E, B] {
return func(ea Lens[E, A]) Lens[E, B] {
return Lens[E, B]{Get: F.Flow2(ea.Get, ab), Set: F.Flow2(ba, ea.Set)}
}
@@ -239,7 +239,7 @@ func IMap[E, A, B any](ab func(A) B, ba func(B) A) func(Lens[E, A]) Lens[E, B] {
// fromPredicate returns a `Lens` for a property accessibly as a getter and setter that can be optional
// if the optional value is set then the nil value will be set instead
func fromPredicate[S, A any](creator func(get func(S) O.Option[A], set func(S, O.Option[A]) S) Lens[S, O.Option[A]], pred func(A) bool, nilValue A) func(sa Lens[S, A]) Lens[S, O.Option[A]] {
func fromPredicate[GET ~func(S) O.Option[A], SET ~func(S, O.Option[A]) S, S, A any](creator func(get GET, set SET) Lens[S, O.Option[A]], pred func(A) bool, nilValue A) func(sa Lens[S, A]) Lens[S, O.Option[A]] {
fromPred := O.FromPredicate(pred)
return func(sa Lens[S, A]) Lens[S, O.Option[A]] {
fold := O.Fold(F.Bind1of1(sa.Set)(nilValue), sa.Set)
@@ -247,7 +247,7 @@ func fromPredicate[S, A any](creator func(get func(S) O.Option[A], set func(S, O
return F.Pipe2(
a,
fold,
I.Ap[S, S](s),
EM.Ap(s),
)
})
}
@@ -256,13 +256,13 @@ func fromPredicate[S, A any](creator func(get func(S) O.Option[A], set func(S, O
// FromPredicate returns a `Lens` for a property accessibly as a getter and setter that can be optional
// if the optional value is set then the nil value will be set instead
func FromPredicate[S, A any](pred func(A) bool, nilValue A) func(sa Lens[S, A]) Lens[S, O.Option[A]] {
return fromPredicate(MakeLens[S, O.Option[A]], pred, nilValue)
return fromPredicate(MakeLens[func(S) O.Option[A], func(S, O.Option[A]) S], pred, nilValue)
}
// FromPredicateRef returns a `Lens` for a property accessibly as a getter and setter that can be optional
// if the optional value is set then the nil value will be set instead
func FromPredicateRef[S, A any](pred func(A) bool, nilValue A) func(sa Lens[*S, A]) Lens[*S, O.Option[A]] {
return fromPredicate(MakeLensRef[S, O.Option[A]], pred, nilValue)
return fromPredicate(MakeLensRef[func(*S) O.Option[A], func(*S, O.Option[A]) *S], pred, nilValue)
}
// FromPredicate returns a `Lens` for a property accessibly as a getter and setter that can be optional
@@ -278,7 +278,7 @@ func FromNillableRef[S, A any](sa Lens[*S, *A]) Lens[*S, O.Option[*A]] {
}
// fromNullableProp returns a `Lens` from a property that may be optional. The getter returns a default value for these items
func fromNullableProp[S, A any](creator func(get func(S) A, set func(S, A) S) Lens[S, A], isNullable func(A) O.Option[A], defaultValue A) func(sa Lens[S, A]) Lens[S, A] {
func fromNullableProp[GET ~func(S) A, SET ~func(S, A) S, S, A any](creator func(get GET, set SET) Lens[S, A], isNullable func(A) O.Option[A], defaultValue A) func(sa Lens[S, A]) Lens[S, A] {
return func(sa Lens[S, A]) Lens[S, A] {
return creator(F.Flow3(
sa.Get,
@@ -293,16 +293,16 @@ func fromNullableProp[S, A any](creator func(get func(S) A, set func(S, A) S) Le
// FromNullableProp returns a `Lens` from a property that may be optional. The getter returns a default value for these items
func FromNullableProp[S, A any](isNullable func(A) O.Option[A], defaultValue A) func(sa Lens[S, A]) Lens[S, A] {
return fromNullableProp(MakeLens[S, A], isNullable, defaultValue)
return fromNullableProp(MakeLens[func(S) A, func(S, A) S], isNullable, defaultValue)
}
// FromNullablePropRef returns a `Lens` from a property that may be optional. The getter returns a default value for these items
func FromNullablePropRef[S, A any](isNullable func(A) O.Option[A], defaultValue A) func(sa Lens[*S, A]) Lens[*S, A] {
return fromNullableProp(MakeLensRef[S, A], isNullable, defaultValue)
return fromNullableProp(MakeLensRef[func(*S) A, func(*S, A) *S], isNullable, defaultValue)
}
// fromFromOption returns a `Lens` from an option property. The getter returns a default value the setter will always set the some option
func fromOption[S, A any](creator func(get func(S) A, set func(S, A) S) Lens[S, A], defaultValue A) func(sa Lens[S, O.Option[A]]) Lens[S, A] {
func fromOption[GET ~func(S) A, SET ~func(S, A) S, S, A any](creator func(get GET, set SET) Lens[S, A], defaultValue A) func(sa Lens[S, O.Option[A]]) Lens[S, A] {
return func(sa Lens[S, O.Option[A]]) Lens[S, A] {
return creator(F.Flow2(
sa.Get,
@@ -316,10 +316,10 @@ func fromOption[S, A any](creator func(get func(S) A, set func(S, A) S) Lens[S,
// FromFromOption returns a `Lens` from an option property. The getter returns a default value the setter will always set the some option
func FromOption[S, A any](defaultValue A) func(sa Lens[S, O.Option[A]]) Lens[S, A] {
return fromOption(MakeLens[S, A], defaultValue)
return fromOption(MakeLens[func(S) A, func(S, A) S], defaultValue)
}
// FromFromOptionRef returns a `Lens` from an option property. The getter returns a default value the setter will always set the some option
func FromOptionRef[S, A any](defaultValue A) func(sa Lens[*S, O.Option[A]]) Lens[*S, A] {
return fromOption(MakeLensRef[S, A], defaultValue)
return fromOption(MakeLensRef[func(*S) A, func(*S, A) *S], defaultValue)
}

View File

@@ -18,6 +18,7 @@
package optional
import (
EM "github.com/IBM/fp-go/endomorphism"
F "github.com/IBM/fp-go/function"
O "github.com/IBM/fp-go/option"
)
@@ -25,12 +26,12 @@ import (
// Optional is an optional reference to a subpart of a data type
type Optional[S, A any] struct {
GetOption func(s S) O.Option[A]
Set func(a A) func(S) S
Set func(a A) EM.Endomorphism[S]
}
// setCopy wraps a setter for a pointer into a setter that first creates a copy before
// modifying that copy
func setCopy[S, A any](setter func(*S, A) *S) func(s *S, a A) *S {
func setCopy[SET ~func(*S, A) *S, S, A any](setter SET) func(s *S, a A) *S {
return func(s *S, a A) *S {
copy := *s
return setter(&copy, a)
@@ -41,7 +42,7 @@ func setCopy[S, A any](setter func(*S, A) *S) func(s *S, a A) *S {
// data. This happens automatically if the data is passed by value. For pointers consider to use `MakeOptionalRef`
// and for other kinds of data structures that are copied by reference make sure the setter creates the copy.
func MakeOptional[S, A any](get func(S) O.Option[A], set func(S, A) S) Optional[S, A] {
return Optional[S, A]{GetOption: get, Set: F.Curry2(F.Swap(set))}
return Optional[S, A]{GetOption: get, Set: EM.Curry2(F.Swap(set))}
}
// MakeOptionalRef creates an Optional based on a getter and a setter function. The setter passed in does not have to create a shallow
@@ -168,7 +169,7 @@ func ichain[S, A, B any](sa Optional[S, A], ab func(A) O.Option[B], ba func(B) O
return MakeOptional(
F.Flow2(sa.GetOption, O.Chain(ab)),
func(s S, b B) S {
return O.MonadFold(ba(b), F.Constant(F.Identity[S]), sa.Set)(s)
return O.MonadFold(ba(b), EM.Identity[S], sa.Set)(s)
},
)
}

View File

@@ -17,6 +17,7 @@
package prism
import (
EM "github.com/IBM/fp-go/endomorphism"
F "github.com/IBM/fp-go/function"
O "github.com/IBM/fp-go/option"
)
@@ -86,12 +87,12 @@ func prismModify[S, A any](f func(A) A, sa Prism[S, A], s S) S {
)
}
func prismSet[S, A any](a A) func(Prism[S, A]) func(S) S {
return F.Curry3(prismModify[S, A])(F.Constant1[A](a))
func prismSet[S, A any](a A) func(Prism[S, A]) EM.Endomorphism[S] {
return EM.Curry3(prismModify[S, A])(F.Constant1[A](a))
}
func Set[S, A any](a A) func(Prism[S, A]) func(S) S {
return F.Curry3(prismModify[S, A])(F.Constant1[A](a))
func Set[S, A any](a A) func(Prism[S, A]) EM.Endomorphism[S] {
return EM.Curry3(prismModify[S, A])(F.Constant1[A](a))
}
func prismSome[A any]() Prism[O.Option[A], A] {
@@ -103,14 +104,14 @@ func Some[S, A any](soa Prism[S, O.Option[A]]) Prism[S, A] {
return Compose[S](prismSome[A]())(soa)
}
func imap[S, A, B any](sa Prism[S, A], ab func(A) B, ba func(B) A) Prism[S, B] {
func imap[S any, AB ~func(A) B, BA ~func(B) A, A, B any](sa Prism[S, A], ab AB, ba BA) Prism[S, B] {
return MakePrism(
F.Flow2(sa.GetOption, O.Map(ab)),
F.Flow2(ba, sa.ReverseGet),
)
}
func IMap[S, A, B any](ab func(A) B, ba func(B) A) func(Prism[S, A]) Prism[S, B] {
func IMap[S any, AB ~func(A) B, BA ~func(B) A, A, B any](ab AB, ba BA) func(Prism[S, A]) Prism[S, B] {
return func(sa Prism[S, A]) Prism[S, B] {
return imap(sa, ab, ba)
}

View File

@@ -531,3 +531,12 @@ func MonadFlap[GFAB ~map[K]func(A) B, GB ~map[K]B, K comparable, A, B any](fab G
func Flap[GFAB ~map[K]func(A) B, GB ~map[K]B, K comparable, A, B any](a A) func(GFAB) GB {
return FC.Flap(MonadMap[GFAB, GB], a)
}
func Copy[M ~map[K]V, K comparable, V any](m M) M {
return duplicate(m)
}
func Clone[M ~map[K]V, K comparable, V any](f func(V) V) func(m M) M {
// impementation assumes that map does not optimize for the empty map
return Map[M, M](f)
}

View File

@@ -284,3 +284,13 @@ func MonadFlap[B any, K comparable, A any](fab map[K]func(A) B, a A) map[K]B {
func Flap[B any, K comparable, A any](a A) func(map[K]func(A) B) map[K]B {
return G.Flap[map[K]func(A) B, map[K]B](a)
}
// Copy creates a shallow copy of the map
func Copy[K comparable, V any](m map[K]V) map[K]V {
return G.Copy[map[K]V](m)
}
// Clone creates a deep copy of the map using the provided endomorphism to clone the values
func Clone[K comparable, V any](f func(V) V) func(m map[K]V) map[K]V {
return G.Clone[map[K]V](f)
}

View File

@@ -21,6 +21,7 @@ import (
"strings"
"testing"
A "github.com/IBM/fp-go/array"
"github.com/IBM/fp-go/internal/utils"
O "github.com/IBM/fp-go/option"
S "github.com/IBM/fp-go/string"
@@ -131,3 +132,20 @@ func ExampleValuesOrd() {
// Output: [c b a]
}
func TestCopyVsClone(t *testing.T) {
slc := []string{"b", "c"}
src := map[string][]string{
"a": slc,
}
// make a shallow copy
cpy := Copy(src)
// make a deep copy
cln := Clone[string](A.Copy[string])(src)
assert.Equal(t, cpy, cln)
// make a modification to the original slice
slc[0] = "d"
assert.NotEqual(t, cpy, cln)
assert.Equal(t, src, cpy)
}