1
0
mirror of https://github.com/IBM/fp-go.git synced 2025-12-19 23:42:05 +02:00

Compare commits

...

1 Commits

Author SHA1 Message Date
Dr. Carsten Leue
d3c466bfb7 fix: some cleanup
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2025-12-19 13:18:49 +01:00
8 changed files with 552 additions and 161 deletions

View File

@@ -16,7 +16,6 @@
package record package record
import ( import (
Mo "github.com/IBM/fp-go/v2/monoid"
G "github.com/IBM/fp-go/v2/record/generic" G "github.com/IBM/fp-go/v2/record/generic"
) )
@@ -30,8 +29,8 @@ import (
// Count int // Count int
// } // }
// result := record.Do[string, State]() // result := record.Do[string, State]()
func Do[K comparable, S any]() map[K]S { func Do[K comparable, S any]() Record[K, S] {
return G.Do[map[K]S]() return G.Do[Record[K, S]]()
} }
// Bind attaches the result of a computation to a context [S1] to produce a context [S2]. // Bind attaches the result of a computation to a context [S1] to produce a context [S2].
@@ -68,29 +67,87 @@ func Do[K comparable, S any]() map[K]S {
// }, // },
// ), // ),
// ) // )
func Bind[S1, T any, K comparable, S2 any](m Mo.Monoid[map[K]S2]) func(setter func(T) func(S1) S2, f func(S1) map[K]T) func(map[K]S1) map[K]S2 { func Bind[S1, T any, K comparable, S2 any](m Monoid[Record[K, S2]]) func(
return G.Bind[map[K]S1, map[K]S2, map[K]T](m) setter func(T) func(S1) S2,
f Kleisli[K, S1, T],
) Operator[K, S1, S2] {
return G.Bind[Record[K, S1], Record[K, S2], Record[K, T]](m)
} }
// Let attaches the result of a computation to a context [S1] to produce a context [S2] // Let attaches the result of a computation to a context [S1] to produce a context [S2].
// Unlike Bind, Let does not require a Monoid because it transforms each value independently
// without merging multiple maps.
//
// The setter function takes the computed value and returns a function that updates the context.
// The computation function f takes the current context and produces a value.
//
// Example:
//
// type State struct {
// Name string
// Length int
// }
//
// result := F.Pipe2(
// map[string]State{"a": {Name: "Alice"}},
// record.Let(
// func(length int) func(State) State {
// return func(s State) State { s.Length = length; return s }
// },
// func(s State) int { return len(s.Name) },
// ),
// ) // map[string]State{"a": {Name: "Alice", Length: 5}}
func Let[S1, T any, K comparable, S2 any]( func Let[S1, T any, K comparable, S2 any](
setter func(T) func(S1) S2, setter func(T) func(S1) S2,
f func(S1) T, f func(S1) T,
) func(map[K]S1) map[K]S2 { ) Operator[K, S1, S2] {
return G.Let[map[K]S1, map[K]S2](setter, f) return G.Let[Record[K, S1], Record[K, S2]](setter, f)
} }
// LetTo attaches the a value to a context [S1] to produce a context [S2] // LetTo attaches a constant value to a context [S1] to produce a context [S2].
// This is similar to Let but uses a fixed value instead of computing it from the context.
//
// The setter function takes the value and returns a function that updates the context.
//
// Example:
//
// type State struct {
// Name string
// Version int
// }
//
// result := F.Pipe2(
// map[string]State{"a": {Name: "Alice"}},
// record.LetTo(
// func(version int) func(State) State {
// return func(s State) State { s.Version = version; return s }
// },
// 2,
// ),
// ) // map[string]State{"a": {Name: "Alice", Version: 2}}
func LetTo[S1, T any, K comparable, S2 any]( func LetTo[S1, T any, K comparable, S2 any](
setter func(T) func(S1) S2, setter func(T) func(S1) S2,
b T, b T,
) func(map[K]S1) map[K]S2 { ) Operator[K, S1, S2] {
return G.LetTo[map[K]S1, map[K]S2](setter, b) return G.LetTo[Record[K, S1], Record[K, S2]](setter, b)
} }
// BindTo initializes a new state [S1] from a value [T] // BindTo initializes a new state [S1] from a value [T].
func BindTo[S1, T any, K comparable](setter func(T) S1) func(map[K]T) map[K]S1 { // This is typically used as the first step in a do-notation chain to convert
return G.BindTo[map[K]S1, map[K]T](setter) // a simple map of values into a map of state objects.
//
// Example:
//
// type State struct {
// Name string
// }
//
// result := F.Pipe1(
// map[string]string{"a": "Alice", "b": "Bob"},
// record.BindTo(func(name string) State { return State{Name: name} }),
// ) // map[string]State{"a": {Name: "Alice"}, "b": {Name: "Bob"}}
func BindTo[S1, T any, K comparable](setter func(T) S1) Operator[K, T, S1] {
return G.BindTo[Record[K, S1], Record[K, T]](setter)
} }
// ApS attaches a value to a context [S1] to produce a context [S2] by considering // ApS attaches a value to a context [S1] to produce a context [S2] by considering
@@ -126,6 +183,9 @@ func BindTo[S1, T any, K comparable](setter func(T) S1) func(map[K]T) map[K]S1 {
// counts, // counts,
// ), // ),
// ) // map[string]State{"a": {Name: "Alice", Count: 10}, "b": {Name: "Bob", Count: 20}} // ) // map[string]State{"a": {Name: "Alice", Count: 10}, "b": {Name: "Bob", Count: 20}}
func ApS[S1, T any, K comparable, S2 any](m Mo.Monoid[map[K]S2]) func(setter func(T) func(S1) S2, fa map[K]T) func(map[K]S1) map[K]S2 { func ApS[S1, T any, K comparable, S2 any](m Monoid[Record[K, S2]]) func(
return G.ApS[map[K]S1, map[K]S2, map[K]T](m) setter func(T) func(S1) S2,
fa Record[K, T],
) Operator[K, S1, S2] {
return G.ApS[Record[K, S1], Record[K, S2], Record[K, T]](m)
} }

227
v2/record/bind_test.go Normal file
View File

@@ -0,0 +1,227 @@
// Copyright (c) 2023 - 2025 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 record
import (
"testing"
F "github.com/IBM/fp-go/v2/function"
"github.com/stretchr/testify/assert"
)
type TestState struct {
Name string
Count int
Version int
}
func TestDo(t *testing.T) {
result := Do[string, TestState]()
assert.NotNil(t, result)
assert.Empty(t, result)
assert.Equal(t, map[string]TestState{}, result)
}
func TestBindTo(t *testing.T) {
input := map[string]string{"a": "Alice", "b": "Bob"}
result := F.Pipe1(
input,
BindTo[TestState, string, string](func(name string) TestState {
return TestState{Name: name}
}),
)
expected := map[string]TestState{
"a": {Name: "Alice"},
"b": {Name: "Bob"},
}
assert.Equal(t, expected, result)
}
func TestLet(t *testing.T) {
input := map[string]TestState{
"a": {Name: "Alice"},
"b": {Name: "Bob"},
}
result := F.Pipe1(
input,
Let[TestState, int, string, TestState](
func(length int) func(TestState) TestState {
return func(s TestState) TestState {
s.Count = length
return s
}
},
func(s TestState) int {
return len(s.Name)
},
),
)
expected := map[string]TestState{
"a": {Name: "Alice", Count: 5},
"b": {Name: "Bob", Count: 3},
}
assert.Equal(t, expected, result)
}
func TestLetTo(t *testing.T) {
input := map[string]TestState{
"a": {Name: "Alice"},
"b": {Name: "Bob"},
}
result := F.Pipe1(
input,
LetTo[TestState, int, string, TestState](
func(version int) func(TestState) TestState {
return func(s TestState) TestState {
s.Version = version
return s
}
},
2,
),
)
expected := map[string]TestState{
"a": {Name: "Alice", Version: 2},
"b": {Name: "Bob", Version: 2},
}
assert.Equal(t, expected, result)
}
func TestBind(t *testing.T) {
monoid := MergeMonoid[string, TestState]()
// Bind chains computations where each step can depend on previous results
result := F.Pipe1(
map[string]string{"x": "test"},
Bind[string, int](monoid)(
func(length int) func(string) TestState {
return func(s string) TestState {
return TestState{Name: s, Count: length}
}
},
func(s string) map[string]int {
return map[string]int{"x": len(s)}
},
),
)
expected := map[string]TestState{
"x": {Name: "test", Count: 4},
}
assert.Equal(t, expected, result)
}
func TestApS(t *testing.T) {
monoid := MergeMonoid[string, TestState]()
// ApS applies independent computations
names := map[string]string{"x": "Alice"}
counts := map[string]int{"x": 10}
result := F.Pipe2(
map[string]TestState{"x": {}},
ApS[TestState, string](monoid)(
func(name string) func(TestState) TestState {
return func(s TestState) TestState {
s.Name = name
return s
}
},
names,
),
ApS[TestState, int](monoid)(
func(count int) func(TestState) TestState {
return func(s TestState) TestState {
s.Count = count
return s
}
},
counts,
),
)
expected := map[string]TestState{
"x": {Name: "Alice", Count: 10},
}
assert.Equal(t, expected, result)
}
func TestBindChain(t *testing.T) {
// Test a complete do-notation chain with BindTo, Let, and LetTo
result := F.Pipe3(
map[string]string{"x": "Alice", "y": "Bob"},
BindTo[TestState, string, string](func(name string) TestState {
return TestState{Name: name}
}),
Let[TestState, int, string, TestState](
func(count int) func(TestState) TestState {
return func(s TestState) TestState {
s.Count = count
return s
}
},
func(s TestState) int {
return len(s.Name)
},
),
LetTo[TestState, int, string, TestState](
func(version int) func(TestState) TestState {
return func(s TestState) TestState {
s.Version = version
return s
}
},
1,
),
)
expected := map[string]TestState{
"x": {Name: "Alice", Count: 5, Version: 1},
"y": {Name: "Bob", Count: 3, Version: 1},
}
assert.Equal(t, expected, result)
}
func TestBindWithDependentComputation(t *testing.T) {
// Test Bind where the computation creates new keys based on input
monoid := MergeMonoid[string, TestState]()
result := F.Pipe1(
map[string]int{"x": 5},
Bind[int, string](monoid)(
func(str string) func(int) TestState {
return func(n int) TestState {
return TestState{Name: str, Count: n}
}
},
func(n int) map[string]string {
// Create a string based on the number
result := ""
for i := 0; i < n; i++ {
result += "a"
}
return map[string]string{"x": result}
},
),
)
expected := map[string]TestState{
"x": {Name: "aaaaa", Count: 5},
}
assert.Equal(t, expected, result)
}
// Made with Bob

View File

@@ -20,11 +20,11 @@ import (
G "github.com/IBM/fp-go/v2/record/generic" G "github.com/IBM/fp-go/v2/record/generic"
) )
func Eq[K comparable, V any](e E.Eq[V]) E.Eq[map[K]V] { func Eq[K comparable, V any](e E.Eq[V]) E.Eq[Record[K, V]] {
return G.Eq[map[K]V](e) return G.Eq[Record[K, V]](e)
} }
// FromStrictEquals constructs an [EQ.Eq] from the canonical comparison function // FromStrictEquals constructs an [EQ.Eq] from the canonical comparison function
func FromStrictEquals[K, V comparable]() E.Eq[map[K]V] { func FromStrictEquals[K, V comparable]() E.Eq[Record[K, V]] {
return G.FromStrictEquals[map[K]V]() return G.FromStrictEquals[Record[K, V]]()
} }

View File

@@ -16,27 +16,34 @@
package record package record
import ( import (
M "github.com/IBM/fp-go/v2/monoid"
G "github.com/IBM/fp-go/v2/record/generic" G "github.com/IBM/fp-go/v2/record/generic"
S "github.com/IBM/fp-go/v2/semigroup" S "github.com/IBM/fp-go/v2/semigroup"
) )
// UnionMonoid computes the union of two maps of the same type // UnionMonoid computes the union of two maps of the same type
func UnionMonoid[K comparable, V any](s S.Semigroup[V]) M.Monoid[map[K]V] { //
return G.UnionMonoid[map[K]V](s) //go:inline
func UnionMonoid[K comparable, V any](s S.Semigroup[V]) Monoid[Record[K, V]] {
return G.UnionMonoid[Record[K, V]](s)
} }
// UnionLastMonoid computes the union of two maps of the same type giving the last map precedence // UnionLastMonoid computes the union of two maps of the same type giving the last map precedence
func UnionLastMonoid[K comparable, V any]() M.Monoid[map[K]V] { //
return G.UnionLastMonoid[map[K]V]() //go:inline
func UnionLastMonoid[K comparable, V any]() Monoid[Record[K, V]] {
return G.UnionLastMonoid[Record[K, V]]()
} }
// UnionFirstMonoid computes the union of two maps of the same type giving the first map precedence // UnionFirstMonoid computes the union of two maps of the same type giving the first map precedence
func UnionFirstMonoid[K comparable, V any]() M.Monoid[map[K]V] { //
return G.UnionFirstMonoid[map[K]V]() //go:inline
func UnionFirstMonoid[K comparable, V any]() Monoid[Record[K, V]] {
return G.UnionFirstMonoid[Record[K, V]]()
} }
// MergeMonoid computes the union of two maps of the same type giving the last map precedence // MergeMonoid computes the union of two maps of the same type giving the last map precedence
func MergeMonoid[K comparable, V any]() M.Monoid[map[K]V] { //
return G.UnionLastMonoid[map[K]V]() //go:inline
func MergeMonoid[K comparable, V any]() Monoid[Record[K, V]] {
return G.UnionLastMonoid[Record[K, V]]()
} }

View File

@@ -16,317 +16,315 @@
package record package record
import ( import (
EM "github.com/IBM/fp-go/v2/endomorphism"
Mg "github.com/IBM/fp-go/v2/magma" Mg "github.com/IBM/fp-go/v2/magma"
Mo "github.com/IBM/fp-go/v2/monoid" "github.com/IBM/fp-go/v2/option"
O "github.com/IBM/fp-go/v2/option"
"github.com/IBM/fp-go/v2/ord" "github.com/IBM/fp-go/v2/ord"
G "github.com/IBM/fp-go/v2/record/generic" G "github.com/IBM/fp-go/v2/record/generic"
) )
// IsEmpty tests if a map is empty // IsEmpty tests if a map is empty
func IsEmpty[K comparable, V any](r map[K]V) bool { func IsEmpty[K comparable, V any](r Record[K, V]) bool {
return G.IsEmpty(r) return G.IsEmpty(r)
} }
// IsNonEmpty tests if a map is not empty // IsNonEmpty tests if a map is not empty
func IsNonEmpty[K comparable, V any](r map[K]V) bool { func IsNonEmpty[K comparable, V any](r Record[K, V]) bool {
return G.IsNonEmpty(r) return G.IsNonEmpty(r)
} }
// Keys returns the key in a map // Keys returns the key in a map
func Keys[K comparable, V any](r map[K]V) []K { func Keys[K comparable, V any](r Record[K, V]) []K {
return G.Keys[map[K]V, []K](r) return G.Keys[Record[K, V], []K](r)
} }
// Values returns the values in a map // Values returns the values in a map
func Values[K comparable, V any](r map[K]V) []V { func Values[K comparable, V any](r Record[K, V]) []V {
return G.Values[map[K]V, []V](r) return G.Values[Record[K, V], []V](r)
} }
// Collect applies a collector function to the key value pairs in a map and returns the result as an array // Collect applies a collector function to the key value pairs in a map and returns the result as an array
func Collect[K comparable, V, R any](f func(K, V) R) func(map[K]V) []R { func Collect[K comparable, V, R any](f func(K, V) R) func(Record[K, V]) []R {
return G.Collect[map[K]V, []R](f) return G.Collect[Record[K, V], []R](f)
} }
// CollectOrd applies a collector function to the key value pairs in a map and returns the result as an array // CollectOrd applies a collector function to the key value pairs in a map and returns the result as an array
func CollectOrd[V, R any, K comparable](o ord.Ord[K]) func(func(K, V) R) func(map[K]V) []R { func CollectOrd[V, R any, K comparable](o ord.Ord[K]) func(func(K, V) R) func(Record[K, V]) []R {
return G.CollectOrd[map[K]V, []R](o) return G.CollectOrd[Record[K, V], []R](o)
} }
// Reduce reduces a map to a single value by applying a reducer function to each value // Reduce reduces a map to a single value by applying a reducer function to each value
func Reduce[K comparable, V, R any](f func(R, V) R, initial R) func(map[K]V) R { func Reduce[K comparable, V, R any](f func(R, V) R, initial R) func(Record[K, V]) R {
return G.Reduce[map[K]V](f, initial) return G.Reduce[Record[K, V]](f, initial)
} }
// ReduceWithIndex reduces a map to a single value by applying a reducer function to each key-value pair // ReduceWithIndex reduces a map to a single value by applying a reducer function to each key-value pair
func ReduceWithIndex[K comparable, V, R any](f func(K, R, V) R, initial R) func(map[K]V) R { func ReduceWithIndex[K comparable, V, R any](f func(K, R, V) R, initial R) func(Record[K, V]) R {
return G.ReduceWithIndex[map[K]V](f, initial) return G.ReduceWithIndex[Record[K, V]](f, initial)
} }
// ReduceRef reduces a map to a single value by applying a reducer function to each value reference // ReduceRef reduces a map to a single value by applying a reducer function to each value reference
func ReduceRef[K comparable, V, R any](f func(R, *V) R, initial R) func(map[K]V) R { func ReduceRef[K comparable, V, R any](f func(R, *V) R, initial R) func(Record[K, V]) R {
return G.ReduceRef[map[K]V](f, initial) return G.ReduceRef[Record[K, V]](f, initial)
} }
// ReduceRefWithIndex reduces a map to a single value by applying a reducer function to each key-value pair with value references // ReduceRefWithIndex reduces a map to a single value by applying a reducer function to each key-value pair with value references
func ReduceRefWithIndex[K comparable, V, R any](f func(K, R, *V) R, initial R) func(map[K]V) R { func ReduceRefWithIndex[K comparable, V, R any](f func(K, R, *V) R, initial R) func(Record[K, V]) R {
return G.ReduceRefWithIndex[map[K]V](f, initial) return G.ReduceRefWithIndex[Record[K, V]](f, initial)
} }
// MonadMap transforms each value in a map using the provided function // MonadMap transforms each value in a map using the provided function
func MonadMap[K comparable, V, R any](r map[K]V, f func(V) R) map[K]R { func MonadMap[K comparable, V, R any](r Record[K, V], f func(V) R) Record[K, R] {
return G.MonadMap[map[K]V, map[K]R](r, f) return G.MonadMap[Record[K, V], Record[K, R]](r, f)
} }
// MonadMapWithIndex transforms each key-value pair in a map using the provided function // MonadMapWithIndex transforms each key-value pair in a map using the provided function
func MonadMapWithIndex[K comparable, V, R any](r map[K]V, f func(K, V) R) map[K]R { func MonadMapWithIndex[K comparable, V, R any](r Record[K, V], f func(K, V) R) Record[K, R] {
return G.MonadMapWithIndex[map[K]V, map[K]R](r, f) return G.MonadMapWithIndex[Record[K, V], Record[K, R]](r, f)
} }
// MonadMapRefWithIndex transforms each key-value pair in a map using the provided function with value references // MonadMapRefWithIndex transforms each key-value pair in a map using the provided function with value references
func MonadMapRefWithIndex[K comparable, V, R any](r map[K]V, f func(K, *V) R) map[K]R { func MonadMapRefWithIndex[K comparable, V, R any](r Record[K, V], f func(K, *V) R) Record[K, R] {
return G.MonadMapRefWithIndex[map[K]V, map[K]R](r, f) return G.MonadMapRefWithIndex[Record[K, V], Record[K, R]](r, f)
} }
// MonadMapRef transforms each value in a map using the provided function with value references // MonadMapRef transforms each value in a map using the provided function with value references
func MonadMapRef[K comparable, V, R any](r map[K]V, f func(*V) R) map[K]R { func MonadMapRef[K comparable, V, R any](r Record[K, V], f func(*V) R) Record[K, R] {
return G.MonadMapRef[map[K]V, map[K]R](r, f) return G.MonadMapRef[Record[K, V], Record[K, R]](r, f)
} }
// Map returns a function that transforms each value in a map using the provided function // Map returns a function that transforms each value in a map using the provided function
func Map[K comparable, V, R any](f func(V) R) Operator[K, V, R] { func Map[K comparable, V, R any](f func(V) R) Operator[K, V, R] {
return G.Map[map[K]V, map[K]R](f) return G.Map[Record[K, V], Record[K, R]](f)
} }
// MapRef returns a function that transforms each value in a map using the provided function with value references // MapRef returns a function that transforms each value in a map using the provided function with value references
func MapRef[K comparable, V, R any](f func(*V) R) Operator[K, V, R] { func MapRef[K comparable, V, R any](f func(*V) R) Operator[K, V, R] {
return G.MapRef[map[K]V, map[K]R](f) return G.MapRef[Record[K, V], Record[K, R]](f)
} }
// MapWithIndex returns a function that transforms each key-value pair in a map using the provided function // MapWithIndex returns a function that transforms each key-value pair in a map using the provided function
func MapWithIndex[K comparable, V, R any](f func(K, V) R) Operator[K, V, R] { func MapWithIndex[K comparable, V, R any](f func(K, V) R) Operator[K, V, R] {
return G.MapWithIndex[map[K]V, map[K]R](f) return G.MapWithIndex[Record[K, V], Record[K, R]](f)
} }
// MapRefWithIndex returns a function that transforms each key-value pair in a map using the provided function with value references // MapRefWithIndex returns a function that transforms each key-value pair in a map using the provided function with value references
func MapRefWithIndex[K comparable, V, R any](f func(K, *V) R) Operator[K, V, R] { func MapRefWithIndex[K comparable, V, R any](f func(K, *V) R) Operator[K, V, R] {
return G.MapRefWithIndex[map[K]V, map[K]R](f) return G.MapRefWithIndex[Record[K, V], Record[K, R]](f)
} }
// Lookup returns the entry for a key in a map if it exists // Lookup returns the entry for a key in a map if it exists
func Lookup[V any, K comparable](k K) func(map[K]V) O.Option[V] { func Lookup[V any, K comparable](k K) option.Kleisli[Record[K, V], V] {
return G.Lookup[map[K]V](k) return G.Lookup[Record[K, V]](k)
} }
// MonadLookup returns the entry for a key in a map if it exists // MonadLookup returns the entry for a key in a map if it exists
func MonadLookup[V any, K comparable](m map[K]V, k K) O.Option[V] { func MonadLookup[V any, K comparable](m Record[K, V], k K) Option[V] {
return G.MonadLookup(m, k) return G.MonadLookup(m, k)
} }
// Has tests if a key is contained in a map // Has tests if a key is contained in a map
func Has[K comparable, V any](k K, r map[K]V) bool { func Has[K comparable, V any](k K, r Record[K, V]) bool {
return G.Has(k, r) return G.Has(k, r)
} }
// Union combines two maps using the provided Magma to resolve conflicts for duplicate keys // Union combines two maps using the provided Magma to resolve conflicts for duplicate keys
func Union[K comparable, V any](m Mg.Magma[V]) func(map[K]V) Operator[K, V, V] { func Union[K comparable, V any](m Mg.Magma[V]) func(Record[K, V]) Operator[K, V, V] {
return G.Union[map[K]V](m) return G.Union[Record[K, V]](m)
} }
// Merge combines two maps giving the values in the right one precedence. Also refer to [MergeMonoid] // Merge combines two maps giving the values in the right one precedence. Also refer to [MergeMonoid]
func Merge[K comparable, V any](right map[K]V) Operator[K, V, V] { func Merge[K comparable, V any](right Record[K, V]) Operator[K, V, V] {
return G.Merge(right) return G.Merge(right)
} }
// Empty creates an empty map // Empty creates an empty map
func Empty[K comparable, V any]() map[K]V { func Empty[K comparable, V any]() Record[K, V] {
return G.Empty[map[K]V]() return G.Empty[Record[K, V]]()
} }
// Size returns the number of elements in a map // Size returns the number of elements in a map
func Size[K comparable, V any](r map[K]V) int { func Size[K comparable, V any](r Record[K, V]) int {
return G.Size(r) return G.Size(r)
} }
// ToArray converts a map to an array of key-value pairs // ToArray converts a map to an array of key-value pairs
func ToArray[K comparable, V any](r map[K]V) Entries[K, V] { func ToArray[K comparable, V any](r Record[K, V]) Entries[K, V] {
return G.ToArray[map[K]V, Entries[K, V]](r) return G.ToArray[Record[K, V], Entries[K, V]](r)
} }
// ToEntries converts a map to an array of key-value pairs (alias for ToArray) // ToEntries converts a map to an array of key-value pairs (alias for ToArray)
func ToEntries[K comparable, V any](r map[K]V) Entries[K, V] { func ToEntries[K comparable, V any](r Record[K, V]) Entries[K, V] {
return G.ToEntries[map[K]V, Entries[K, V]](r) return G.ToEntries[Record[K, V], Entries[K, V]](r)
} }
// FromEntries creates a map from an array of key-value pairs // FromEntries creates a map from an array of key-value pairs
func FromEntries[K comparable, V any](fa Entries[K, V]) map[K]V { func FromEntries[K comparable, V any](fa Entries[K, V]) Record[K, V] {
return G.FromEntries[map[K]V](fa) return G.FromEntries[Record[K, V]](fa)
} }
// UpsertAt returns a function that inserts or updates a key-value pair in a map // UpsertAt returns a function that inserts or updates a key-value pair in a map
func UpsertAt[K comparable, V any](k K, v V) Operator[K, V, V] { func UpsertAt[K comparable, V any](k K, v V) Operator[K, V, V] {
return G.UpsertAt[map[K]V](k, v) return G.UpsertAt[Record[K, V]](k, v)
} }
// DeleteAt returns a function that removes a key from a map // DeleteAt returns a function that removes a key from a map
func DeleteAt[K comparable, V any](k K) Operator[K, V, V] { func DeleteAt[K comparable, V any](k K) Operator[K, V, V] {
return G.DeleteAt[map[K]V](k) return G.DeleteAt[Record[K, V]](k)
} }
// Singleton creates a new map with a single entry // Singleton creates a new map with a single entry
func Singleton[K comparable, V any](k K, v V) map[K]V { func Singleton[K comparable, V any](k K, v V) Record[K, V] {
return G.Singleton[map[K]V](k, v) return G.Singleton[Record[K, V]](k, v)
} }
// FilterMapWithIndex creates a new map with only the elements for which the transformation function creates a Some // 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]) Operator[K, V1, V2] { func FilterMapWithIndex[K comparable, V1, V2 any](f func(K, V1) Option[V2]) Operator[K, V1, V2] {
return G.FilterMapWithIndex[map[K]V1, map[K]V2](f) return G.FilterMapWithIndex[Record[K, V1], Record[K, V2]](f)
} }
// FilterMap creates a new map with only the elements for which the transformation function creates a Some // 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]) Operator[K, V1, V2] { func FilterMap[K comparable, V1, V2 any](f option.Kleisli[V1, V2]) Operator[K, V1, V2] {
return G.FilterMap[map[K]V1, map[K]V2](f) return G.FilterMap[Record[K, V1], Record[K, V2]](f)
} }
// Filter creates a new map with only the elements that match the predicate // Filter creates a new map with only the elements that match the predicate
func Filter[K comparable, V any](f func(K) bool) Operator[K, V, V] { func Filter[K comparable, V any](f Predicate[K]) Operator[K, V, V] {
return G.Filter[map[K]V](f) return G.Filter[Record[K, V]](f)
} }
// FilterWithIndex creates a new map with only the elements that match the predicate // FilterWithIndex creates a new map with only the elements that match the predicate
func FilterWithIndex[K comparable, V any](f func(K, V) bool) Operator[K, V, V] { func FilterWithIndex[K comparable, V any](f PredicateWithIndex[K, V]) Operator[K, V, V] {
return G.FilterWithIndex[map[K]V](f) return G.FilterWithIndex[Record[K, V]](f)
} }
// IsNil checks if the map is set to nil // IsNil checks if the map is set to nil
func IsNil[K comparable, V any](m map[K]V) bool { func IsNil[K comparable, V any](m Record[K, V]) bool {
return G.IsNil(m) return G.IsNil(m)
} }
// IsNonNil checks if the map is set to nil // IsNonNil checks if the map is set to nil
func IsNonNil[K comparable, V any](m map[K]V) bool { func IsNonNil[K comparable, V any](m Record[K, V]) bool {
return G.IsNonNil(m) return G.IsNonNil(m)
} }
// ConstNil return a nil map // ConstNil return a nil map
func ConstNil[K comparable, V any]() map[K]V { func ConstNil[K comparable, V any]() Record[K, V] {
return map[K]V(nil) return Record[K, V](nil)
} }
// MonadChainWithIndex chains a map transformation function that produces maps, combining results using the provided Monoid // MonadChainWithIndex chains a map transformation function that produces maps, combining results using the provided Monoid
func MonadChainWithIndex[V1 any, K comparable, V2 any](m Mo.Monoid[map[K]V2], r map[K]V1, f func(K, V1) map[K]V2) map[K]V2 { func MonadChainWithIndex[V1 any, K comparable, V2 any](m Monoid[Record[K, V2]], r Record[K, V1], f KleisliWithIndex[K, V1, V2]) Record[K, V2] {
return G.MonadChainWithIndex(m, r, f) return G.MonadChainWithIndex(m, r, f)
} }
// MonadChain chains a map transformation function that produces maps, combining results using the provided Monoid // MonadChain chains a map transformation function that produces maps, combining results using the provided Monoid
func MonadChain[V1 any, K comparable, V2 any](m Mo.Monoid[map[K]V2], r map[K]V1, f func(V1) map[K]V2) map[K]V2 { func MonadChain[V1 any, K comparable, V2 any](m Monoid[Record[K, V2]], r Record[K, V1], f Kleisli[K, V1, V2]) Record[K, V2] {
return G.MonadChain(m, r, f) return G.MonadChain(m, r, f)
} }
// ChainWithIndex returns a function that chains a map transformation function that produces maps, combining results using the provided Monoid // ChainWithIndex returns a function that chains a map transformation function that produces maps, combining results using the provided Monoid
func ChainWithIndex[V1 any, K comparable, V2 any](m Mo.Monoid[map[K]V2]) func(func(K, V1) map[K]V2) Operator[K, V1, V2] { func ChainWithIndex[V1 any, K comparable, V2 any](m Monoid[Record[K, V2]]) func(KleisliWithIndex[K, V1, V2]) Operator[K, V1, V2] {
return G.ChainWithIndex[map[K]V1](m) return G.ChainWithIndex[Record[K, V1]](m)
} }
// Chain returns a function that chains a map transformation function that produces maps, combining results using the provided Monoid // Chain returns a function that chains a map transformation function that produces maps, combining results using the provided Monoid
func Chain[V1 any, K comparable, V2 any](m Mo.Monoid[map[K]V2]) func(func(V1) map[K]V2) Operator[K, V1, V2] { func Chain[V1 any, K comparable, V2 any](m Monoid[Record[K, V2]]) func(Kleisli[K, V1, V2]) Operator[K, V1, V2] {
return G.Chain[map[K]V1](m) return G.Chain[Record[K, V1]](m)
} }
// Flatten converts a nested map into a regular map // Flatten converts a nested map into a regular map
func Flatten[K comparable, V any](m Mo.Monoid[map[K]V]) func(map[K]map[K]V) map[K]V { func Flatten[K comparable, V any](m Monoid[Record[K, V]]) func(Record[K, Record[K, V]]) Record[K, V] {
return G.Flatten[map[K]map[K]V](m) return G.Flatten[Record[K, Record[K, V]]](m)
} }
// FilterChainWithIndex creates a new map with only the elements for which the transformation function creates a Some // FilterChainWithIndex creates a new map with only the elements for which the transformation function creates a Some
func FilterChainWithIndex[V1 any, K comparable, V2 any](m Mo.Monoid[map[K]V2]) func(func(K, V1) O.Option[map[K]V2]) Operator[K, V1, V2] { func FilterChainWithIndex[V1 any, K comparable, V2 any](m Monoid[Record[K, V2]]) func(func(K, V1) Option[Record[K, V2]]) Operator[K, V1, V2] {
return G.FilterChainWithIndex[map[K]V1](m) return G.FilterChainWithIndex[Record[K, V1]](m)
} }
// FilterChain creates a new map with only the elements for which the transformation function creates a Some // FilterChain creates a new map with only the elements for which the transformation function creates a Some
func FilterChain[V1 any, K comparable, V2 any](m Mo.Monoid[map[K]V2]) func(func(V1) O.Option[map[K]V2]) Operator[K, V1, V2] { func FilterChain[V1 any, K comparable, V2 any](m Monoid[Record[K, V2]]) func(option.Kleisli[V1, Record[K, V2]]) Operator[K, V1, V2] {
return G.FilterChain[map[K]V1](m) return G.FilterChain[Record[K, V1]](m)
} }
// FoldMap maps and folds a record. Map the record passing each value to the iterating function. Then fold the results using the provided Monoid. // FoldMap maps and folds a record. Map the record passing each value to the iterating function. Then fold the results using the provided Monoid.
func FoldMap[K comparable, A, B any](m Mo.Monoid[B]) func(func(A) B) func(map[K]A) B { func FoldMap[K comparable, A, B any](m Monoid[B]) func(func(A) B) func(Record[K, A]) B {
return G.FoldMap[map[K]A](m) return G.FoldMap[Record[K, A]](m)
} }
// FoldMapWithIndex maps and folds a record. Map the record passing each value to the iterating function. Then fold the results using the provided Monoid. // FoldMapWithIndex maps and folds a record. Map the record passing each value to the iterating function. Then fold the results using the provided Monoid.
func FoldMapWithIndex[K comparable, A, B any](m Mo.Monoid[B]) func(func(K, A) B) func(map[K]A) B { func FoldMapWithIndex[K comparable, A, B any](m Monoid[B]) func(func(K, A) B) func(Record[K, A]) B {
return G.FoldMapWithIndex[map[K]A](m) return G.FoldMapWithIndex[Record[K, A]](m)
} }
// Fold folds the record using the provided Monoid. // Fold folds the record using the provided Monoid.
func Fold[K comparable, A any](m Mo.Monoid[A]) func(map[K]A) A { func Fold[K comparable, A any](m Monoid[A]) func(Record[K, A]) A {
return G.Fold[map[K]A](m) return G.Fold[Record[K, A]](m)
} }
// ReduceOrdWithIndex reduces a map into a single value via a reducer function making sure that the keys are passed to the reducer in the specified order // ReduceOrdWithIndex reduces a map into a single value via a reducer function making sure that the keys are passed to the reducer in the specified order
func ReduceOrdWithIndex[V, R any, K comparable](o ord.Ord[K]) func(func(K, R, V) R, R) func(map[K]V) R { func ReduceOrdWithIndex[V, R any, K comparable](o ord.Ord[K]) func(func(K, R, V) R, R) func(Record[K, V]) R {
return G.ReduceOrdWithIndex[map[K]V, K, V, R](o) return G.ReduceOrdWithIndex[Record[K, V], K, V, R](o)
} }
// ReduceOrd reduces a map into a single value via a reducer function making sure that the keys are passed to the reducer in the specified order // ReduceOrd reduces a map into a single value via a reducer function making sure that the keys are passed to the reducer in the specified order
func ReduceOrd[V, R any, K comparable](o ord.Ord[K]) func(func(R, V) R, R) func(map[K]V) R { func ReduceOrd[V, R any, K comparable](o ord.Ord[K]) func(func(R, V) R, R) func(Record[K, V]) R {
return G.ReduceOrd[map[K]V, K, V, R](o) return G.ReduceOrd[Record[K, V], K, V, R](o)
} }
// FoldMap maps and folds a record. Map the record passing each value to the iterating function. Then fold the results using the provided Monoid and the items in the provided order // FoldMap maps and folds a record. Map the record passing each value to the iterating function. Then fold the results using the provided Monoid and the items in the provided order
func FoldMapOrd[A, B any, K comparable](o ord.Ord[K]) func(m Mo.Monoid[B]) func(func(A) B) func(map[K]A) B { func FoldMapOrd[A, B any, K comparable](o ord.Ord[K]) func(m Monoid[B]) func(func(A) B) func(Record[K, A]) B {
return G.FoldMapOrd[map[K]A, K, A, B](o) return G.FoldMapOrd[Record[K, A], K, A, B](o)
} }
// Fold folds the record using the provided Monoid with the items passed in the given order // Fold folds the record using the provided Monoid with the items passed in the given order
func FoldOrd[A any, K comparable](o ord.Ord[K]) func(m Mo.Monoid[A]) func(map[K]A) A { func FoldOrd[A any, K comparable](o ord.Ord[K]) func(m Monoid[A]) func(Record[K, A]) A {
return G.FoldOrd[map[K]A](o) return G.FoldOrd[Record[K, A]](o)
} }
// FoldMapWithIndex maps and folds a record. Map the record passing each value to the iterating function. Then fold the results using the provided Monoid and the items in the provided order // FoldMapWithIndex maps and folds a record. Map the record passing each value to the iterating function. Then fold the results using the provided Monoid and the items in the provided order
func FoldMapOrdWithIndex[K comparable, A, B any](o ord.Ord[K]) func(m Mo.Monoid[B]) func(func(K, A) B) func(map[K]A) B { func FoldMapOrdWithIndex[K comparable, A, B any](o ord.Ord[K]) func(m Monoid[B]) func(func(K, A) B) func(Record[K, A]) B {
return G.FoldMapOrdWithIndex[map[K]A, K, A, B](o) return G.FoldMapOrdWithIndex[Record[K, A], K, A, B](o)
} }
// KeysOrd returns the keys in the map in their given order // KeysOrd returns the keys in the map in their given order
func KeysOrd[V any, K comparable](o ord.Ord[K]) func(r map[K]V) []K { func KeysOrd[V any, K comparable](o ord.Ord[K]) func(r Record[K, V]) []K {
return G.KeysOrd[map[K]V, []K](o) return G.KeysOrd[Record[K, V], []K](o)
} }
// ValuesOrd returns the values in the map ordered by their keys in the given order // ValuesOrd returns the values in the map ordered by their keys in the given order
func ValuesOrd[V any, K comparable](o ord.Ord[K]) func(r map[K]V) []V { func ValuesOrd[V any, K comparable](o ord.Ord[K]) func(r Record[K, V]) []V {
return G.ValuesOrd[map[K]V, []V](o) return G.ValuesOrd[Record[K, V], []V](o)
} }
// MonadFlap applies a value to a map of functions, producing a map of results // MonadFlap applies a value to a map of functions, producing a map of results
func MonadFlap[B any, K comparable, A any](fab map[K]func(A) B, a A) map[K]B { func MonadFlap[B any, K comparable, A any](fab Record[K, func(A) B], a A) Record[K, B] {
return G.MonadFlap[map[K]func(A) B, map[K]B](fab, a) return G.MonadFlap[Record[K, func(A) B], Record[K, B]](fab, a)
} }
// Flap returns a function that applies a value to a map of functions, producing a map of results // Flap returns a function that applies a value to a map of functions, producing a map of results
func Flap[B any, K comparable, A any](a A) func(map[K]func(A) B) map[K]B { func Flap[B any, K comparable, A any](a A) Operator[K, func(A) B, B] {
return G.Flap[map[K]func(A) B, map[K]B](a) return G.Flap[Record[K, func(A) B], Record[K, B]](a)
} }
// Copy creates a shallow copy of the map // Copy creates a shallow copy of the map
func Copy[K comparable, V any](m map[K]V) map[K]V { func Copy[K comparable, V any](m Record[K, V]) Record[K, V] {
return G.Copy(m) return G.Copy(m)
} }
// Clone creates a deep copy of the map using the provided endomorphism to clone the values // Clone creates a deep copy of the map using the provided endomorphism to clone the values
func Clone[K comparable, V any](f EM.Endomorphism[V]) EM.Endomorphism[map[K]V] { func Clone[K comparable, V any](f Endomorphism[V]) Endomorphism[Record[K, V]] {
return G.Clone[map[K]V](f) return G.Clone[Record[K, V]](f)
} }
// FromFoldableMap converts from a reducer to a map // FromFoldableMap converts from a reducer to a map
// Duplicate keys are resolved by the provided [Mg.Magma] // Duplicate keys are resolved by the provided [Mg.Magma]
func FromFoldableMap[ func FromFoldableMap[
FOLDABLE ~func(func(map[K]V, A) map[K]V, map[K]V) func(HKTA) map[K]V, // the reduce function FOLDABLE ~func(func(Record[K, V], A) Record[K, V], Record[K, V]) func(HKTA) Record[K, V], // the reduce function
A any, A any,
HKTA any, HKTA any,
K comparable, K comparable,
V any](m Mg.Magma[V], red FOLDABLE) func(f func(A) Entry[K, V]) func(fa HKTA) map[K]V { V any](m Mg.Magma[V], red FOLDABLE) func(f func(A) Entry[K, V]) Kleisli[K, HKTA, V] {
return G.FromFoldableMap[func(A) Entry[K, V]](m, red) return G.FromFoldableMap[func(A) Entry[K, V]](m, red)
} }
@@ -335,17 +333,17 @@ func FromFoldableMap[
func FromArrayMap[ func FromArrayMap[
A any, A any,
K comparable, K comparable,
V any](m Mg.Magma[V]) func(f func(A) Entry[K, V]) func(fa []A) map[K]V { V any](m Mg.Magma[V]) func(f func(A) Entry[K, V]) Kleisli[K, []A, V] {
return G.FromArrayMap[func(A) Entry[K, V], []A, map[K]V](m) return G.FromArrayMap[func(A) Entry[K, V], []A, Record[K, V]](m)
} }
// FromFoldable converts from a reducer to a map // FromFoldable converts from a reducer to a map
// Duplicate keys are resolved by the provided [Mg.Magma] // Duplicate keys are resolved by the provided [Mg.Magma]
func FromFoldable[ func FromFoldable[
HKTA any, HKTA any,
FOLDABLE ~func(func(map[K]V, Entry[K, V]) map[K]V, map[K]V) func(HKTA) map[K]V, // the reduce function FOLDABLE ~func(func(Record[K, V], Entry[K, V]) Record[K, V], Record[K, V]) func(HKTA) Record[K, V], // the reduce function
K comparable, K comparable,
V any](m Mg.Magma[V], red FOLDABLE) func(fa HKTA) map[K]V { V any](m Mg.Magma[V], red FOLDABLE) Kleisli[K, HKTA, V] {
return G.FromFoldable(m, red) return G.FromFoldable(m, red)
} }
@@ -353,21 +351,21 @@ func FromFoldable[
// Duplicate keys are resolved by the provided [Mg.Magma] // Duplicate keys are resolved by the provided [Mg.Magma]
func FromArray[ func FromArray[
K comparable, K comparable,
V any](m Mg.Magma[V]) func(fa Entries[K, V]) map[K]V { V any](m Mg.Magma[V]) Kleisli[K, Entries[K, V], V] {
return G.FromArray[Entries[K, V], map[K]V](m) return G.FromArray[Entries[K, V], Record[K, V]](m)
} }
// MonadAp applies a map of functions to a map of values, combining results using the provided Monoid // MonadAp applies a map of functions to a map of values, combining results using the provided Monoid
func MonadAp[A any, K comparable, B any](m Mo.Monoid[map[K]B], fab map[K]func(A) B, fa map[K]A) map[K]B { func MonadAp[A any, K comparable, B any](m Monoid[Record[K, B]], fab Record[K, func(A) B], fa Record[K, A]) Record[K, B] {
return G.MonadAp(m, fab, fa) return G.MonadAp(m, fab, fa)
} }
// Ap returns a function that applies a map of functions to a map of values, combining results using the provided Monoid // Ap returns a function that applies a map of functions to a map of values, combining results using the provided Monoid
func Ap[A any, K comparable, B any](m Mo.Monoid[map[K]B]) func(fa map[K]A) func(map[K]func(A) B) map[K]B { func Ap[A any, K comparable, B any](m Monoid[Record[K, B]]) func(fa Record[K, A]) Operator[K, func(A) B, B] {
return G.Ap[map[K]B, map[K]func(A) B, map[K]A](m) return G.Ap[Record[K, B], Record[K, func(A) B], Record[K, A]](m)
} }
// Of creates a map with a single key-value pair // Of creates a map with a single key-value pair
func Of[K comparable, A any](k K, a A) map[K]A { func Of[K comparable, A any](k K, a A) Record[K, A] {
return map[K]A{k: a} return Record[K, A]{k: a}
} }

View File

@@ -17,7 +17,6 @@ package record
import ( import (
G "github.com/IBM/fp-go/v2/record/generic" G "github.com/IBM/fp-go/v2/record/generic"
S "github.com/IBM/fp-go/v2/semigroup"
) )
// UnionSemigroup creates a semigroup for maps that combines two maps using the provided // UnionSemigroup creates a semigroup for maps that combines two maps using the provided
@@ -50,8 +49,10 @@ import (
// map2 := map[string]string{"b": "!", "c": "Goodbye"} // map2 := map[string]string{"b": "!", "c": "Goodbye"}
// result := mapSemigroup.Concat(map1, map2) // result := mapSemigroup.Concat(map1, map2)
// // result: {"a": "Hello", "b": "World!", "c": "Goodbye"} // // result: {"a": "Hello", "b": "World!", "c": "Goodbye"}
func UnionSemigroup[K comparable, V any](s S.Semigroup[V]) S.Semigroup[map[K]V] { //
return G.UnionSemigroup[map[K]V](s) //go:inline
func UnionSemigroup[K comparable, V any](s Semigroup[V]) Semigroup[Record[K, V]] {
return G.UnionSemigroup[Record[K, V]](s)
} }
// UnionLastSemigroup creates a semigroup for maps where the last (right) value wins // UnionLastSemigroup creates a semigroup for maps where the last (right) value wins
@@ -77,8 +78,10 @@ func UnionSemigroup[K comparable, V any](s S.Semigroup[V]) S.Semigroup[map[K]V]
// - Configuration overrides (later configs override earlier ones) // - Configuration overrides (later configs override earlier ones)
// - Applying updates to a base map // - Applying updates to a base map
// - Merging user preferences where newer values should win // - Merging user preferences where newer values should win
func UnionLastSemigroup[K comparable, V any]() S.Semigroup[map[K]V] { //
return G.UnionLastSemigroup[map[K]V]() //go:inline
func UnionLastSemigroup[K comparable, V any]() Semigroup[Record[K, V]] {
return G.UnionLastSemigroup[Record[K, V]]()
} }
// UnionFirstSemigroup creates a semigroup for maps where the first (left) value wins // UnionFirstSemigroup creates a semigroup for maps where the first (left) value wins
@@ -104,6 +107,8 @@ func UnionLastSemigroup[K comparable, V any]() S.Semigroup[map[K]V] {
// - Default values (defaults are set first, user values don't override) // - Default values (defaults are set first, user values don't override)
// - Caching (first cached value is kept, subsequent updates ignored) // - Caching (first cached value is kept, subsequent updates ignored)
// - Immutable registries (first registration wins, duplicates are ignored) // - Immutable registries (first registration wins, duplicates are ignored)
func UnionFirstSemigroup[K comparable, V any]() S.Semigroup[map[K]V] { //
return G.UnionFirstSemigroup[map[K]V]() //go:inline
func UnionFirstSemigroup[K comparable, V any]() Semigroup[Record[K, V]] {
return G.UnionFirstSemigroup[Record[K, V]]()
} }

View File

@@ -19,6 +19,36 @@ import (
G "github.com/IBM/fp-go/v2/internal/record" G "github.com/IBM/fp-go/v2/internal/record"
) )
// TraverseWithIndex transforms a map of values into a value of a map by applying an effectful function
// to each key-value pair. The function has access to both the key and value.
//
// This is useful when you need to perform an operation that may fail or have side effects on each
// element of a map, and you want to collect the results in the same applicative context.
//
// Type parameters:
// - K: The key type (must be comparable)
// - A: The input value type
// - B: The output value type
// - HKTB: Higher-kinded type representing the effect containing B (e.g., Option[B], Either[E, B])
// - HKTAB: Higher-kinded type representing a function from B to map[K]B in the effect
// - HKTRB: Higher-kinded type representing the effect containing map[K]B
//
// Parameters:
// - fof: Lifts a pure map[K]B into the effect (the "of" or "pure" function)
// - fmap: Maps a function over the effect (the "map" or "fmap" function)
// - fap: Applies an effectful function to an effectful value (the "ap" function)
// - f: The transformation function that takes a key and value and returns an effect
//
// Example with Option:
//
// f := func(k string, n int) O.Option[int] {
// if n > 0 {
// return O.Some(n * 2)
// }
// return O.None[int]()
// }
// traverse := TraverseWithIndex(O.Of[map[string]int], O.Map[...], O.Ap[...], f)
// result := traverse(map[string]int{"a": 1, "b": 2}) // O.Some(map[string]int{"a": 2, "b": 4})
func TraverseWithIndex[K comparable, A, B, HKTB, HKTAB, HKTRB any]( func TraverseWithIndex[K comparable, A, B, HKTB, HKTAB, HKTRB any](
fof func(map[K]B) HKTRB, fof func(map[K]B) HKTRB,
fmap func(func(map[K]B) func(B) map[K]B) func(HKTRB) HKTAB, fmap func(func(map[K]B) func(B) map[K]B) func(HKTRB) HKTAB,
@@ -28,10 +58,36 @@ func TraverseWithIndex[K comparable, A, B, HKTB, HKTAB, HKTRB any](
return G.TraverseWithIndex[map[K]A](fof, fmap, fap, f) return G.TraverseWithIndex[map[K]A](fof, fmap, fap, f)
} }
// HKTA = HKT<A> // Traverse transforms a map of values into a value of a map by applying an effectful function
// HKTB = HKT<B> // to each value. Unlike TraverseWithIndex, this function does not provide access to the keys.
// HKTAB = HKT<func(A)B> //
// HKTRB = HKT<map[K]B> // This is useful when you need to perform an operation that may fail or have side effects on each
// element of a map, and you want to collect the results in the same applicative context.
//
// Type parameters:
// - K: The key type (must be comparable)
// - A: The input value type
// - B: The output value type
// - HKTB: Higher-kinded type representing the effect containing B (e.g., Option[B], Either[E, B])
// - HKTAB: Higher-kinded type representing a function from B to map[K]B in the effect
// - HKTRB: Higher-kinded type representing the effect containing map[K]B
//
// Parameters:
// - fof: Lifts a pure map[K]B into the effect (the "of" or "pure" function)
// - fmap: Maps a function over the effect (the "map" or "fmap" function)
// - fap: Applies an effectful function to an effectful value (the "ap" function)
// - f: The transformation function that takes a value and returns an effect
//
// Example with Option:
//
// f := func(s string) O.Option[string] {
// if s != "" {
// return O.Some(strings.ToUpper(s))
// }
// return O.None[string]()
// }
// traverse := Traverse(O.Of[map[string]string], O.Map[...], O.Ap[...], f)
// result := traverse(map[string]string{"a": "hello"}) // O.Some(map[string]string{"a": "HELLO"})
func Traverse[K comparable, A, B, HKTB, HKTAB, HKTRB any]( func Traverse[K comparable, A, B, HKTB, HKTAB, HKTRB any](
fof func(map[K]B) HKTRB, fof func(map[K]B) HKTRB,
fmap func(func(map[K]B) func(B) map[K]B) func(HKTRB) HKTAB, fmap func(func(map[K]B) func(B) map[K]B) func(HKTRB) HKTAB,
@@ -40,9 +96,39 @@ func Traverse[K comparable, A, B, HKTB, HKTAB, HKTRB any](
return G.Traverse[map[K]A](fof, fmap, fap, f) return G.Traverse[map[K]A](fof, fmap, fap, f)
} }
// HKTA = HKT[A] // Sequence transforms a map of effects into an effect of a map.
// HKTAA = HKT[func(A)map[K]A] // This is the dual of Traverse where the transformation function is the identity.
// HKTRA = HKT[map[K]A] //
// This is useful when you have a map where each value is already in an effect context
// (like Option, Either, etc.) and you want to "flip" the nesting to get a single effect
// containing a map of plain values.
//
// If any value in the map is a "failure" (e.g., None, Left), the entire result will be
// a failure. If all values are "successes", the result will be a success containing a map
// of all the unwrapped values.
//
// Type parameters:
// - K: The key type (must be comparable)
// - A: The value type inside the effect
// - HKTA: Higher-kinded type representing the effect containing A (e.g., Option[A])
// - HKTAA: Higher-kinded type representing a function from A to map[K]A in the effect
// - HKTRA: Higher-kinded type representing the effect containing map[K]A
//
// Parameters:
// - fof: Lifts a pure map[K]A into the effect (the "of" or "pure" function)
// - fmap: Maps a function over the effect (the "map" or "fmap" function)
// - fap: Applies an effectful function to an effectful value (the "ap" function)
// - ma: The input map where each value is in an effect context
//
// Example with Option:
//
// input := map[string]O.Option[int]{"a": O.Some(1), "b": O.Some(2)}
// result := Sequence(O.Of[map[string]int], O.Map[...], O.Ap[...], input)
// // result: O.Some(map[string]int{"a": 1, "b": 2})
//
// input2 := map[string]O.Option[int]{"a": O.Some(1), "b": O.None[int]()}
// result2 := Sequence(O.Of[map[string]int], O.Map[...], O.Ap[...], input2)
// // result2: O.None[map[string]int]()
func Sequence[K comparable, A, HKTA, HKTAA, HKTRA any]( func Sequence[K comparable, A, HKTA, HKTAA, HKTRA any](
fof func(map[K]A) HKTRA, fof func(map[K]A) HKTRA,
fmap func(func(map[K]A) func(A) map[K]A) func(HKTRA) HKTAA, fmap func(func(map[K]A) func(A) map[K]A) func(HKTRA) HKTAA,

View File

@@ -16,11 +16,19 @@
package record package record
import ( import (
"github.com/IBM/fp-go/v2/endomorphism"
"github.com/IBM/fp-go/v2/monoid"
"github.com/IBM/fp-go/v2/option"
"github.com/IBM/fp-go/v2/pair" "github.com/IBM/fp-go/v2/pair"
"github.com/IBM/fp-go/v2/predicate" "github.com/IBM/fp-go/v2/predicate"
"github.com/IBM/fp-go/v2/semigroup"
) )
type ( type (
Endomorphism[A any] = endomorphism.Endomorphism[A]
Monoid[A any] = monoid.Monoid[A]
Semigroup[A any] = semigroup.Semigroup[A]
Option[A any] = option.Option[A]
// Record represents a map with comparable keys and values of any type. // Record represents a map with comparable keys and values of any type.
// This is the primary data structure for the record package, providing // This is the primary data structure for the record package, providing
@@ -108,7 +116,7 @@ type (
// sum := Reduce(func(acc int, v int) int { // sum := Reduce(func(acc int, v int) int {
// return acc + v // return acc + v
// }, 0) // }, 0)
Reducer[K comparable, V, R any] = func(R, V) R Reducer[V, R any] = func(R, V) R
// ReducerWithIndex accumulates values using both key and value information. // ReducerWithIndex accumulates values using both key and value information.
// //