mirror of
https://github.com/IBM/fp-go.git
synced 2025-08-30 19:52:00 +02:00
Compare commits
21 Commits
cleue-fix-
...
v1.0.77
Author | SHA1 | Date | |
---|---|---|---|
|
1d02f21ff5 | ||
|
e82575fe08 | ||
|
5fcd0b1595 | ||
|
5caabf478c | ||
|
b7ec18c83e | ||
|
96686425fb | ||
|
1f675e08fa | ||
|
4d2f410c98 | ||
|
8f49c1328c | ||
|
2a1d5821db | ||
|
6abbdc5ee1 | ||
|
5fea9858a9 | ||
|
e6426c90c0 | ||
|
54ce59105e | ||
|
8bb006c741 | ||
|
b6efa35b03 | ||
|
35848900c0 | ||
|
3d54f99739 | ||
|
ffd9418cac | ||
|
acfcea59f4 | ||
|
b4bf511f03 |
4
.github/workflows/build.yml
vendored
4
.github/workflows/build.yml
vendored
@@ -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 }}
|
||||
-
|
||||
@@ -65,7 +65,7 @@ jobs:
|
||||
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}}
|
||||
|
||||
|
@@ -308,10 +308,16 @@ func SliceRight[A any](start int) func([]A) []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)
|
||||
|
@@ -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 {
|
||||
|
32
array/generic/uniq.go
Normal file
32
array/generic/uniq.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package generic
|
||||
|
||||
import F "github.com/IBM/fp-go/function"
|
||||
|
||||
// StrictUniq converts an array of arbitrary items into an array or unique items
|
||||
// where uniqueness is determined by the built-in uniqueness constraint
|
||||
func StrictUniq[AS ~[]A, A comparable](as AS) AS {
|
||||
return Uniq[AS](F.Identity[A])(as)
|
||||
}
|
||||
|
||||
// uniquePredUnsafe returns a predicate on a map for uniqueness
|
||||
func uniquePredUnsafe[PRED ~func(A) K, A any, K comparable](f PRED) func(int, A) bool {
|
||||
lookup := make(map[K]bool)
|
||||
return func(_ int, a A) bool {
|
||||
k := f(a)
|
||||
_, has := lookup[k]
|
||||
if has {
|
||||
return false
|
||||
}
|
||||
lookup[k] = true
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// Uniq converts an array of arbitrary items into an array or unique items
|
||||
// where uniqueness is determined based on a key extractor function
|
||||
func Uniq[AS ~[]A, PRED ~func(A) K, A any, K comparable](f PRED) func(as AS) AS {
|
||||
return func(as AS) AS {
|
||||
// we need to create a new predicate for each iteration
|
||||
return filterWithIndex(as, uniquePredUnsafe(f))
|
||||
}
|
||||
}
|
17
array/uniq.go
Normal file
17
array/uniq.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package array
|
||||
|
||||
import (
|
||||
G "github.com/IBM/fp-go/array/generic"
|
||||
)
|
||||
|
||||
// StrictUniq converts an array of arbitrary items into an array or unique items
|
||||
// where uniqueness is determined by the built-in uniqueness constraint
|
||||
func StrictUniq[A comparable](as []A) []A {
|
||||
return G.StrictUniq[[]A](as)
|
||||
}
|
||||
|
||||
// Uniq converts an array of arbitrary items into an array or unique items
|
||||
// where uniqueness is determined based on a key extractor function
|
||||
func Uniq[A any, K comparable](f func(A) K) func(as []A) []A {
|
||||
return G.Uniq[[]A](f)
|
||||
}
|
14
array/uniq_test.go
Normal file
14
array/uniq_test.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package array
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestUniq(t *testing.T) {
|
||||
data := From(1, 2, 3, 2, 4, 1)
|
||||
|
||||
uniq := StrictUniq(data)
|
||||
assert.Equal(t, From(1, 2, 3, 4), uniq)
|
||||
}
|
@@ -166,3 +166,289 @@ func SequenceRecord[K comparable,
|
||||
|
||||
return MonadTraverseRecord[K, GAS, GRAS](ma, F.Identity[GRA])
|
||||
}
|
||||
|
||||
// MonadTraverseArraySeq transforms an array
|
||||
func MonadTraverseArraySeq[
|
||||
AS ~[]A,
|
||||
GRBS ~func(context.Context) GIOBS,
|
||||
GRB ~func(context.Context) GIOB,
|
||||
GIOBS ~func() E.Either[error, BS],
|
||||
GIOB ~func() E.Either[error, B],
|
||||
BS ~[]B,
|
||||
A, B any](as AS, f func(A) GRB) GRBS {
|
||||
|
||||
return RA.MonadTraverse[AS](
|
||||
Of[GRBS, GIOBS, BS],
|
||||
Map[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GIOBS, func() E.Either[error, func(B) BS], BS, func(B) BS],
|
||||
ApSeq[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GRB],
|
||||
|
||||
as, f,
|
||||
)
|
||||
}
|
||||
|
||||
// TraverseArraySeq transforms an array
|
||||
func TraverseArraySeq[
|
||||
AS ~[]A,
|
||||
GRBS ~func(context.Context) GIOBS,
|
||||
GRB ~func(context.Context) GIOB,
|
||||
GIOBS ~func() E.Either[error, BS],
|
||||
GIOB ~func() E.Either[error, B],
|
||||
BS ~[]B,
|
||||
A, B any](f func(A) GRB) func(AS) GRBS {
|
||||
|
||||
return RA.Traverse[AS](
|
||||
Of[GRBS, GIOBS, BS],
|
||||
Map[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GIOBS, func() E.Either[error, func(B) BS], BS, func(B) BS],
|
||||
ApSeq[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GRB],
|
||||
|
||||
f,
|
||||
)
|
||||
}
|
||||
|
||||
// TraverseArrayWithIndexSeq transforms an array
|
||||
func TraverseArrayWithIndexSeq[
|
||||
AS ~[]A,
|
||||
GRBS ~func(context.Context) GIOBS,
|
||||
GRB ~func(context.Context) GIOB,
|
||||
GIOBS ~func() E.Either[error, BS],
|
||||
GIOB ~func() E.Either[error, B],
|
||||
BS ~[]B,
|
||||
A, B any](f func(int, A) GRB) func(AS) GRBS {
|
||||
|
||||
return RA.TraverseWithIndex[AS](
|
||||
Of[GRBS, GIOBS, BS],
|
||||
Map[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GIOBS, func() E.Either[error, func(B) BS], BS, func(B) BS],
|
||||
ApSeq[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GRB],
|
||||
|
||||
f,
|
||||
)
|
||||
}
|
||||
|
||||
// SequenceArraySeq converts a homogeneous sequence of either into an either of sequence
|
||||
func SequenceArraySeq[
|
||||
AS ~[]A,
|
||||
GAS ~[]GRA,
|
||||
GRAS ~func(context.Context) GIOAS,
|
||||
GRA ~func(context.Context) GIOA,
|
||||
GIOAS ~func() E.Either[error, AS],
|
||||
GIOA ~func() E.Either[error, A],
|
||||
A any](ma GAS) GRAS {
|
||||
|
||||
return MonadTraverseArraySeq[GAS, GRAS](ma, F.Identity[GRA])
|
||||
}
|
||||
|
||||
// MonadTraverseRecordSeq transforms a record
|
||||
func MonadTraverseRecordSeq[K comparable,
|
||||
AS ~map[K]A,
|
||||
GRBS ~func(context.Context) GIOBS,
|
||||
GRB ~func(context.Context) GIOB,
|
||||
GIOBS ~func() E.Either[error, BS],
|
||||
GIOB ~func() E.Either[error, B],
|
||||
BS ~map[K]B,
|
||||
|
||||
A, B any](ma AS, f func(A) GRB) GRBS {
|
||||
|
||||
return RR.MonadTraverse[AS](
|
||||
Of[GRBS, GIOBS, BS],
|
||||
Map[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GIOBS, func() E.Either[error, func(B) BS], BS, func(B) BS],
|
||||
ApSeq[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GRB],
|
||||
|
||||
ma, f,
|
||||
)
|
||||
}
|
||||
|
||||
// TraverseRecordSeq transforms a record
|
||||
func TraverseRecordSeq[K comparable,
|
||||
AS ~map[K]A,
|
||||
GRBS ~func(context.Context) GIOBS,
|
||||
GRB ~func(context.Context) GIOB,
|
||||
GIOBS ~func() E.Either[error, BS],
|
||||
GIOB ~func() E.Either[error, B],
|
||||
BS ~map[K]B,
|
||||
|
||||
A, B any](f func(A) GRB) func(AS) GRBS {
|
||||
|
||||
return RR.Traverse[AS](
|
||||
Of[GRBS, GIOBS, BS],
|
||||
Map[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GIOBS, func() E.Either[error, func(B) BS], BS, func(B) BS],
|
||||
ApSeq[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GRB],
|
||||
|
||||
f,
|
||||
)
|
||||
}
|
||||
|
||||
// TraverseRecordWithIndexSeq transforms a record
|
||||
func TraverseRecordWithIndexSeq[K comparable,
|
||||
AS ~map[K]A,
|
||||
GRBS ~func(context.Context) GIOBS,
|
||||
GRB ~func(context.Context) GIOB,
|
||||
GIOBS ~func() E.Either[error, BS],
|
||||
GIOB ~func() E.Either[error, B],
|
||||
BS ~map[K]B,
|
||||
|
||||
A, B any](f func(K, A) GRB) func(AS) GRBS {
|
||||
|
||||
return RR.TraverseWithIndex[AS](
|
||||
Of[GRBS, GIOBS, BS],
|
||||
Map[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GIOBS, func() E.Either[error, func(B) BS], BS, func(B) BS],
|
||||
ApSeq[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GRB],
|
||||
|
||||
f,
|
||||
)
|
||||
}
|
||||
|
||||
// SequenceRecordSeq converts a homogeneous sequence of either into an either of sequence
|
||||
func SequenceRecordSeq[K comparable,
|
||||
AS ~map[K]A,
|
||||
GAS ~map[K]GRA,
|
||||
GRAS ~func(context.Context) GIOAS,
|
||||
GRA ~func(context.Context) GIOA,
|
||||
GIOAS ~func() E.Either[error, AS],
|
||||
GIOA ~func() E.Either[error, A],
|
||||
A any](ma GAS) GRAS {
|
||||
|
||||
return MonadTraverseRecordSeq[K, GAS, GRAS](ma, F.Identity[GRA])
|
||||
}
|
||||
|
||||
// MonadTraverseArrayPar transforms an array
|
||||
func MonadTraverseArrayPar[
|
||||
AS ~[]A,
|
||||
GRBS ~func(context.Context) GIOBS,
|
||||
GRB ~func(context.Context) GIOB,
|
||||
GIOBS ~func() E.Either[error, BS],
|
||||
GIOB ~func() E.Either[error, B],
|
||||
BS ~[]B,
|
||||
A, B any](as AS, f func(A) GRB) GRBS {
|
||||
|
||||
return RA.MonadTraverse[AS](
|
||||
Of[GRBS, GIOBS, BS],
|
||||
Map[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GIOBS, func() E.Either[error, func(B) BS], BS, func(B) BS],
|
||||
ApPar[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GRB],
|
||||
|
||||
as, f,
|
||||
)
|
||||
}
|
||||
|
||||
// TraverseArrayPar transforms an array
|
||||
func TraverseArrayPar[
|
||||
AS ~[]A,
|
||||
GRBS ~func(context.Context) GIOBS,
|
||||
GRB ~func(context.Context) GIOB,
|
||||
GIOBS ~func() E.Either[error, BS],
|
||||
GIOB ~func() E.Either[error, B],
|
||||
BS ~[]B,
|
||||
A, B any](f func(A) GRB) func(AS) GRBS {
|
||||
|
||||
return RA.Traverse[AS](
|
||||
Of[GRBS, GIOBS, BS],
|
||||
Map[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GIOBS, func() E.Either[error, func(B) BS], BS, func(B) BS],
|
||||
ApPar[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GRB],
|
||||
|
||||
f,
|
||||
)
|
||||
}
|
||||
|
||||
// TraverseArrayWithIndexPar transforms an array
|
||||
func TraverseArrayWithIndexPar[
|
||||
AS ~[]A,
|
||||
GRBS ~func(context.Context) GIOBS,
|
||||
GRB ~func(context.Context) GIOB,
|
||||
GIOBS ~func() E.Either[error, BS],
|
||||
GIOB ~func() E.Either[error, B],
|
||||
BS ~[]B,
|
||||
A, B any](f func(int, A) GRB) func(AS) GRBS {
|
||||
|
||||
return RA.TraverseWithIndex[AS](
|
||||
Of[GRBS, GIOBS, BS],
|
||||
Map[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GIOBS, func() E.Either[error, func(B) BS], BS, func(B) BS],
|
||||
ApPar[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GRB],
|
||||
|
||||
f,
|
||||
)
|
||||
}
|
||||
|
||||
// SequenceArrayPar converts a homogeneous sequence of either into an either of sequence
|
||||
func SequenceArrayPar[
|
||||
AS ~[]A,
|
||||
GAS ~[]GRA,
|
||||
GRAS ~func(context.Context) GIOAS,
|
||||
GRA ~func(context.Context) GIOA,
|
||||
GIOAS ~func() E.Either[error, AS],
|
||||
GIOA ~func() E.Either[error, A],
|
||||
A any](ma GAS) GRAS {
|
||||
|
||||
return MonadTraverseArrayPar[GAS, GRAS](ma, F.Identity[GRA])
|
||||
}
|
||||
|
||||
// MonadTraverseRecordPar transforms a record
|
||||
func MonadTraverseRecordPar[K comparable,
|
||||
AS ~map[K]A,
|
||||
GRBS ~func(context.Context) GIOBS,
|
||||
GRB ~func(context.Context) GIOB,
|
||||
GIOBS ~func() E.Either[error, BS],
|
||||
GIOB ~func() E.Either[error, B],
|
||||
BS ~map[K]B,
|
||||
|
||||
A, B any](ma AS, f func(A) GRB) GRBS {
|
||||
|
||||
return RR.MonadTraverse[AS](
|
||||
Of[GRBS, GIOBS, BS],
|
||||
Map[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GIOBS, func() E.Either[error, func(B) BS], BS, func(B) BS],
|
||||
ApPar[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GRB],
|
||||
|
||||
ma, f,
|
||||
)
|
||||
}
|
||||
|
||||
// TraverseRecordPar transforms a record
|
||||
func TraverseRecordPar[K comparable,
|
||||
AS ~map[K]A,
|
||||
GRBS ~func(context.Context) GIOBS,
|
||||
GRB ~func(context.Context) GIOB,
|
||||
GIOBS ~func() E.Either[error, BS],
|
||||
GIOB ~func() E.Either[error, B],
|
||||
BS ~map[K]B,
|
||||
|
||||
A, B any](f func(A) GRB) func(AS) GRBS {
|
||||
|
||||
return RR.Traverse[AS](
|
||||
Of[GRBS, GIOBS, BS],
|
||||
Map[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GIOBS, func() E.Either[error, func(B) BS], BS, func(B) BS],
|
||||
ApPar[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GRB],
|
||||
|
||||
f,
|
||||
)
|
||||
}
|
||||
|
||||
// TraverseRecordWithIndexPar transforms a record
|
||||
func TraverseRecordWithIndexPar[K comparable,
|
||||
AS ~map[K]A,
|
||||
GRBS ~func(context.Context) GIOBS,
|
||||
GRB ~func(context.Context) GIOB,
|
||||
GIOBS ~func() E.Either[error, BS],
|
||||
GIOB ~func() E.Either[error, B],
|
||||
BS ~map[K]B,
|
||||
|
||||
A, B any](f func(K, A) GRB) func(AS) GRBS {
|
||||
|
||||
return RR.TraverseWithIndex[AS](
|
||||
Of[GRBS, GIOBS, BS],
|
||||
Map[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GIOBS, func() E.Either[error, func(B) BS], BS, func(B) BS],
|
||||
ApPar[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GRB],
|
||||
|
||||
f,
|
||||
)
|
||||
}
|
||||
|
||||
// SequenceRecordPar converts a homogeneous sequence of either into an either of sequence
|
||||
func SequenceRecordPar[K comparable,
|
||||
AS ~map[K]A,
|
||||
GAS ~map[K]GRA,
|
||||
GRAS ~func(context.Context) GIOAS,
|
||||
GRA ~func(context.Context) GIOA,
|
||||
GIOAS ~func() E.Either[error, AS],
|
||||
GIOA ~func() E.Either[error, A],
|
||||
A any](ma GAS) GRAS {
|
||||
|
||||
return MonadTraverseRecordPar[K, GAS, GRAS](ma, F.Identity[GRA])
|
||||
}
|
||||
|
@@ -48,3 +48,63 @@ func TraverseRecordWithIndex[K comparable, A, B any](f func(K, A) ReaderIOEither
|
||||
func SequenceRecord[K comparable, A any](ma map[K]ReaderIOEither[A]) ReaderIOEither[map[K]A] {
|
||||
return G.SequenceRecord[K, map[K]A, map[K]ReaderIOEither[A], ReaderIOEither[map[K]A]](ma)
|
||||
}
|
||||
|
||||
// TraverseArraySeq uses transforms an array [[]A] into [[]ReaderIOEither[B]] and then resolves that into a [ReaderIOEither[[]B]]
|
||||
func TraverseArraySeq[A, B any](f func(A) ReaderIOEither[B]) func([]A) ReaderIOEither[[]B] {
|
||||
return G.TraverseArraySeq[[]A, ReaderIOEither[[]B]](f)
|
||||
}
|
||||
|
||||
// TraverseArrayWithIndexSeq uses transforms an array [[]A] into [[]ReaderIOEither[B]] and then resolves that into a [ReaderIOEither[[]B]]
|
||||
func TraverseArrayWithIndexSeq[A, B any](f func(int, A) ReaderIOEither[B]) func([]A) ReaderIOEither[[]B] {
|
||||
return G.TraverseArrayWithIndexSeq[[]A, ReaderIOEither[[]B]](f)
|
||||
}
|
||||
|
||||
// SequenceArraySeq converts a homogeneous sequence of either into an either of sequence
|
||||
func SequenceArraySeq[A any](ma []ReaderIOEither[A]) ReaderIOEither[[]A] {
|
||||
return G.SequenceArraySeq[[]A, []ReaderIOEither[A], ReaderIOEither[[]A]](ma)
|
||||
}
|
||||
|
||||
// TraverseRecordSeq uses transforms a record [map[K]A] into [map[K]ReaderIOEither[B]] and then resolves that into a [ReaderIOEither[map[K]B]]
|
||||
func TraverseRecordSeq[K comparable, A, B any](f func(A) ReaderIOEither[B]) func(map[K]A) ReaderIOEither[map[K]B] {
|
||||
return G.TraverseRecordSeq[K, map[K]A, ReaderIOEither[map[K]B]](f)
|
||||
}
|
||||
|
||||
// TraverseRecordWithIndexSeq uses transforms a record [map[K]A] into [map[K]ReaderIOEither[B]] and then resolves that into a [ReaderIOEither[map[K]B]]
|
||||
func TraverseRecordWithIndexSeq[K comparable, A, B any](f func(K, A) ReaderIOEither[B]) func(map[K]A) ReaderIOEither[map[K]B] {
|
||||
return G.TraverseRecordWithIndexSeq[K, map[K]A, ReaderIOEither[map[K]B]](f)
|
||||
}
|
||||
|
||||
// SequenceRecordSeq converts a homogeneous sequence of either into an either of sequence
|
||||
func SequenceRecordSeq[K comparable, A any](ma map[K]ReaderIOEither[A]) ReaderIOEither[map[K]A] {
|
||||
return G.SequenceRecordSeq[K, map[K]A, map[K]ReaderIOEither[A], ReaderIOEither[map[K]A]](ma)
|
||||
}
|
||||
|
||||
// TraverseArrayPar uses transforms an array [[]A] into [[]ReaderIOEither[B]] and then resolves that into a [ReaderIOEither[[]B]]
|
||||
func TraverseArrayPar[A, B any](f func(A) ReaderIOEither[B]) func([]A) ReaderIOEither[[]B] {
|
||||
return G.TraverseArrayPar[[]A, ReaderIOEither[[]B]](f)
|
||||
}
|
||||
|
||||
// TraverseArrayWithIndexPar uses transforms an array [[]A] into [[]ReaderIOEither[B]] and then resolves that into a [ReaderIOEither[[]B]]
|
||||
func TraverseArrayWithIndexPar[A, B any](f func(int, A) ReaderIOEither[B]) func([]A) ReaderIOEither[[]B] {
|
||||
return G.TraverseArrayWithIndexPar[[]A, ReaderIOEither[[]B]](f)
|
||||
}
|
||||
|
||||
// SequenceArrayPar converts a homogeneous sequence of either into an either of sequence
|
||||
func SequenceArrayPar[A any](ma []ReaderIOEither[A]) ReaderIOEither[[]A] {
|
||||
return G.SequenceArrayPar[[]A, []ReaderIOEither[A], ReaderIOEither[[]A]](ma)
|
||||
}
|
||||
|
||||
// TraverseRecordPar uses transforms a record [map[K]A] into [map[K]ReaderIOEither[B]] and then resolves that into a [ReaderIOEither[map[K]B]]
|
||||
func TraverseRecordPar[K comparable, A, B any](f func(A) ReaderIOEither[B]) func(map[K]A) ReaderIOEither[map[K]B] {
|
||||
return G.TraverseRecordPar[K, map[K]A, ReaderIOEither[map[K]B]](f)
|
||||
}
|
||||
|
||||
// TraverseRecordWithIndexPar uses transforms a record [map[K]A] into [map[K]ReaderIOEither[B]] and then resolves that into a [ReaderIOEither[map[K]B]]
|
||||
func TraverseRecordWithIndexPar[K comparable, A, B any](f func(K, A) ReaderIOEither[B]) func(map[K]A) ReaderIOEither[map[K]B] {
|
||||
return G.TraverseRecordWithIndexPar[K, map[K]A, ReaderIOEither[map[K]B]](f)
|
||||
}
|
||||
|
||||
// SequenceRecordPar converts a homogeneous sequence of either into an either of sequence
|
||||
func SequenceRecordPar[K comparable, A any](ma map[K]ReaderIOEither[A]) ReaderIOEither[map[K]A] {
|
||||
return G.SequenceRecordPar[K, map[K]A, map[K]ReaderIOEither[A], ReaderIOEither[map[K]A]](ma)
|
||||
}
|
||||
|
@@ -117,7 +117,7 @@ var (
|
||||
mp,
|
||||
R.Map[int](F.Flow3(
|
||||
getAt(res),
|
||||
IOO.FromIOEither[error, any],
|
||||
IOE.ToIOOption[error, any],
|
||||
F.ToAny[IOO.IOOption[any]],
|
||||
)),
|
||||
IOE.Of[error, paramValue],
|
||||
|
115
di/provider.go
115
di/provider.go
@@ -73,6 +73,46 @@ func eraseProviderFactory2[T1, T2 any, R any](
|
||||
}
|
||||
}
|
||||
|
||||
func eraseProviderFactory3[T1, T2, T3 any, R any](
|
||||
d1 Dependency[T1],
|
||||
d2 Dependency[T2],
|
||||
d3 Dependency[T3],
|
||||
f func(T1, T2, T3) IOE.IOEither[error, R]) func(params ...any) IOE.IOEither[error, any] {
|
||||
ft := T.Tupled3(f)
|
||||
t1 := lookupAt[T1](0, d1)
|
||||
t2 := lookupAt[T2](1, d2)
|
||||
t3 := lookupAt[T3](2, d3)
|
||||
return func(params ...any) IOE.IOEither[error, any] {
|
||||
return F.Pipe3(
|
||||
E.SequenceT3(t1(params), t2(params), t3(params)),
|
||||
IOE.FromEither[error, T.Tuple3[T1, T2, T3]],
|
||||
IOE.Chain(ft),
|
||||
IOE.Map[error](F.ToAny[R]),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func eraseProviderFactory4[T1, T2, T3, T4 any, R any](
|
||||
d1 Dependency[T1],
|
||||
d2 Dependency[T2],
|
||||
d3 Dependency[T3],
|
||||
d4 Dependency[T4],
|
||||
f func(T1, T2, T3, T4) IOE.IOEither[error, R]) func(params ...any) IOE.IOEither[error, any] {
|
||||
ft := T.Tupled4(f)
|
||||
t1 := lookupAt[T1](0, d1)
|
||||
t2 := lookupAt[T2](1, d2)
|
||||
t3 := lookupAt[T3](2, d3)
|
||||
t4 := lookupAt[T4](3, d4)
|
||||
return func(params ...any) IOE.IOEither[error, any] {
|
||||
return F.Pipe3(
|
||||
E.SequenceT4(t1(params), t2(params), t3(params), t4(params)),
|
||||
IOE.FromEither[error, T.Tuple4[T1, T2, T3, T4]],
|
||||
IOE.Chain(ft),
|
||||
IOE.Map[error](F.ToAny[R]),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func MakeProviderFactory0[R any](
|
||||
fct func() IOE.IOEither[error, R],
|
||||
) DIE.ProviderFactory {
|
||||
@@ -160,6 +200,81 @@ func MakeProvider2[T1, T2, R any](
|
||||
)
|
||||
}
|
||||
|
||||
func MakeProviderFactory3[T1, T2, T3, R any](
|
||||
d1 Dependency[T1],
|
||||
d2 Dependency[T2],
|
||||
d3 Dependency[T3],
|
||||
fct func(T1, T2, T3) IOE.IOEither[error, R],
|
||||
) DIE.ProviderFactory {
|
||||
|
||||
return DIE.MakeProviderFactory(
|
||||
A.From[DIE.Dependency](d1, d2, d3),
|
||||
eraseProviderFactory3(d1, d2, d3, fct),
|
||||
)
|
||||
}
|
||||
|
||||
// MakeTokenWithDefault3 create a unique `InjectionToken` for a specific type with an attached default provider
|
||||
func MakeTokenWithDefault3[T1, T2, T3, R any](name string,
|
||||
d1 Dependency[T1],
|
||||
d2 Dependency[T2],
|
||||
d3 Dependency[T3],
|
||||
fct func(T1, T2, T3) IOE.IOEither[error, R]) InjectionToken[R] {
|
||||
return MakeTokenWithDefault[R](name, MakeProviderFactory3(d1, d2, d3, fct))
|
||||
}
|
||||
|
||||
func MakeProvider3[T1, T2, T3, R any](
|
||||
token InjectionToken[R],
|
||||
d1 Dependency[T1],
|
||||
d2 Dependency[T2],
|
||||
d3 Dependency[T3],
|
||||
fct func(T1, T2, T3) IOE.IOEither[error, R],
|
||||
) DIE.Provider {
|
||||
|
||||
return DIE.MakeProvider(
|
||||
token,
|
||||
MakeProviderFactory3(d1, d2, d3, fct),
|
||||
)
|
||||
}
|
||||
|
||||
func MakeProviderFactory4[T1, T2, T3, T4, R any](
|
||||
d1 Dependency[T1],
|
||||
d2 Dependency[T2],
|
||||
d3 Dependency[T3],
|
||||
d4 Dependency[T4],
|
||||
fct func(T1, T2, T3, T4) IOE.IOEither[error, R],
|
||||
) DIE.ProviderFactory {
|
||||
|
||||
return DIE.MakeProviderFactory(
|
||||
A.From[DIE.Dependency](d1, d2, d3, d4),
|
||||
eraseProviderFactory4(d1, d2, d3, d4, fct),
|
||||
)
|
||||
}
|
||||
|
||||
// MakeTokenWithDefault4 create a unique `InjectionToken` for a specific type with an attached default provider
|
||||
func MakeTokenWithDefault4[T1, T2, T3, T4, R any](name string,
|
||||
d1 Dependency[T1],
|
||||
d2 Dependency[T2],
|
||||
d3 Dependency[T3],
|
||||
d4 Dependency[T4],
|
||||
fct func(T1, T2, T3, T4) IOE.IOEither[error, R]) InjectionToken[R] {
|
||||
return MakeTokenWithDefault[R](name, MakeProviderFactory4(d1, d2, d3, d4, fct))
|
||||
}
|
||||
|
||||
func MakeProvider4[T1, T2, T3, T4, R any](
|
||||
token InjectionToken[R],
|
||||
d1 Dependency[T1],
|
||||
d2 Dependency[T2],
|
||||
d3 Dependency[T3],
|
||||
d4 Dependency[T4],
|
||||
fct func(T1, T2, T3, T4) IOE.IOEither[error, R],
|
||||
) DIE.Provider {
|
||||
|
||||
return DIE.MakeProvider(
|
||||
token,
|
||||
MakeProviderFactory4(d1, d2, d3, d4, fct),
|
||||
)
|
||||
}
|
||||
|
||||
// ConstProvider simple implementation for a provider with a constant value
|
||||
func ConstProvider[R any](token InjectionToken[R], value R) DIE.Provider {
|
||||
return MakeProvider0[R](token, F.Constant(IOE.Of[error](value)))
|
||||
|
@@ -61,14 +61,14 @@ func SequenceArray[E, A any](ma []Either[E, A]) Either[E, []A] {
|
||||
return SequenceArrayG[[]A](ma)
|
||||
}
|
||||
|
||||
// CompactArrayG discards the none values and keeps the some values
|
||||
// CompactArrayG discards the none values and keeps the right values
|
||||
func CompactArrayG[A1 ~[]Either[E, A], A2 ~[]A, E, A any](fa A1) A2 {
|
||||
return RA.Reduce(fa, func(out A2, value Either[E, A]) A2 {
|
||||
return MonadFold(value, F.Constant1[E](out), F.Bind1st(RA.Append[A2, A], out))
|
||||
}, make(A2, len(fa)))
|
||||
}, make(A2, 0, len(fa)))
|
||||
}
|
||||
|
||||
// CompactArray discards the none values and keeps the some values
|
||||
// CompactArray discards the none values and keeps the right values
|
||||
func CompactArray[E, A any](fa []Either[E, A]) []A {
|
||||
return CompactArrayG[[]Either[E, A], []A](fa)
|
||||
}
|
||||
|
18
either/array_test.go
Normal file
18
either/array_test.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package either
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestCompactArray(t *testing.T) {
|
||||
ar := []Either[string, string]{
|
||||
Of[string]("ok"),
|
||||
Left[string]("err"),
|
||||
Of[string]("ok"),
|
||||
}
|
||||
|
||||
res := CompactArray(ar)
|
||||
assert.Equal(t, 2, len(res))
|
||||
}
|
@@ -89,7 +89,7 @@ func MonadChainFirst[E, A, B any](ma Either[E, A], f func(a A) Either[E, B]) Eit
|
||||
})
|
||||
}
|
||||
|
||||
func MonadChainTo[E, A, B any](ma Either[E, A], mb Either[E, B]) Either[E, B] {
|
||||
func MonadChainTo[A, E, B any](ma Either[E, A], mb Either[E, B]) Either[E, B] {
|
||||
return mb
|
||||
}
|
||||
|
||||
@@ -104,8 +104,8 @@ func ChainOptionK[A, B, E any](onNone func() E) func(func(A) O.Option[B]) func(E
|
||||
}
|
||||
}
|
||||
|
||||
func ChainTo[E, A, B any](mb Either[E, B]) func(Either[E, A]) Either[E, B] {
|
||||
return F.Bind2nd(MonadChainTo[E, A, B], mb)
|
||||
func ChainTo[A, E, B any](mb Either[E, B]) func(Either[E, A]) Either[E, B] {
|
||||
return F.Bind2nd(MonadChainTo[A, E, B], mb)
|
||||
}
|
||||
|
||||
func Chain[E, A, B any](f func(a A) Either[E, B]) func(Either[E, A]) Either[E, B] {
|
||||
|
@@ -41,7 +41,7 @@ func Logger[E, A any](loggers ...*log.Logger) func(string) func(Either[E, A]) Ei
|
||||
return func(ma Either[E, A]) Either[E, A] {
|
||||
return F.Pipe1(
|
||||
delegate(ma),
|
||||
ChainTo[E, A](ma),
|
||||
ChainTo[A](ma),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
47
endomorphism/generic/monoid.go
Normal file
47
endomorphism/generic/monoid.go
Normal file
@@ -0,0 +1,47 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package 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]())
|
||||
}
|
@@ -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]]()
|
||||
}
|
||||
|
@@ -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
|
||||
}
|
||||
|
2
go.mod
2
go.mod
@@ -4,7 +4,7 @@ go 1.20
|
||||
|
||||
require (
|
||||
github.com/stretchr/testify v1.8.4
|
||||
github.com/urfave/cli/v2 v2.25.7
|
||||
github.com/urfave/cli/v2 v2.26.0
|
||||
)
|
||||
|
||||
require (
|
||||
|
2
go.sum
2
go.sum
@@ -10,6 +10,8 @@ github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcU
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs=
|
||||
github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ=
|
||||
github.com/urfave/cli/v2 v2.26.0 h1:3f3AMg3HpThFNT4I++TKOejZO8yU55t3JnnSr4S4QEI=
|
||||
github.com/urfave/cli/v2 v2.26.0/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ=
|
||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
|
||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
|
71
http/form/form.go
Normal file
71
http/form/form.go
Normal file
@@ -0,0 +1,71 @@
|
||||
// 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"
|
||||
)
|
||||
|
||||
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]()
|
||||
|
||||
// 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.Flow3(
|
||||
O.Of[string],
|
||||
AtValue(name).Set,
|
||||
ENDO.Of[func(url.Values) url.Values],
|
||||
)
|
||||
}
|
||||
|
||||
// WithoutValue creates a [FormBuilder] that removes a field
|
||||
func WithoutValue(name string) Endomorphism {
|
||||
return AtValue(name).Set(noField)
|
||||
}
|
93
http/form/form_test.go
Normal file
93
http/form/form_test.go
Normal file
@@ -0,0 +1,93 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package form
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
A "github.com/IBM/fp-go/array"
|
||||
"github.com/IBM/fp-go/eq"
|
||||
F "github.com/IBM/fp-go/function"
|
||||
LT "github.com/IBM/fp-go/optics/lens/testing"
|
||||
O "github.com/IBM/fp-go/option"
|
||||
RG "github.com/IBM/fp-go/record/generic"
|
||||
S "github.com/IBM/fp-go/string"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var (
|
||||
sEq = eq.FromEquals(S.Eq)
|
||||
valuesEq = RG.Eq[url.Values](A.Eq(sEq))
|
||||
)
|
||||
|
||||
func TestLaws(t *testing.T) {
|
||||
name := "Content-Type"
|
||||
fieldLaws := LT.AssertLaws[url.Values, O.Option[string]](t, O.Eq(sEq), valuesEq)(AtValue(name))
|
||||
|
||||
n := O.None[string]()
|
||||
s1 := O.Some("s1")
|
||||
|
||||
v1 := F.Pipe1(
|
||||
Default,
|
||||
WithValue(name)("v1"),
|
||||
)
|
||||
|
||||
v2 := F.Pipe1(
|
||||
Default,
|
||||
WithValue("Other-Header")("v2"),
|
||||
)
|
||||
|
||||
assert.True(t, fieldLaws(Default, n))
|
||||
assert.True(t, fieldLaws(v1, n))
|
||||
assert.True(t, fieldLaws(v2, n))
|
||||
|
||||
assert.True(t, fieldLaws(Default, s1))
|
||||
assert.True(t, fieldLaws(v1, s1))
|
||||
assert.True(t, fieldLaws(v2, s1))
|
||||
}
|
||||
|
||||
func TestFormField(t *testing.T) {
|
||||
|
||||
v1 := F.Pipe1(
|
||||
Default,
|
||||
WithValue("h1")("v1"),
|
||||
)
|
||||
|
||||
v2 := F.Pipe1(
|
||||
v1,
|
||||
WithValue("h2")("v2"),
|
||||
)
|
||||
|
||||
// make sure the code does not change structures
|
||||
assert.False(t, valuesEq.Equals(Default, v1))
|
||||
assert.False(t, valuesEq.Equals(Default, v2))
|
||||
assert.False(t, valuesEq.Equals(v1, v2))
|
||||
|
||||
// check for existence of values
|
||||
assert.Equal(t, "v1", v1.Get("h1"))
|
||||
assert.Equal(t, "v1", v2.Get("h1"))
|
||||
assert.Equal(t, "v2", v2.Get("h2"))
|
||||
|
||||
// check getter on lens
|
||||
|
||||
l1 := AtValue("h1")
|
||||
l2 := AtValue("h2")
|
||||
|
||||
assert.Equal(t, O.Of("v1"), l1.Get(v1))
|
||||
assert.Equal(t, O.Of("v1"), l1.Get(v2))
|
||||
assert.Equal(t, O.Of("v2"), l2.Get(v2))
|
||||
}
|
@@ -32,6 +32,17 @@ func MonadAlt[LAZY ~func() HKTFA, E, A, HKTFA any](
|
||||
return fchain(first, ET.Fold(F.Ignore1of1[E](second), F.Flow2(ET.Of[E, A], fof)))
|
||||
}
|
||||
|
||||
func Alt[LAZY ~func() HKTFA, E, A, HKTFA any](
|
||||
fof func(ET.Either[E, A]) HKTFA,
|
||||
fchain func(HKTFA, func(ET.Either[E, A]) HKTFA) HKTFA,
|
||||
|
||||
second LAZY) func(HKTFA) HKTFA {
|
||||
|
||||
return func(fa HKTFA) HKTFA {
|
||||
return MonadAlt(fof, fchain, fa, second)
|
||||
}
|
||||
}
|
||||
|
||||
// HKTFA = HKT<F, Either<E, A>>
|
||||
// HKTFB = HKT<F, Either<E, B>>
|
||||
func MonadMap[E, A, B, HKTFA, HKTFB any](fmap func(HKTFA, func(ET.Either[E, A]) ET.Either[E, B]) HKTFB, fa HKTFA, f func(A) B) HKTFB {
|
||||
|
@@ -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,
|
||||
@@ -78,3 +88,24 @@ func MonadChainOptionK[A, B, HKTA, HKTB any](
|
||||
) HKTB {
|
||||
return MonadChain(fchain, fof, ma, FromOptionK(fof, f))
|
||||
}
|
||||
|
||||
func MonadAlt[LAZY ~func() HKTFA, A, HKTFA any](
|
||||
fof func(O.Option[A]) HKTFA,
|
||||
fchain func(HKTFA, func(O.Option[A]) HKTFA) HKTFA,
|
||||
|
||||
first HKTFA,
|
||||
second LAZY) HKTFA {
|
||||
|
||||
return fchain(first, O.Fold(second, F.Flow2(O.Of[A], fof)))
|
||||
}
|
||||
|
||||
func Alt[LAZY ~func() HKTFA, A, HKTFA any](
|
||||
fof func(O.Option[A]) HKTFA,
|
||||
fchain func(HKTFA, func(O.Option[A]) HKTFA) HKTFA,
|
||||
|
||||
second LAZY) func(HKTFA) HKTFA {
|
||||
|
||||
return func(fa HKTFA) HKTFA {
|
||||
return MonadAlt(fof, fchain, fa, second)
|
||||
}
|
||||
}
|
||||
|
@@ -16,6 +16,7 @@
|
||||
package generic
|
||||
|
||||
import (
|
||||
F "github.com/IBM/fp-go/function"
|
||||
G "github.com/IBM/fp-go/internal/apply"
|
||||
)
|
||||
|
||||
@@ -27,7 +28,10 @@ const (
|
||||
// MonadApSeq implements the applicative on a single thread by first executing mab and the ma
|
||||
func MonadApSeq[GA ~func() A, GB ~func() B, GAB ~func() func(A) B, A, B any](mab GAB, ma GA) GB {
|
||||
return MakeIO[GB](func() B {
|
||||
return mab()(ma())
|
||||
return F.Pipe1(
|
||||
ma(),
|
||||
mab(),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
|
30
io/generic/resource.go
Normal file
30
io/generic/resource.go
Normal file
@@ -0,0 +1,30 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package 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
27
io/resource.go
Normal 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)
|
||||
}
|
@@ -336,3 +336,14 @@ func MonadFlap[GEAB ~func() ET.Either[E, func(A) B], GEB ~func() ET.Either[E, B]
|
||||
func Flap[GEAB ~func() ET.Either[E, func(A) B], GEB ~func() ET.Either[E, B], E, B, A any](a A) func(GEAB) GEB {
|
||||
return FC.Flap(MonadMap[GEAB, GEB], a)
|
||||
}
|
||||
|
||||
func ToIOOption[GA ~func() O.Option[A], GEA ~func() ET.Either[E, A], E, A any](ioe GEA) GA {
|
||||
return F.Pipe1(
|
||||
ioe,
|
||||
IO.Map[GEA, GA](ET.ToOption[E, A]),
|
||||
)
|
||||
}
|
||||
|
||||
func FromIOOption[GEA ~func() ET.Either[E, A], GA ~func() O.Option[A], E, A any](onNone func() E) func(ioo GA) GEA {
|
||||
return IO.Map[GA, GEA](ET.FromOption[A](onNone))
|
||||
}
|
||||
|
@@ -111,3 +111,183 @@ func TraverseRecordWithIndex[GB ~func() ET.Either[E, B], GBS ~func() ET.Either[E
|
||||
func SequenceRecord[GA ~func() ET.Either[E, A], GAS ~func() ET.Either[E, AAS], AAS ~map[K]A, GAAS ~map[K]GA, K comparable, E, A any](tas GAAS) GAS {
|
||||
return MonadTraverseRecord[GA, GAS](tas, F.Identity[GA])
|
||||
}
|
||||
|
||||
// MonadTraverseArraySeq transforms an array
|
||||
func MonadTraverseArraySeq[GB ~func() ET.Either[E, B], GBS ~func() ET.Either[E, BBS], AAS ~[]A, BBS ~[]B, E, A, B any](tas AAS, f func(A) GB) GBS {
|
||||
return RA.MonadTraverse[AAS](
|
||||
Of[GBS, E, BBS],
|
||||
Map[GBS, func() ET.Either[E, func(B) BBS], E, BBS, func(B) BBS],
|
||||
ApSeq[GBS, func() ET.Either[E, func(B) BBS], GB],
|
||||
|
||||
tas,
|
||||
f,
|
||||
)
|
||||
}
|
||||
|
||||
// TraverseArraySeq transforms an array
|
||||
func TraverseArraySeq[GB ~func() ET.Either[E, B], GBS ~func() ET.Either[E, BBS], AAS ~[]A, BBS ~[]B, E, A, B any](f func(A) GB) func(AAS) GBS {
|
||||
return RA.Traverse[AAS](
|
||||
Of[GBS, E, BBS],
|
||||
Map[GBS, func() ET.Either[E, func(B) BBS], E, BBS, func(B) BBS],
|
||||
ApSeq[GBS, func() ET.Either[E, func(B) BBS], GB],
|
||||
|
||||
f,
|
||||
)
|
||||
}
|
||||
|
||||
// MonadTraverseArrayWithIndexSeq transforms an array
|
||||
func MonadTraverseArrayWithIndexSeq[GB ~func() ET.Either[E, B], GBS ~func() ET.Either[E, BBS], AAS ~[]A, BBS ~[]B, E, A, B any](tas AAS, f func(int, A) GB) GBS {
|
||||
return RA.MonadTraverseWithIndex[AAS](
|
||||
Of[GBS, E, BBS],
|
||||
Map[GBS, func() ET.Either[E, func(B) BBS], E, BBS, func(B) BBS],
|
||||
ApSeq[GBS, func() ET.Either[E, func(B) BBS], GB],
|
||||
|
||||
tas,
|
||||
f,
|
||||
)
|
||||
}
|
||||
|
||||
// TraverseArrayWithIndexSeq transforms an array
|
||||
func TraverseArrayWithIndexSeq[GB ~func() ET.Either[E, B], GBS ~func() ET.Either[E, BBS], AAS ~[]A, BBS ~[]B, E, A, B any](f func(int, A) GB) func(AAS) GBS {
|
||||
return RA.TraverseWithIndex[AAS](
|
||||
Of[GBS, E, BBS],
|
||||
Map[GBS, func() ET.Either[E, func(B) BBS], E, BBS, func(B) BBS],
|
||||
ApSeq[GBS, func() ET.Either[E, func(B) BBS], GB],
|
||||
|
||||
f,
|
||||
)
|
||||
}
|
||||
|
||||
// SequenceArraySeq converts a homogeneous sequence of either into an either of sequence
|
||||
func SequenceArraySeq[GA ~func() ET.Either[E, A], GAS ~func() ET.Either[E, AAS], AAS ~[]A, GAAS ~[]GA, E, A any](tas GAAS) GAS {
|
||||
return MonadTraverseArraySeq[GA, GAS](tas, F.Identity[GA])
|
||||
}
|
||||
|
||||
// MonadTraverseRecordSeq transforms an array
|
||||
func MonadTraverseRecordSeq[GB ~func() ET.Either[E, B], GBS ~func() ET.Either[E, BBS], AAS ~map[K]A, BBS ~map[K]B, K comparable, E, A, B any](tas AAS, f func(A) GB) GBS {
|
||||
return RR.MonadTraverse[AAS](
|
||||
Of[GBS, E, BBS],
|
||||
Map[GBS, func() ET.Either[E, func(B) BBS], E, BBS, func(B) BBS],
|
||||
ApSeq[GBS, func() ET.Either[E, func(B) BBS], GB],
|
||||
|
||||
tas,
|
||||
f,
|
||||
)
|
||||
}
|
||||
|
||||
// TraverseRecordSeq transforms an array
|
||||
func TraverseRecordSeq[GB ~func() ET.Either[E, B], GBS ~func() ET.Either[E, BBS], AAS ~map[K]A, BBS ~map[K]B, K comparable, E, A, B any](f func(A) GB) func(AAS) GBS {
|
||||
return RR.Traverse[AAS](
|
||||
Of[GBS, E, BBS],
|
||||
Map[GBS, func() ET.Either[E, func(B) BBS], E, BBS, func(B) BBS],
|
||||
ApSeq[GBS, func() ET.Either[E, func(B) BBS], GB],
|
||||
|
||||
f,
|
||||
)
|
||||
}
|
||||
|
||||
// TraverseRecordWithIndexSeq transforms an array
|
||||
func TraverseRecordWithIndexSeq[GB ~func() ET.Either[E, B], GBS ~func() ET.Either[E, BBS], AAS ~map[K]A, BBS ~map[K]B, K comparable, E, A, B any](f func(K, A) GB) func(AAS) GBS {
|
||||
return RR.TraverseWithIndex[AAS](
|
||||
Of[GBS, E, BBS],
|
||||
Map[GBS, func() ET.Either[E, func(B) BBS], E, BBS, func(B) BBS],
|
||||
ApSeq[GBS, func() ET.Either[E, func(B) BBS], GB],
|
||||
|
||||
f,
|
||||
)
|
||||
}
|
||||
|
||||
// SequenceRecordSeq converts a homogeneous sequence of either into an either of sequence
|
||||
func SequenceRecordSeq[GA ~func() ET.Either[E, A], GAS ~func() ET.Either[E, AAS], AAS ~map[K]A, GAAS ~map[K]GA, K comparable, E, A any](tas GAAS) GAS {
|
||||
return MonadTraverseRecordSeq[GA, GAS](tas, F.Identity[GA])
|
||||
}
|
||||
|
||||
// MonadTraverseArrayPar transforms an array
|
||||
func MonadTraverseArrayPar[GB ~func() ET.Either[E, B], GBS ~func() ET.Either[E, BBS], AAS ~[]A, BBS ~[]B, E, A, B any](tas AAS, f func(A) GB) GBS {
|
||||
return RA.MonadTraverse[AAS](
|
||||
Of[GBS, E, BBS],
|
||||
Map[GBS, func() ET.Either[E, func(B) BBS], E, BBS, func(B) BBS],
|
||||
ApPar[GBS, func() ET.Either[E, func(B) BBS], GB],
|
||||
|
||||
tas,
|
||||
f,
|
||||
)
|
||||
}
|
||||
|
||||
// TraverseArrayPar transforms an array
|
||||
func TraverseArrayPar[GB ~func() ET.Either[E, B], GBS ~func() ET.Either[E, BBS], AAS ~[]A, BBS ~[]B, E, A, B any](f func(A) GB) func(AAS) GBS {
|
||||
return RA.Traverse[AAS](
|
||||
Of[GBS, E, BBS],
|
||||
Map[GBS, func() ET.Either[E, func(B) BBS], E, BBS, func(B) BBS],
|
||||
ApPar[GBS, func() ET.Either[E, func(B) BBS], GB],
|
||||
|
||||
f,
|
||||
)
|
||||
}
|
||||
|
||||
// MonadTraverseArrayWithIndexPar transforms an array
|
||||
func MonadTraverseArrayWithIndexPar[GB ~func() ET.Either[E, B], GBS ~func() ET.Either[E, BBS], AAS ~[]A, BBS ~[]B, E, A, B any](tas AAS, f func(int, A) GB) GBS {
|
||||
return RA.MonadTraverseWithIndex[AAS](
|
||||
Of[GBS, E, BBS],
|
||||
Map[GBS, func() ET.Either[E, func(B) BBS], E, BBS, func(B) BBS],
|
||||
ApPar[GBS, func() ET.Either[E, func(B) BBS], GB],
|
||||
|
||||
tas,
|
||||
f,
|
||||
)
|
||||
}
|
||||
|
||||
// TraverseArrayWithIndexPar transforms an array
|
||||
func TraverseArrayWithIndexPar[GB ~func() ET.Either[E, B], GBS ~func() ET.Either[E, BBS], AAS ~[]A, BBS ~[]B, E, A, B any](f func(int, A) GB) func(AAS) GBS {
|
||||
return RA.TraverseWithIndex[AAS](
|
||||
Of[GBS, E, BBS],
|
||||
Map[GBS, func() ET.Either[E, func(B) BBS], E, BBS, func(B) BBS],
|
||||
ApPar[GBS, func() ET.Either[E, func(B) BBS], GB],
|
||||
|
||||
f,
|
||||
)
|
||||
}
|
||||
|
||||
// SequenceArrayPar converts a homogeneous sequence of either into an either of sequence
|
||||
func SequenceArrayPar[GA ~func() ET.Either[E, A], GAS ~func() ET.Either[E, AAS], AAS ~[]A, GAAS ~[]GA, E, A any](tas GAAS) GAS {
|
||||
return MonadTraverseArrayPar[GA, GAS](tas, F.Identity[GA])
|
||||
}
|
||||
|
||||
// MonadTraverseRecordPar transforms an array
|
||||
func MonadTraverseRecordPar[GB ~func() ET.Either[E, B], GBS ~func() ET.Either[E, BBS], AAS ~map[K]A, BBS ~map[K]B, K comparable, E, A, B any](tas AAS, f func(A) GB) GBS {
|
||||
return RR.MonadTraverse[AAS](
|
||||
Of[GBS, E, BBS],
|
||||
Map[GBS, func() ET.Either[E, func(B) BBS], E, BBS, func(B) BBS],
|
||||
ApPar[GBS, func() ET.Either[E, func(B) BBS], GB],
|
||||
|
||||
tas,
|
||||
f,
|
||||
)
|
||||
}
|
||||
|
||||
// TraverseRecordPar transforms an array
|
||||
func TraverseRecordPar[GB ~func() ET.Either[E, B], GBS ~func() ET.Either[E, BBS], AAS ~map[K]A, BBS ~map[K]B, K comparable, E, A, B any](f func(A) GB) func(AAS) GBS {
|
||||
return RR.Traverse[AAS](
|
||||
Of[GBS, E, BBS],
|
||||
Map[GBS, func() ET.Either[E, func(B) BBS], E, BBS, func(B) BBS],
|
||||
ApPar[GBS, func() ET.Either[E, func(B) BBS], GB],
|
||||
|
||||
f,
|
||||
)
|
||||
}
|
||||
|
||||
// TraverseRecordWithIndexPar transforms an array
|
||||
func TraverseRecordWithIndexPar[GB ~func() ET.Either[E, B], GBS ~func() ET.Either[E, BBS], AAS ~map[K]A, BBS ~map[K]B, K comparable, E, A, B any](f func(K, A) GB) func(AAS) GBS {
|
||||
return RR.TraverseWithIndex[AAS](
|
||||
Of[GBS, E, BBS],
|
||||
Map[GBS, func() ET.Either[E, func(B) BBS], E, BBS, func(B) BBS],
|
||||
ApPar[GBS, func() ET.Either[E, func(B) BBS], GB],
|
||||
|
||||
f,
|
||||
)
|
||||
}
|
||||
|
||||
// SequenceRecordPar converts a homogeneous sequence of either into an either of sequence
|
||||
func SequenceRecordPar[GA ~func() ET.Either[E, A], GAS ~func() ET.Either[E, AAS], AAS ~map[K]A, GAAS ~map[K]GA, K comparable, E, A any](tas GAAS) GAS {
|
||||
return MonadTraverseRecordPar[GA, GAS](tas, F.Identity[GA])
|
||||
}
|
||||
|
267
ioeither/http/builder/builder.go
Normal file
267
ioeither/http/builder/builder.go
Normal file
@@ -0,0 +1,267 @@
|
||||
// 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"
|
||||
|
||||
ENDO "github.com/IBM/fp-go/endomorphism"
|
||||
F "github.com/IBM/fp-go/function"
|
||||
IOE "github.com/IBM/fp-go/ioeither"
|
||||
IOEH "github.com/IBM/fp-go/ioeither/http"
|
||||
J "github.com/IBM/fp-go/json"
|
||||
LZ "github.com/IBM/fp-go/lazy"
|
||||
L "github.com/IBM/fp-go/optics/lens"
|
||||
O "github.com/IBM/fp-go/option"
|
||||
S "github.com/IBM/fp-go/string"
|
||||
)
|
||||
|
||||
type (
|
||||
Builder struct {
|
||||
method O.Option[string]
|
||||
url string
|
||||
headers http.Header
|
||||
body O.Option[IOE.IOEither[error, []byte]]
|
||||
}
|
||||
|
||||
// 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)
|
||||
|
||||
getHeader = F.Bind2of2((*Builder).GetHeader)
|
||||
delHeader = F.Bind2of2((*Builder).DelHeader)
|
||||
setHeader = F.Bind2of3((*Builder).SetHeader)
|
||||
|
||||
noHeader = O.None[string]()
|
||||
noBody = O.None[IOE.IOEither[error, []byte]]()
|
||||
|
||||
// WithMethod creates a [BuilderBuilder] for a certain method
|
||||
WithMethod = F.Flow2(
|
||||
Method.Set,
|
||||
ENDO.Of[func(*Builder) *Builder],
|
||||
)
|
||||
// WithUrl creates a [BuilderBuilder] for a certain method
|
||||
WithUrl = F.Flow2(
|
||||
Url.Set,
|
||||
ENDO.Of[func(*Builder) *Builder],
|
||||
)
|
||||
// WithHeaders creates a [BuilderBuilder] for a set of headers
|
||||
WithHeaders = F.Flow2(
|
||||
Headers.Set,
|
||||
ENDO.Of[func(*Builder) *Builder],
|
||||
)
|
||||
// WithBody creates a [BuilderBuilder] for a request body
|
||||
WithBody = F.Flow3(
|
||||
O.Of[IOE.IOEither[error, []byte]],
|
||||
Body.Set,
|
||||
ENDO.Of[func(*Builder) *Builder],
|
||||
)
|
||||
// WithContentType adds the content type header
|
||||
WithContentType = WithHeader("Content-Type")
|
||||
|
||||
// WithGet adds the [http.MethodGet] method
|
||||
WithGet = WithMethod(http.MethodGet)
|
||||
// WithPost adds the [http.MethodPost] method
|
||||
WithPost = WithMethod(http.MethodPost)
|
||||
// WithPut adds the [http.MethodPut] method
|
||||
WithPut = WithMethod(http.MethodPut)
|
||||
// WithDelete adds the [http.MethodDelete] method
|
||||
WithDelete = WithMethod(http.MethodDelete)
|
||||
|
||||
// Requester creates a requester from a builder
|
||||
Requester = (*Builder).Requester
|
||||
|
||||
// WithoutBody creates a [BuilderBuilder] to remove the body
|
||||
WithoutBody = F.Pipe2(
|
||||
noBody,
|
||||
Body.Set,
|
||||
ENDO.Of[func(*Builder) *Builder],
|
||||
)
|
||||
)
|
||||
|
||||
func (builder *Builder) clone() *Builder {
|
||||
cpy := *builder
|
||||
cpy.headers = cpy.headers.Clone()
|
||||
return &cpy
|
||||
}
|
||||
|
||||
func (builder *Builder) GetUrl() string {
|
||||
return builder.url
|
||||
}
|
||||
|
||||
func (builder *Builder) GetMethod() string {
|
||||
return F.Pipe1(
|
||||
builder.method,
|
||||
O.GetOrElse(defaultMethod),
|
||||
)
|
||||
}
|
||||
|
||||
func (builder *Builder) GetHeaders() http.Header {
|
||||
return builder.headers
|
||||
}
|
||||
|
||||
func (builder *Builder) GetBody() O.Option[IOE.IOEither[error, []byte]] {
|
||||
return builder.body
|
||||
}
|
||||
|
||||
func (builder *Builder) SetMethod(method string) *Builder {
|
||||
builder.method = O.Some(method)
|
||||
return builder
|
||||
}
|
||||
|
||||
func (builder *Builder) SetUrl(url string) *Builder {
|
||||
builder.url = url
|
||||
return builder
|
||||
}
|
||||
|
||||
func (builder *Builder) SetHeaders(headers http.Header) *Builder {
|
||||
builder.headers = headers
|
||||
return builder
|
||||
}
|
||||
|
||||
func (builder *Builder) SetBody(body O.Option[IOE.IOEither[error, []byte]]) *Builder {
|
||||
builder.body = body
|
||||
return builder
|
||||
}
|
||||
|
||||
func (builder *Builder) SetHeader(name, value string) *Builder {
|
||||
builder.headers.Set(name, value)
|
||||
return builder
|
||||
}
|
||||
|
||||
func (builder *Builder) DelHeader(name string) *Builder {
|
||||
builder.headers.Del(name)
|
||||
return builder
|
||||
}
|
||||
|
||||
func (builder *Builder) GetHeader(name string) O.Option[string] {
|
||||
return F.Pipe2(
|
||||
name,
|
||||
builder.headers.Get,
|
||||
O.FromPredicate(S.IsNonEmpty),
|
||||
)
|
||||
}
|
||||
|
||||
func (builder *Builder) GetHeaderValues(name string) []string {
|
||||
return builder.headers.Values(name)
|
||||
}
|
||||
|
||||
func (builder *Builder) AddHeaderHeader(name, value string) *Builder {
|
||||
builder.headers.Add(name, value)
|
||||
return builder
|
||||
}
|
||||
|
||||
func (builder *Builder) Requester() IOEH.Requester {
|
||||
return F.Pipe3(
|
||||
builder.GetBody(),
|
||||
O.Map(IOE.Map[error](bytes.NewReader)),
|
||||
O.GetOrElse(F.Constant(IOE.Of[error, *bytes.Reader](nil))),
|
||||
IOE.Chain(func(rdr *bytes.Reader) IOE.IOEither[error, *http.Request] {
|
||||
return IOE.TryCatchError(func() (*http.Request, error) {
|
||||
req, err := http.NewRequest(builder.GetMethod(), builder.GetUrl(), rdr)
|
||||
if err == nil {
|
||||
for name, value := range builder.GetHeaders() {
|
||||
req.Header[name] = value
|
||||
}
|
||||
if rdr != nil {
|
||||
req.Header.Set("Content-Length", strconv.FormatInt(rdr.Size(), 10))
|
||||
}
|
||||
}
|
||||
return req, err
|
||||
})
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
// Header returns a [L.Lens] for a single header
|
||||
func Header(name string) L.Lens[*Builder, O.Option[string]] {
|
||||
get := getHeader(name)
|
||||
set := F.Bind1of2(setHeader(name))
|
||||
del := F.Flow2(
|
||||
LZ.Of[*Builder],
|
||||
LZ.Map(delHeader(name)),
|
||||
)
|
||||
|
||||
return L.MakeLens[*Builder, O.Option[string]](get, func(b *Builder, value O.Option[string]) *Builder {
|
||||
cpy := b.clone()
|
||||
return F.Pipe1(
|
||||
value,
|
||||
O.Fold(del(cpy), set(cpy)),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
// WithHeader creates a [BuilderBuilder] for a certain header
|
||||
func WithHeader(name string) func(value string) Endomorphism {
|
||||
return F.Flow3(
|
||||
O.Of[string],
|
||||
Header(name).Set,
|
||||
ENDO.Of[func(*Builder) *Builder],
|
||||
)
|
||||
}
|
||||
|
||||
// WithoutHeader creates a [BuilderBuilder] to remove a certain header
|
||||
func WithoutHeader(name string) Endomorphism {
|
||||
return Header(name).Set(noHeader)
|
||||
}
|
||||
|
||||
// WithFormData creates a [BuilderBuilder] 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("application/x-www-form-urlencoded"),
|
||||
)
|
||||
}
|
||||
|
||||
// WithJson creates a [BuilderBuilder] 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("application/json"),
|
||||
)
|
||||
}
|
51
ioeither/http/builder/builder_test.go
Normal file
51
ioeither/http/builder/builder_test.go
Normal file
@@ -0,0 +1,51 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package builder
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
F "github.com/IBM/fp-go/function"
|
||||
O "github.com/IBM/fp-go/option"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestBuiler(t *testing.T) {
|
||||
|
||||
name := "Content-type"
|
||||
withContentType := WithHeader(name)
|
||||
withoutContentType := WithoutHeader(name)
|
||||
|
||||
b1 := F.Pipe1(
|
||||
Default,
|
||||
withContentType("application/json"),
|
||||
)
|
||||
|
||||
b2 := F.Pipe1(
|
||||
b1,
|
||||
withContentType("text/plain"),
|
||||
)
|
||||
|
||||
b3 := F.Pipe1(
|
||||
b2,
|
||||
withoutContentType,
|
||||
)
|
||||
|
||||
assert.Equal(t, O.None[string](), Default.GetHeader(name))
|
||||
assert.Equal(t, O.Of("application/json"), b1.GetHeader(name))
|
||||
assert.Equal(t, O.Of("text/plain"), b2.GetHeader(name))
|
||||
assert.Equal(t, O.None[string](), b3.GetHeader(name))
|
||||
}
|
32
ioeither/http/di.go
Normal file
32
ioeither/http/di.go
Normal file
@@ -0,0 +1,32 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package http
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
DI "github.com/IBM/fp-go/di"
|
||||
IOE "github.com/IBM/fp-go/ioeither"
|
||||
L "github.com/IBM/fp-go/lazy"
|
||||
)
|
||||
|
||||
var (
|
||||
// InjHttpClient is the injection token for the default http client
|
||||
InjHttpClient = DI.MakeTokenWithDefault0("HTTP_CLIENT", L.Of(IOE.Of[error](http.DefaultClient)))
|
||||
|
||||
// InjClient is the injection token for the default [Client]
|
||||
InjClient = DI.MakeTokenWithDefault1("CLIENT", InjHttpClient.IOEither(), IOE.Map[error](MakeClient))
|
||||
)
|
@@ -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,
|
||||
|
@@ -19,6 +19,7 @@ import (
|
||||
ET "github.com/IBM/fp-go/either"
|
||||
I "github.com/IBM/fp-go/io"
|
||||
G "github.com/IBM/fp-go/ioeither/generic"
|
||||
IOO "github.com/IBM/fp-go/iooption"
|
||||
L "github.com/IBM/fp-go/lazy"
|
||||
O "github.com/IBM/fp-go/option"
|
||||
)
|
||||
@@ -63,6 +64,10 @@ func FromOption[A, E any](onNone func() E) func(o O.Option[A]) IOEither[E, A] {
|
||||
return G.FromOption[IOEither[E, A]](onNone)
|
||||
}
|
||||
|
||||
func FromIOOption[A, E any](onNone func() E) func(o IOO.IOOption[A]) IOEither[E, A] {
|
||||
return G.FromIOOption[IOEither[E, A], IOO.IOOption[A]](onNone)
|
||||
}
|
||||
|
||||
func ChainOptionK[A, B, E any](onNone func() E) func(func(A) O.Option[B]) func(IOEither[E, A]) IOEither[E, B] {
|
||||
return G.ChainOptionK[IOEither[E, A], IOEither[E, B]](onNone)
|
||||
}
|
||||
@@ -192,12 +197,12 @@ func GetOrElse[E, A any](onLeft func(E) I.IO[A]) func(IOEither[E, A]) I.IO[A] {
|
||||
}
|
||||
|
||||
// MonadChainTo composes to the second monad ignoring the return value of the first
|
||||
func MonadChainTo[E, A, B any](fa IOEither[E, A], fb IOEither[E, B]) IOEither[E, B] {
|
||||
func MonadChainTo[A, E, B any](fa IOEither[E, A], fb IOEither[E, B]) IOEither[E, B] {
|
||||
return G.MonadChainTo(fa, fb)
|
||||
}
|
||||
|
||||
// ChainTo composes to the second [IOEither] monad ignoring the return value of the first
|
||||
func ChainTo[E, A, B any](fb IOEither[E, B]) func(IOEither[E, A]) IOEither[E, B] {
|
||||
func ChainTo[A, E, B any](fb IOEither[E, B]) func(IOEither[E, A]) IOEither[E, B] {
|
||||
return G.ChainTo[IOEither[E, A]](fb)
|
||||
}
|
||||
|
||||
@@ -266,3 +271,8 @@ func MonadFlap[E, B, A any](fab IOEither[E, func(A) B], a A) IOEither[E, B] {
|
||||
func Flap[E, B, A any](a A) func(IOEither[E, func(A) B]) IOEither[E, B] {
|
||||
return G.Flap[IOEither[E, func(A) B], IOEither[E, B]](a)
|
||||
}
|
||||
|
||||
// ToIOOption converts an [IOEither] to an [IOO.IOOption]
|
||||
func ToIOOption[E, A any](ioe IOEither[E, A]) IOO.IOOption[A] {
|
||||
return G.ToIOOption[IOO.IOOption[A]](ioe)
|
||||
}
|
||||
|
@@ -48,3 +48,63 @@ func TraverseRecordWithIndex[K comparable, E, A, B any](f func(K, A) IOEither[E,
|
||||
func SequenceRecord[K comparable, E, A any](ma map[K]IOEither[E, A]) IOEither[E, map[K]A] {
|
||||
return G.SequenceRecord[IOEither[E, A], IOEither[E, map[K]A]](ma)
|
||||
}
|
||||
|
||||
// TraverseArraySeq transforms an array
|
||||
func TraverseArraySeq[E, A, B any](f func(A) IOEither[E, B]) func([]A) IOEither[E, []B] {
|
||||
return G.TraverseArraySeq[IOEither[E, B], IOEither[E, []B], []A](f)
|
||||
}
|
||||
|
||||
// TraverseArrayWithIndexSeq transforms an array
|
||||
func TraverseArrayWithIndexSeq[E, A, B any](f func(int, A) IOEither[E, B]) func([]A) IOEither[E, []B] {
|
||||
return G.TraverseArrayWithIndexSeq[IOEither[E, B], IOEither[E, []B], []A](f)
|
||||
}
|
||||
|
||||
// SequenceArraySeq converts a homogeneous sequence of either into an either of sequence
|
||||
func SequenceArraySeq[E, A any](ma []IOEither[E, A]) IOEither[E, []A] {
|
||||
return G.SequenceArraySeq[IOEither[E, A], IOEither[E, []A]](ma)
|
||||
}
|
||||
|
||||
// TraverseRecordSeq transforms a record
|
||||
func TraverseRecordSeq[K comparable, E, A, B any](f func(A) IOEither[E, B]) func(map[K]A) IOEither[E, map[K]B] {
|
||||
return G.TraverseRecordSeq[IOEither[E, B], IOEither[E, map[K]B], map[K]A](f)
|
||||
}
|
||||
|
||||
// TraverseRecordWithIndexSeq transforms a record
|
||||
func TraverseRecordWithIndexSeq[K comparable, E, A, B any](f func(K, A) IOEither[E, B]) func(map[K]A) IOEither[E, map[K]B] {
|
||||
return G.TraverseRecordWithIndexSeq[IOEither[E, B], IOEither[E, map[K]B], map[K]A](f)
|
||||
}
|
||||
|
||||
// SequenceRecordSeq converts a homogeneous sequence of either into an either of sequence
|
||||
func SequenceRecordSeq[K comparable, E, A any](ma map[K]IOEither[E, A]) IOEither[E, map[K]A] {
|
||||
return G.SequenceRecordSeq[IOEither[E, A], IOEither[E, map[K]A]](ma)
|
||||
}
|
||||
|
||||
// TraverseArrayPar transforms an array
|
||||
func TraverseArrayPar[E, A, B any](f func(A) IOEither[E, B]) func([]A) IOEither[E, []B] {
|
||||
return G.TraverseArrayPar[IOEither[E, B], IOEither[E, []B], []A](f)
|
||||
}
|
||||
|
||||
// TraverseArrayWithIndexPar transforms an array
|
||||
func TraverseArrayWithIndexPar[E, A, B any](f func(int, A) IOEither[E, B]) func([]A) IOEither[E, []B] {
|
||||
return G.TraverseArrayWithIndexPar[IOEither[E, B], IOEither[E, []B], []A](f)
|
||||
}
|
||||
|
||||
// SequenceArrayPar converts a homogeneous Paruence of either into an either of Paruence
|
||||
func SequenceArrayPar[E, A any](ma []IOEither[E, A]) IOEither[E, []A] {
|
||||
return G.SequenceArrayPar[IOEither[E, A], IOEither[E, []A]](ma)
|
||||
}
|
||||
|
||||
// TraverseRecordPar transforms a record
|
||||
func TraverseRecordPar[K comparable, E, A, B any](f func(A) IOEither[E, B]) func(map[K]A) IOEither[E, map[K]B] {
|
||||
return G.TraverseRecordPar[IOEither[E, B], IOEither[E, map[K]B], map[K]A](f)
|
||||
}
|
||||
|
||||
// TraverseRecordWithIndexPar transforms a record
|
||||
func TraverseRecordWithIndexPar[K comparable, E, A, B any](f func(K, A) IOEither[E, B]) func(map[K]A) IOEither[E, map[K]B] {
|
||||
return G.TraverseRecordWithIndexPar[IOEither[E, B], IOEither[E, map[K]B], map[K]A](f)
|
||||
}
|
||||
|
||||
// SequenceRecordPar converts a homogeneous Paruence of either into an either of Paruence
|
||||
func SequenceRecordPar[K comparable, E, A any](ma map[K]IOEither[E, A]) IOEither[E, map[K]A] {
|
||||
return G.SequenceRecordPar[IOEither[E, A], IOEither[E, map[K]A]](ma)
|
||||
}
|
||||
|
@@ -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"
|
||||
@@ -64,13 +65,6 @@ func FromEither[GA ~func() O.Option[A], E, A any](e ET.Either[E, A]) GA {
|
||||
)
|
||||
}
|
||||
|
||||
func FromIOEither[GA ~func() O.Option[A], GEA ~func() ET.Either[E, A], E, A any](ioe GEA) GA {
|
||||
return F.Pipe1(
|
||||
ioe,
|
||||
IO.Map[GEA, GA](ET.ToOption[E, A]),
|
||||
)
|
||||
}
|
||||
|
||||
func MonadMap[GA ~func() O.Option[A], GB ~func() O.Option[B], A, B any](fa GA, f func(A) B) GB {
|
||||
return optiont.MonadMap(IO.MonadMap[GA, GB, O.Option[A], O.Option[B]], fa, f)
|
||||
}
|
||||
@@ -83,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 {
|
||||
@@ -194,3 +228,17 @@ func Fold[GA ~func() O.Option[A], GB ~func() B, A, B any](onNone func() GB, onSo
|
||||
func Defer[GA ~func() O.Option[A], A any](gen func() GA) GA {
|
||||
return IO.Defer[GA](gen)
|
||||
}
|
||||
|
||||
func MonadAlt[LAZY ~func() GIOA, GIOA ~func() O.Option[A], A any](first GIOA, second LAZY) GIOA {
|
||||
return optiont.MonadAlt(
|
||||
IO.Of[GIOA],
|
||||
IO.MonadChain[GIOA, GIOA],
|
||||
|
||||
first,
|
||||
second,
|
||||
)
|
||||
}
|
||||
|
||||
func Alt[LAZY ~func() GIOA, GIOA ~func() O.Option[A], A any](second LAZY) func(GIOA) GIOA {
|
||||
return F.Bind2nd(MonadAlt[LAZY], second)
|
||||
}
|
||||
|
31
iooption/generic/resource.go
Normal file
31
iooption/generic/resource.go
Normal file
@@ -0,0 +1,31 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package 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))
|
||||
}
|
@@ -18,8 +18,9 @@ package iooption
|
||||
import (
|
||||
ET "github.com/IBM/fp-go/either"
|
||||
I "github.com/IBM/fp-go/io"
|
||||
IOE "github.com/IBM/fp-go/ioeither"
|
||||
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"
|
||||
)
|
||||
|
||||
@@ -129,12 +130,37 @@ func Defer[A any](gen func() IOOption[A]) IOOption[A] {
|
||||
return G.Defer[IOOption[A]](gen)
|
||||
}
|
||||
|
||||
// FromIOEither converts an [IOEither] into an [IOOption]
|
||||
func FromIOEither[E, A any](ioe IOE.IOEither[E, A]) IOOption[A] {
|
||||
return G.FromIOEither[IOOption[A]](ioe)
|
||||
}
|
||||
|
||||
// FromEither converts an [Either] into an [IOOption]
|
||||
func FromEither[E, A any](e ET.Either[E, A]) IOOption[A] {
|
||||
return G.FromEither[IOOption[A]](e)
|
||||
}
|
||||
|
||||
// MonadAlt identifies an associative operation on a type constructor
|
||||
func MonadAlt[A any](first IOOption[A], second L.Lazy[IOOption[A]]) IOOption[A] {
|
||||
return G.MonadAlt(first, second)
|
||||
}
|
||||
|
||||
// Alt identifies an associative operation on a type constructor
|
||||
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
27
iooption/resource.go
Normal 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)
|
||||
}
|
@@ -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)
|
||||
|
39
optics/lens/array/generic/head.go
Normal file
39
optics/lens/array/generic/head.go
Normal 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
30
optics/lens/array/head.go
Normal file
@@ -0,0 +1,30 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package 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]()
|
||||
}
|
40
optics/lens/array/head_test.go
Normal file
40
optics/lens/array/head_test.go
Normal 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")))
|
||||
}
|
@@ -65,7 +65,7 @@ func SequenceArray[A any](ma []Option[A]) Option[[]A] {
|
||||
func CompactArrayG[A1 ~[]Option[A], A2 ~[]A, A any](fa A1) A2 {
|
||||
return RA.Reduce(fa, func(out A2, value Option[A]) A2 {
|
||||
return MonadFold(value, F.Constant(out), F.Bind1st(RA.Append[A2, A], out))
|
||||
}, make(A2, len(fa)))
|
||||
}, make(A2, 0, len(fa)))
|
||||
}
|
||||
|
||||
// CompactArray discards the none values and keeps the some values
|
||||
|
@@ -34,3 +34,14 @@ func TestSequenceArray(t *testing.T) {
|
||||
|
||||
assert.Equal(t, res, Of([]int{1, 2}))
|
||||
}
|
||||
|
||||
func TestCompactArray(t *testing.T) {
|
||||
ar := []Option[string]{
|
||||
Of("ok"),
|
||||
None[string](),
|
||||
Of("ok"),
|
||||
}
|
||||
|
||||
res := CompactArray(ar)
|
||||
assert.Equal(t, 2, len(res))
|
||||
}
|
||||
|
@@ -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)
|
||||
}
|
||||
|
@@ -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)
|
||||
}
|
||||
|
@@ -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)
|
||||
}
|
||||
|
@@ -52,7 +52,7 @@ func TestIOEitherToOption2(t *testing.T) {
|
||||
content,
|
||||
IOEF.WriteFile(filepath.Join(tmpDir, "test.txt"), os.ModePerm),
|
||||
IOE.Swap[error, []byte],
|
||||
IOO.FromIOEither[[]byte, error],
|
||||
IOE.ToIOOption[[]byte, error],
|
||||
)
|
||||
|
||||
fmt.Println(resIOO())
|
||||
|
Reference in New Issue
Block a user