1
0
mirror of https://github.com/IBM/fp-go.git synced 2025-08-10 22:31:32 +02:00

fix: rework http support

Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
This commit is contained in:
Dr. Carsten Leue
2023-07-16 22:46:18 +02:00
parent bf04dad87c
commit 48f38f2e43
20 changed files with 905 additions and 31 deletions

10
record/eq.go Normal file
View File

@@ -0,0 +1,10 @@
package record
import (
E "github.com/ibm/fp-go/eq"
G "github.com/ibm/fp-go/record/generic"
)
func Eq[K comparable, V any](e E.Eq[V]) E.Eq[map[K]V] {
return G.Eq[map[K]V, K, V](e)
}

24
record/generic/eq.go Normal file
View File

@@ -0,0 +1,24 @@
package generic
import (
E "github.com/ibm/fp-go/eq"
)
func equals[M ~map[K]V, K comparable, V any](left, right M, eq func(V, V) bool) bool {
if len(left) != len(right) {
return false
}
for k, v1 := range left {
if v2, ok := right[k]; !ok || !eq(v1, v2) {
return false
}
}
return true
}
func Eq[M ~map[K]V, K comparable, V any](e E.Eq[V]) E.Eq[M] {
eq := e.Equals
return E.FromEquals(func(left, right M) bool {
return equals(left, right, eq)
})
}

13
record/generic/monoid.go Normal file
View File

@@ -0,0 +1,13 @@
package generic
import (
M "github.com/ibm/fp-go/monoid"
S "github.com/ibm/fp-go/semigroup"
)
func UnionMonoid[N ~map[K]V, K comparable, V any](s S.Semigroup[V]) M.Monoid[N] {
return M.MakeMonoid(
UnionSemigroup[N](s).Concat,
Empty[N](),
)
}

270
record/generic/record.go Normal file
View File

@@ -0,0 +1,270 @@
package generic
import (
F "github.com/ibm/fp-go/function"
G "github.com/ibm/fp-go/internal/record"
Mg "github.com/ibm/fp-go/magma"
O "github.com/ibm/fp-go/option"
T "github.com/ibm/fp-go/tuple"
)
func IsEmpty[M ~map[K]V, K comparable, V any](r M) bool {
return len(r) == 0
}
func IsNonEmpty[M ~map[K]V, K comparable, V any](r M) bool {
return len(r) > 0
}
func Keys[M ~map[K]V, GK ~[]K, K comparable, V any](r M) GK {
return collect[M, GK](r, F.First[K, V])
}
func Values[M ~map[K]V, GV ~[]V, K comparable, V any](r M) GV {
return collect[M, GV](r, F.Second[K, V])
}
func collect[M ~map[K]V, GR ~[]R, K comparable, V, R any](r M, f func(K, V) R) GR {
count := len(r)
result := make(GR, count)
idx := 0
for k, v := range r {
result[idx] = f(k, v)
idx++
}
return result
}
func Collect[M ~map[K]V, GR ~[]R, K comparable, V, R any](f func(K, V) R) func(M) GR {
return F.Bind2nd(collect[M, GR, K, V, R], f)
}
func Reduce[M ~map[K]V, K comparable, V, R any](f func(R, V) R, initial R) func(M) R {
return func(r M) R {
return G.Reduce(r, f, initial)
}
}
func ReduceWithIndex[M ~map[K]V, K comparable, V, R any](f func(K, R, V) R, initial R) func(M) R {
return func(r M) R {
return G.ReduceWithIndex(r, f, initial)
}
}
func ReduceRef[M ~map[K]V, K comparable, V, R any](f func(R, *V) R, initial R) func(M) R {
return func(r M) R {
return G.ReduceRef(r, f, initial)
}
}
func ReduceRefWithIndex[M ~map[K]V, K comparable, V, R any](f func(K, R, *V) R, initial R) func(M) R {
return func(r M) R {
return G.ReduceRefWithIndex(r, f, initial)
}
}
func MonadMap[M ~map[K]V, N ~map[K]R, K comparable, V, R any](r M, f func(V) R) N {
return MonadMapWithIndex[M, N](r, F.Ignore1of2[K](f))
}
func MonadMapWithIndex[M ~map[K]V, N ~map[K]R, K comparable, V, R any](r M, f func(K, V) R) N {
return G.ReduceWithIndex(r, func(k K, dst N, v V) N {
return upsertAtReadWrite(dst, k, f(k, v))
}, make(N, len(r)))
}
func MonadMapRefWithIndex[M ~map[K]V, N ~map[K]R, K comparable, V, R any](r M, f func(K, *V) R) N {
return G.ReduceRefWithIndex(r, func(k K, dst N, v *V) N {
return upsertAtReadWrite(dst, k, f(k, v))
}, make(N, len(r)))
}
func MonadMapRef[M ~map[K]V, N ~map[K]R, K comparable, V, R any](r M, f func(*V) R) N {
return MonadMapRefWithIndex[M, N](r, F.Ignore1of2[K](f))
}
func Map[M ~map[K]V, N ~map[K]R, K comparable, V, R any](f func(V) R) func(M) N {
return F.Bind2nd(MonadMap[M, N, K, V, R], f)
}
func MapRef[M ~map[K]V, N ~map[K]R, K comparable, V, R any](f func(*V) R) func(M) N {
return F.Bind2nd(MonadMapRef[M, N, K, V, R], f)
}
func MapWithIndex[M ~map[K]V, N ~map[K]R, K comparable, V, R any](f func(K, V) R) func(M) N {
return F.Bind2nd(MonadMapWithIndex[M, N, K, V, R], f)
}
func MapRefWithIndex[M ~map[K]V, N ~map[K]R, K comparable, V, R any](f func(K, *V) R) func(M) N {
return F.Bind2nd(MonadMapRefWithIndex[M, N, K, V, R], f)
}
func lookup[M ~map[K]V, K comparable, V any](r M, k K) O.Option[V] {
if val, ok := r[k]; ok {
return O.Some(val)
}
return O.None[V]()
}
func Lookup[M ~map[K]V, K comparable, V any](k K) func(M) O.Option[V] {
return F.Bind2nd(lookup[M, K, V], k)
}
func Has[M ~map[K]V, K comparable, V any](k K, r M) bool {
_, ok := r[k]
return ok
}
func union[M ~map[K]V, K comparable, V any](m Mg.Magma[V], left M, right M) M {
lenLeft := len(left)
if lenLeft == 0 {
return right
}
lenRight := len(right)
if lenRight == 0 {
return left
}
result := make(M, lenLeft+lenRight)
for k, v := range left {
if val, ok := right[k]; ok {
result[k] = m.Concat(v, val)
} else {
result[k] = v
}
}
for k, v := range right {
if _, ok := left[k]; !ok {
result[k] = v
}
}
return result
}
func Union[M ~map[K]V, K comparable, V any](m Mg.Magma[V]) func(M) func(M) M {
return func(right M) func(M) M {
return func(left M) M {
return union(m, left, right)
}
}
}
func Empty[M ~map[K]V, K comparable, V any]() M {
return make(M)
}
func Size[M ~map[K]V, K comparable, V any](r M) int {
return len(r)
}
func ToArray[M ~map[K]V, GT ~[]T.Tuple2[K, V], K comparable, V any](r M) GT {
return collect[M, GT](r, T.MakeTuple2[K, V])
}
func ToEntries[M ~map[K]V, GT ~[]T.Tuple2[K, V], K comparable, V any](r M) GT {
return ToArray[M, GT](r)
}
func FromEntries[M ~map[K]V, GT ~[]T.Tuple2[K, V], K comparable, V any](fa GT) M {
m := make(M)
for _, t := range fa {
upsertAtReadWrite(m, t.F1, t.F2)
}
return m
}
func duplicate[M ~map[K]V, K comparable, V any](r M) M {
return MonadMap[M, M](r, F.Identity[V])
}
func upsertAt[M ~map[K]V, K comparable, V any](r M, k K, v V) M {
dup := duplicate(r)
dup[k] = v
return dup
}
func deleteAt[M ~map[K]V, K comparable, V any](r M, k K) M {
dup := duplicate(r)
delete(dup, k)
return dup
}
func upsertAtReadWrite[M ~map[K]V, K comparable, V any](r M, k K, v V) M {
r[k] = v
return r
}
func UpsertAt[M ~map[K]V, K comparable, V any](k K, v V) func(M) M {
return func(ma M) M {
return upsertAt(ma, k, v)
}
}
func DeleteAt[M ~map[K]V, K comparable, V any](k K) func(M) M {
return F.Bind2nd(deleteAt[M, K, V], k)
}
func Singleton[M ~map[K]V, K comparable, V any](k K, v V) M {
return M{k: v}
}
func filterMapWithIndex[M ~map[K]V1, N ~map[K]V2, K comparable, V1, V2 any](fa M, f func(K, V1) O.Option[V2]) N {
return G.ReduceWithIndex(fa, func(key K, n N, value V1) N {
return O.MonadFold(f(key, value), F.Constant(n), func(v V2) N {
return upsertAtReadWrite(n, key, v)
})
}, make(N))
}
func filterWithIndex[M ~map[K]V, K comparable, V any](fa M, f func(K, V) bool) M {
return filterMapWithIndex[M, M](fa, func(k K, v V) O.Option[V] {
if f(k, v) {
return O.Of(v)
}
return O.None[V]()
})
}
func filter[M ~map[K]V, K comparable, V any](fa M, f func(K) bool) M {
return filterWithIndex(fa, F.Ignore2of2[V](f))
}
// Filter creates a new map with only the elements that match the predicate
func Filter[M ~map[K]V, K comparable, V any](f func(K) bool) func(M) M {
return F.Bind2nd(filter[M, K, V], f)
}
// FilterWithIndex creates a new map with only the elements that match the predicate
func FilterWithIndex[M ~map[K]V, K comparable, V any](f func(K, V) bool) func(M) M {
return F.Bind2nd(filterWithIndex[M, K, V], f)
}
// FilterMapWithIndex creates a new map with only the elements for which the transformation function creates a Some
func FilterMapWithIndex[M ~map[K]V1, N ~map[K]V2, K comparable, V1, V2 any](f func(K, V1) O.Option[V2]) func(M) N {
return F.Bind2nd(filterMapWithIndex[M, N, K, V1, V2], f)
}
// FilterMap creates a new map with only the elements for which the transformation function creates a Some
func FilterMap[M ~map[K]V1, N ~map[K]V2, K comparable, V1, V2 any](f func(V1) O.Option[V2]) func(M) N {
return F.Bind2nd(filterMapWithIndex[M, N, K, V1, V2], F.Ignore1of2[K](f))
}
// IsNil checks if the map is set to nil
func IsNil[M ~map[K]V, K comparable, V any](m M) bool {
return m == nil
}
// IsNonNil checks if the map is set to nil
func IsNonNil[M ~map[K]V, K comparable, V any](m M) bool {
return m != nil
}
// ConstNil return a nil map
func ConstNil[M ~map[K]V, K comparable, V any]() M {
return (M)(nil)
}

View File

@@ -0,0 +1,12 @@
package generic
import (
S "github.com/ibm/fp-go/semigroup"
)
func UnionSemigroup[N ~map[K]V, K comparable, V any](s S.Semigroup[V]) S.Semigroup[N] {
union := Union[N, K, V](s)
return S.MakeSemigroup(func(first N, second N) N {
return union(second)(first)
})
}

11
record/monoid.go Normal file
View File

@@ -0,0 +1,11 @@
package record
import (
M "github.com/ibm/fp-go/monoid"
G "github.com/ibm/fp-go/record/generic"
S "github.com/ibm/fp-go/semigroup"
)
func UnionMonoid[K comparable, V any](s S.Semigroup[V]) M.Monoid[map[K]V] {
return G.UnionMonoid[map[K]V](s)
}

41
record/monoid_test.go Normal file
View File

@@ -0,0 +1,41 @@
package record
import (
"testing"
S "github.com/ibm/fp-go/string"
"github.com/stretchr/testify/assert"
)
func TestUnionMonoid(t *testing.T) {
m := UnionMonoid[string](S.Semigroup())
e := Empty[string, string]()
x := map[string]string{
"a": "a1",
"b": "b1",
"c": "c1",
}
y := map[string]string{
"b": "b2",
"c": "c2",
"d": "d2",
}
res := map[string]string{
"a": "a1",
"b": "b1b2",
"c": "c1c2",
"d": "d2",
}
assert.Equal(t, x, m.Concat(x, m.Empty()))
assert.Equal(t, x, m.Concat(m.Empty(), x))
assert.Equal(t, x, m.Concat(x, e))
assert.Equal(t, x, m.Concat(e, x))
assert.Equal(t, res, m.Concat(x, y))
}

155
record/record.go Normal file
View File

@@ -0,0 +1,155 @@
package record
import (
Mg "github.com/ibm/fp-go/magma"
O "github.com/ibm/fp-go/option"
G "github.com/ibm/fp-go/record/generic"
T "github.com/ibm/fp-go/tuple"
)
func IsEmpty[K comparable, V any](r map[K]V) bool {
return G.IsEmpty(r)
}
func IsNonEmpty[K comparable, V any](r map[K]V) bool {
return G.IsNonEmpty(r)
}
func Keys[K comparable, V any](r map[K]V) []K {
return G.Keys[map[K]V, []K](r)
}
func Values[K comparable, V any](r map[K]V) []V {
return G.Values[map[K]V, []V](r)
}
func Collect[K comparable, V, R any](f func(K, V) R) func(map[K]V) []R {
return G.Collect[map[K]V, []R](f)
}
func Reduce[K comparable, V, R any](f func(R, V) R, initial R) func(map[K]V) R {
return G.Reduce[map[K]V](f, initial)
}
func ReduceWithIndex[K comparable, V, R any](f func(K, R, V) R, initial R) func(map[K]V) R {
return G.ReduceWithIndex[map[K]V](f, initial)
}
func ReduceRef[K comparable, V, R any](f func(R, *V) R, initial R) func(map[K]V) R {
return G.ReduceRef[map[K]V](f, initial)
}
func ReduceRefWithIndex[K comparable, V, R any](f func(K, R, *V) R, initial R) func(map[K]V) R {
return G.ReduceRefWithIndex[map[K]V](f, initial)
}
func MonadMap[K comparable, V, R any](r map[K]V, f func(V) R) map[K]R {
return G.MonadMap[map[K]V, map[K]R](r, f)
}
func MonadMapWithIndex[K comparable, V, R any](r map[K]V, f func(K, V) R) map[K]R {
return G.MonadMapWithIndex[map[K]V, map[K]R](r, f)
}
func MonadMapRefWithIndex[K comparable, V, R any](r map[K]V, f func(K, *V) R) map[K]R {
return G.MonadMapRefWithIndex[map[K]V, map[K]R](r, f)
}
func MonadMapRef[K comparable, V, R any](r map[K]V, f func(*V) R) map[K]R {
return G.MonadMapRef[map[K]V, map[K]R](r, f)
}
func Map[K comparable, V, R any](f func(V) R) func(map[K]V) map[K]R {
return G.Map[map[K]V, map[K]R](f)
}
func MapRef[K comparable, V, R any](f func(*V) R) func(map[K]V) map[K]R {
return G.MapRef[map[K]V, map[K]R](f)
}
func MapWithIndex[K comparable, V, R any](f func(K, V) R) func(map[K]V) map[K]R {
return G.MapWithIndex[map[K]V, map[K]R](f)
}
func MapRefWithIndex[K comparable, V, R any](f func(K, *V) R) func(map[K]V) map[K]R {
return G.MapRefWithIndex[map[K]V, map[K]R](f)
}
func Lookup[K comparable, V any](k K) func(map[K]V) O.Option[V] {
return G.Lookup[map[K]V](k)
}
func Has[K comparable, V any](k K, r map[K]V) bool {
return G.Has(k, r)
}
func Union[K comparable, V any](m Mg.Magma[V]) func(map[K]V) func(map[K]V) map[K]V {
return G.Union[map[K]V](m)
}
func Empty[K comparable, V any]() map[K]V {
return G.Empty[map[K]V]()
}
func Size[K comparable, V any](r map[K]V) int {
return G.Size(r)
}
func ToArray[K comparable, V any](r map[K]V) []T.Tuple2[K, V] {
return G.ToArray[map[K]V, []T.Tuple2[K, V]](r)
}
func ToEntries[K comparable, V any](r map[K]V) []T.Tuple2[K, V] {
return G.ToEntries[map[K]V, []T.Tuple2[K, V]](r)
}
func FromEntries[K comparable, V any](fa []T.Tuple2[K, V]) map[K]V {
return G.FromEntries[map[K]V](fa)
}
func UpsertAt[K comparable, V any](k K, v V) func(map[K]V) map[K]V {
return G.UpsertAt[map[K]V](k, v)
}
func DeleteAt[K comparable, V any](k K) func(map[K]V) map[K]V {
return G.DeleteAt[map[K]V](k)
}
func Singleton[K comparable, V any](k K, v V) map[K]V {
return G.Singleton[map[K]V](k, v)
}
// FilterMapWithIndex creates a new map with only the elements for which the transformation function creates a Some
func FilterMapWithIndex[K comparable, V1, V2 any](f func(K, V1) O.Option[V2]) func(map[K]V1) map[K]V2 {
return G.FilterMapWithIndex[map[K]V1, map[K]V2](f)
}
// FilterMap creates a new map with only the elements for which the transformation function creates a Some
func FilterMap[K comparable, V1, V2 any](f func(V1) O.Option[V2]) func(map[K]V1) map[K]V2 {
return G.FilterMap[map[K]V1, map[K]V2](f)
}
// Filter creates a new map with only the elements that match the predicate
func Filter[K comparable, V any](f func(K) bool) func(map[K]V) map[K]V {
return G.Filter[map[K]V](f)
}
// FilterWithIndex creates a new map with only the elements that match the predicate
func FilterWithIndex[K comparable, V any](f func(K, V) bool) func(map[K]V) map[K]V {
return G.FilterWithIndex[map[K]V](f)
}
// IsNil checks if the map is set to nil
func IsNil[K comparable, V any](m map[K]V) bool {
return G.IsNil(m)
}
// IsNonNil checks if the map is set to nil
func IsNonNil[K comparable, V any](m map[K]V) bool {
return G.IsNonNil(m)
}
// ConstNil return a nil map
func ConstNil[K comparable, V any]() map[K]V {
return (map[K]V)(nil)
}

58
record/record_test.go Normal file
View File

@@ -0,0 +1,58 @@
package record
import (
"sort"
"testing"
"github.com/ibm/fp-go/internal/utils"
O "github.com/ibm/fp-go/option"
"github.com/stretchr/testify/assert"
)
func TestKeys(t *testing.T) {
data := map[string]string{
"a": "A",
"b": "B",
"c": "C",
}
keys := Keys(data)
sort.Strings(keys)
assert.Equal(t, []string{"a", "b", "c"}, keys)
}
func TestValues(t *testing.T) {
data := map[string]string{
"a": "A",
"b": "B",
"c": "C",
}
keys := Values(data)
sort.Strings(keys)
assert.Equal(t, []string{"A", "B", "C"}, keys)
}
func TestMap(t *testing.T) {
data := map[string]string{
"a": "a",
"b": "b",
"c": "c",
}
expected := map[string]string{
"a": "A",
"b": "B",
"c": "C",
}
assert.Equal(t, expected, Map[string](utils.Upper)(data))
}
func TestLookup(t *testing.T) {
data := map[string]string{
"a": "a",
"b": "b",
"c": "c",
}
assert.Equal(t, O.Some("a"), Lookup[string, string]("a")(data))
assert.Equal(t, O.None[string](), Lookup[string, string]("a1")(data))
}

10
record/semigroup.go Normal file
View File

@@ -0,0 +1,10 @@
package record
import (
G "github.com/ibm/fp-go/record/generic"
S "github.com/ibm/fp-go/semigroup"
)
func UnionSemigroup[K comparable, V any](s S.Semigroup[V]) S.Semigroup[map[K]V] {
return G.UnionSemigroup[map[K]V](s)
}

38
record/traverse.go Normal file
View File

@@ -0,0 +1,38 @@
package record
import (
G "github.com/ibm/fp-go/internal/record"
)
func TraverseWithIndex[K comparable, A, B, HKTB, HKTAB, HKTRB any](
fof func(map[K]B) HKTRB,
fmap func(func(map[K]B) func(B) map[K]B) func(HKTRB) HKTAB,
fap func(HKTB) func(HKTAB) HKTRB,
f func(K, A) HKTB) func(map[K]A) HKTRB {
return G.TraverseWithIndex[map[K]A](fof, fmap, fap, f)
}
// HKTA = HKT<A>
// HKTB = HKT<B>
// HKTAB = HKT<func(A)B>
// HKTRB = HKT<map[K]B>
func Traverse[K comparable, A, B, HKTB, HKTAB, HKTRB any](
fof func(map[K]B) HKTRB,
fmap func(func(map[K]B) func(B) map[K]B) func(HKTRB) HKTAB,
fap func(HKTB) func(HKTAB) HKTRB,
f func(A) HKTB) func(map[K]A) HKTRB {
return G.Traverse[map[K]A](fof, fmap, fap, f)
}
// HKTA = HKT[A]
// HKTAA = HKT[func(A)map[K]A]
// HKTRA = HKT[map[K]A]
func Sequence[K comparable, A, HKTA, HKTAA, HKTRA any](
fof func(map[K]A) HKTRA,
fmap func(func(map[K]A) func(A) map[K]A) func(HKTRA) HKTAA,
fap func(HKTA) func(HKTAA) HKTRA,
ma map[K]HKTA) HKTRA {
return G.Sequence(fof, fmap, fap, ma)
}

75
record/traverse_test.go Normal file
View File

@@ -0,0 +1,75 @@
package record
import (
"testing"
F "github.com/ibm/fp-go/function"
O "github.com/ibm/fp-go/option"
"github.com/stretchr/testify/assert"
)
type MapType = map[string]int
type MapTypeString = map[string]string
type MapTypeO = map[string]O.Option[int]
func TestSimpleTraversalWithIndex(t *testing.T) {
f := func(k string, n int) O.Option[int] {
if k != "a" {
return O.Some(n)
}
return O.None[int]()
}
tWithIndex := TraverseWithIndex(
O.Of[MapType],
O.Map[MapType, func(int) MapType],
O.Ap[MapType, int],
f)
assert.Equal(t, O.None[MapType](), F.Pipe1(MapType{"a": 1, "b": 2}, tWithIndex))
assert.Equal(t, O.Some(MapType{"b": 2}), F.Pipe1(MapType{"b": 2}, tWithIndex))
}
func TestSimpleTraversalNoIndex(t *testing.T) {
f := func(k string) O.Option[string] {
if k != "1" {
return O.Some(k)
}
return O.None[string]()
}
tWithoutIndex := Traverse(
O.Of[MapTypeString],
O.Map[MapTypeString, func(string) MapTypeString],
O.Ap[MapTypeString, string],
f)
assert.Equal(t, O.None[MapTypeString](), F.Pipe1(MapTypeString{"a": "1", "b": "2"}, tWithoutIndex))
assert.Equal(t, O.Some(MapTypeString{"b": "2"}), F.Pipe1(MapTypeString{"b": "2"}, tWithoutIndex))
}
func TestSequence(t *testing.T) {
// source map
simpleMapO := MapTypeO{"a": O.Of(1), "b": O.Of(2)}
// convert to an option of record
s := Traverse(
O.Of[MapType],
O.Map[MapType, func(int) MapType],
O.Ap[MapType, int],
F.Identity[O.Option[int]],
)
assert.Equal(t, O.Of(MapType{"a": 1, "b": 2}), F.Pipe1(simpleMapO, s))
s1 := Sequence(
O.Of[MapType],
O.Map[MapType, func(int) MapType],
O.Ap[MapType, int],
simpleMapO,
)
assert.Equal(t, O.Of(MapType{"a": 1, "b": 2}), s1)
}