mirror of
https://github.com/IBM/fp-go.git
synced 2025-12-19 23:42:05 +02:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d3c466bfb7 | ||
|
|
a6c6ea804f | ||
|
|
31ff98901e |
16
v2/README.md
16
v2/README.md
@@ -452,17 +452,27 @@ func process() IOResult[string] {
|
||||
|
||||
### Core Modules
|
||||
|
||||
#### Standard Packages (Struct-based)
|
||||
- **Option** - Represent optional values without nil
|
||||
- **Either** - Type-safe error handling with left/right values
|
||||
- **Result** - Simplified Either with error as left type
|
||||
- **Result** - Simplified Either with error as left type (recommended for error handling)
|
||||
- **IO** - Lazy evaluation and side effect management
|
||||
- **IOEither** - Combine IO with error handling
|
||||
- **IOResult** - Combine IO with Result for error handling (recommended over IOEither)
|
||||
- **Reader** - Dependency injection pattern
|
||||
- **ReaderIOEither** - Combine Reader, IO, and Either for complex workflows
|
||||
- **ReaderIOResult** - Combine Reader, IO, and Result for complex workflows
|
||||
- **Array** - Functional array operations
|
||||
- **Record** - Functional record/map operations
|
||||
- **Optics** - Lens, Prism, Optional, and Traversal for immutable updates
|
||||
|
||||
#### Idiomatic Packages (Tuple-based, High Performance)
|
||||
- **idiomatic/option** - Option monad using native Go `(value, bool)` tuples
|
||||
- **idiomatic/result** - Result monad using native Go `(value, error)` tuples
|
||||
- **idiomatic/ioresult** - IOResult monad using `func() (value, error)` for IO operations
|
||||
- **idiomatic/readerresult** - Reader monad combined with Result pattern
|
||||
- **idiomatic/readerioresult** - Reader monad combined with IOResult pattern
|
||||
|
||||
The idiomatic packages offer 2-10x performance improvements and zero allocations by using Go's native tuple patterns instead of struct wrappers. Use them for performance-critical code or when you prefer Go's native error handling style.
|
||||
|
||||
## 🤔 Should I Migrate?
|
||||
|
||||
**Migrate to V2 if:**
|
||||
|
||||
@@ -23,14 +23,15 @@ import (
|
||||
IOR "github.com/IBM/fp-go/v2/ioresult"
|
||||
L "github.com/IBM/fp-go/v2/lazy"
|
||||
O "github.com/IBM/fp-go/v2/option"
|
||||
"github.com/IBM/fp-go/v2/pair"
|
||||
R "github.com/IBM/fp-go/v2/record"
|
||||
T "github.com/IBM/fp-go/v2/tuple"
|
||||
|
||||
"sync"
|
||||
)
|
||||
|
||||
func providerToEntry(p Provider) T.Tuple2[string, ProviderFactory] {
|
||||
return T.MakeTuple2(p.Provides().Id(), p.Factory())
|
||||
func providerToEntry(p Provider) Entry[string, ProviderFactory] {
|
||||
return pair.MakePair(p.Provides().Id(), p.Factory())
|
||||
}
|
||||
|
||||
func itemProviderToMap(p Provider) map[string][]ProviderFactory {
|
||||
|
||||
@@ -4,10 +4,12 @@ import (
|
||||
"github.com/IBM/fp-go/v2/iooption"
|
||||
"github.com/IBM/fp-go/v2/ioresult"
|
||||
"github.com/IBM/fp-go/v2/option"
|
||||
"github.com/IBM/fp-go/v2/record"
|
||||
)
|
||||
|
||||
type (
|
||||
Option[T any] = option.Option[T]
|
||||
IOResult[T any] = ioresult.IOResult[T]
|
||||
IOOption[T any] = iooption.IOOption[T]
|
||||
Option[T any] = option.Option[T]
|
||||
IOResult[T any] = ioresult.IOResult[T]
|
||||
IOOption[T any] = iooption.IOOption[T]
|
||||
Entry[K comparable, V any] = record.Entry[K, V]
|
||||
)
|
||||
|
||||
@@ -4,12 +4,14 @@ import (
|
||||
"github.com/IBM/fp-go/v2/context/ioresult"
|
||||
"github.com/IBM/fp-go/v2/iooption"
|
||||
"github.com/IBM/fp-go/v2/option"
|
||||
"github.com/IBM/fp-go/v2/record"
|
||||
"github.com/IBM/fp-go/v2/result"
|
||||
)
|
||||
|
||||
type (
|
||||
Option[T any] = option.Option[T]
|
||||
Result[T any] = result.Result[T]
|
||||
IOResult[T any] = ioresult.IOResult[T]
|
||||
IOOption[T any] = iooption.IOOption[T]
|
||||
Option[T any] = option.Option[T]
|
||||
Result[T any] = result.Result[T]
|
||||
IOResult[T any] = ioresult.IOResult[T]
|
||||
IOOption[T any] = iooption.IOOption[T]
|
||||
Entry[K comparable, V any] = record.Entry[K, V]
|
||||
)
|
||||
|
||||
@@ -22,9 +22,11 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
A "github.com/IBM/fp-go/v2/array"
|
||||
F "github.com/IBM/fp-go/v2/function"
|
||||
N "github.com/IBM/fp-go/v2/number"
|
||||
O "github.com/IBM/fp-go/v2/option"
|
||||
R "github.com/IBM/fp-go/v2/record"
|
||||
S "github.com/IBM/fp-go/v2/string"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
@@ -42,13 +44,13 @@ func toMap[K comparable, V any](seq Seq2[K, V]) map[K]V {
|
||||
func TestOf(t *testing.T) {
|
||||
seq := Of(42)
|
||||
result := toSlice(seq)
|
||||
assert.Equal(t, []int{42}, result)
|
||||
assert.Equal(t, A.Of(42), result)
|
||||
}
|
||||
|
||||
func TestOf2(t *testing.T) {
|
||||
seq := Of2("key", 100)
|
||||
result := toMap(seq)
|
||||
assert.Equal(t, map[string]int{"key": 100}, result)
|
||||
assert.Equal(t, R.Of("key", 100), result)
|
||||
}
|
||||
|
||||
func TestFrom(t *testing.T) {
|
||||
@@ -587,3 +589,29 @@ func ExampleMonoid() {
|
||||
fmt.Println(result)
|
||||
// Output: [1 2 3 4 5 6]
|
||||
}
|
||||
|
||||
func TestMonadMapToArray(t *testing.T) {
|
||||
seq := From(1, 2, 3)
|
||||
result := MonadMapToArray(seq, N.Mul(2))
|
||||
assert.Equal(t, []int{2, 4, 6}, result)
|
||||
}
|
||||
|
||||
func TestMonadMapToArrayEmpty(t *testing.T) {
|
||||
seq := Empty[int]()
|
||||
result := MonadMapToArray(seq, N.Mul(2))
|
||||
assert.Empty(t, result)
|
||||
}
|
||||
|
||||
func TestMapToArray(t *testing.T) {
|
||||
seq := From(1, 2, 3)
|
||||
mapper := MapToArray(N.Mul(2))
|
||||
result := mapper(seq)
|
||||
assert.Equal(t, []int{2, 4, 6}, result)
|
||||
}
|
||||
|
||||
func TestMapToArrayIdentity(t *testing.T) {
|
||||
seq := From("a", "b", "c")
|
||||
mapper := MapToArray(F.Identity[string])
|
||||
result := mapper(seq)
|
||||
assert.Equal(t, []string{"a", "b", "c"}, result)
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
package record
|
||||
|
||||
import (
|
||||
Mo "github.com/IBM/fp-go/v2/monoid"
|
||||
G "github.com/IBM/fp-go/v2/record/generic"
|
||||
)
|
||||
|
||||
@@ -30,8 +29,8 @@ import (
|
||||
// Count int
|
||||
// }
|
||||
// result := record.Do[string, State]()
|
||||
func Do[K comparable, S any]() map[K]S {
|
||||
return G.Do[map[K]S]()
|
||||
func Do[K comparable, S any]() Record[K, S] {
|
||||
return G.Do[Record[K, S]]()
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return G.Bind[map[K]S1, map[K]S2, map[K]T](m)
|
||||
func Bind[S1, T any, K comparable, S2 any](m Monoid[Record[K, S2]]) func(
|
||||
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](
|
||||
setter func(T) func(S1) S2,
|
||||
f func(S1) T,
|
||||
) func(map[K]S1) map[K]S2 {
|
||||
return G.Let[map[K]S1, map[K]S2](setter, f)
|
||||
) Operator[K, S1, S2] {
|
||||
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](
|
||||
setter func(T) func(S1) S2,
|
||||
b T,
|
||||
) func(map[K]S1) map[K]S2 {
|
||||
return G.LetTo[map[K]S1, map[K]S2](setter, b)
|
||||
) Operator[K, S1, S2] {
|
||||
return G.LetTo[Record[K, S1], Record[K, S2]](setter, b)
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return G.BindTo[map[K]S1, map[K]T](setter)
|
||||
// BindTo initializes a new state [S1] from a value [T].
|
||||
// This is typically used as the first step in a do-notation chain to convert
|
||||
// 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
|
||||
@@ -126,6 +183,9 @@ func BindTo[S1, T any, K comparable](setter func(T) S1) func(map[K]T) map[K]S1 {
|
||||
// counts,
|
||||
// ),
|
||||
// ) // 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 {
|
||||
return G.ApS[map[K]S1, map[K]S2, map[K]T](m)
|
||||
func ApS[S1, T any, K comparable, S2 any](m Monoid[Record[K, S2]]) func(
|
||||
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
227
v2/record/bind_test.go
Normal 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
|
||||
85
v2/record/coverage.out
Normal file
85
v2/record/coverage.out
Normal file
@@ -0,0 +1,85 @@
|
||||
mode: set
|
||||
github.com/IBM/fp-go/v2/record/bind.go:33.40,35.2 1 0
|
||||
github.com/IBM/fp-go/v2/record/bind.go:71.144,73.2 1 0
|
||||
github.com/IBM/fp-go/v2/record/bind.go:79.27,81.2 1 0
|
||||
github.com/IBM/fp-go/v2/record/bind.go:87.27,89.2 1 0
|
||||
github.com/IBM/fp-go/v2/record/bind.go:92.80,94.2 1 0
|
||||
github.com/IBM/fp-go/v2/record/bind.go:129.135,131.2 1 0
|
||||
github.com/IBM/fp-go/v2/record/eq.go:23.55,25.2 1 0
|
||||
github.com/IBM/fp-go/v2/record/eq.go:28.56,30.2 1 1
|
||||
github.com/IBM/fp-go/v2/record/monoid.go:25.75,27.2 1 1
|
||||
github.com/IBM/fp-go/v2/record/monoid.go:30.63,32.2 1 1
|
||||
github.com/IBM/fp-go/v2/record/monoid.go:35.64,37.2 1 1
|
||||
github.com/IBM/fp-go/v2/record/monoid.go:40.59,42.2 1 1
|
||||
github.com/IBM/fp-go/v2/record/record.go:28.51,30.2 1 1
|
||||
github.com/IBM/fp-go/v2/record/record.go:33.54,35.2 1 1
|
||||
github.com/IBM/fp-go/v2/record/record.go:38.47,40.2 1 1
|
||||
github.com/IBM/fp-go/v2/record/record.go:43.49,45.2 1 1
|
||||
github.com/IBM/fp-go/v2/record/record.go:48.72,50.2 1 1
|
||||
github.com/IBM/fp-go/v2/record/record.go:53.92,55.2 1 1
|
||||
github.com/IBM/fp-go/v2/record/record.go:57.80,59.2 1 1
|
||||
github.com/IBM/fp-go/v2/record/record.go:61.92,63.2 1 1
|
||||
github.com/IBM/fp-go/v2/record/record.go:65.84,67.2 1 1
|
||||
github.com/IBM/fp-go/v2/record/record.go:69.96,71.2 1 1
|
||||
github.com/IBM/fp-go/v2/record/record.go:73.71,75.2 1 1
|
||||
github.com/IBM/fp-go/v2/record/record.go:77.83,79.2 1 1
|
||||
github.com/IBM/fp-go/v2/record/record.go:81.87,83.2 1 1
|
||||
github.com/IBM/fp-go/v2/record/record.go:85.75,87.2 1 1
|
||||
github.com/IBM/fp-go/v2/record/record.go:89.69,91.2 1 1
|
||||
github.com/IBM/fp-go/v2/record/record.go:93.73,95.2 1 1
|
||||
github.com/IBM/fp-go/v2/record/record.go:97.81,99.2 1 1
|
||||
github.com/IBM/fp-go/v2/record/record.go:101.85,103.2 1 1
|
||||
github.com/IBM/fp-go/v2/record/record.go:106.65,108.2 1 1
|
||||
github.com/IBM/fp-go/v2/record/record.go:111.67,113.2 1 1
|
||||
github.com/IBM/fp-go/v2/record/record.go:116.52,118.2 1 1
|
||||
github.com/IBM/fp-go/v2/record/record.go:120.84,122.2 1 0
|
||||
github.com/IBM/fp-go/v2/record/record.go:125.70,127.2 1 1
|
||||
github.com/IBM/fp-go/v2/record/record.go:130.43,132.2 1 1
|
||||
github.com/IBM/fp-go/v2/record/record.go:135.47,137.2 1 1
|
||||
github.com/IBM/fp-go/v2/record/record.go:139.60,141.2 1 1
|
||||
github.com/IBM/fp-go/v2/record/record.go:143.62,145.2 1 1
|
||||
github.com/IBM/fp-go/v2/record/record.go:147.65,149.2 1 1
|
||||
github.com/IBM/fp-go/v2/record/record.go:151.68,153.2 1 1
|
||||
github.com/IBM/fp-go/v2/record/record.go:155.63,157.2 1 1
|
||||
github.com/IBM/fp-go/v2/record/record.go:160.55,162.2 1 1
|
||||
github.com/IBM/fp-go/v2/record/record.go:165.103,167.2 1 1
|
||||
github.com/IBM/fp-go/v2/record/record.go:170.91,172.2 1 1
|
||||
github.com/IBM/fp-go/v2/record/record.go:175.72,177.2 1 1
|
||||
github.com/IBM/fp-go/v2/record/record.go:180.84,182.2 1 1
|
||||
github.com/IBM/fp-go/v2/record/record.go:185.49,187.2 1 1
|
||||
github.com/IBM/fp-go/v2/record/record.go:190.52,192.2 1 1
|
||||
github.com/IBM/fp-go/v2/record/record.go:195.46,197.2 1 1
|
||||
github.com/IBM/fp-go/v2/record/record.go:199.124,201.2 1 0
|
||||
github.com/IBM/fp-go/v2/record/record.go:203.112,205.2 1 1
|
||||
github.com/IBM/fp-go/v2/record/record.go:207.125,209.2 1 0
|
||||
github.com/IBM/fp-go/v2/record/record.go:211.113,213.2 1 1
|
||||
github.com/IBM/fp-go/v2/record/record.go:216.85,218.2 1 1
|
||||
github.com/IBM/fp-go/v2/record/record.go:221.141,223.2 1 1
|
||||
github.com/IBM/fp-go/v2/record/record.go:226.129,228.2 1 0
|
||||
github.com/IBM/fp-go/v2/record/record.go:231.86,233.2 1 1
|
||||
github.com/IBM/fp-go/v2/record/record.go:236.98,238.2 1 0
|
||||
github.com/IBM/fp-go/v2/record/record.go:241.64,243.2 1 1
|
||||
github.com/IBM/fp-go/v2/record/record.go:246.104,248.2 1 0
|
||||
github.com/IBM/fp-go/v2/record/record.go:251.92,253.2 1 0
|
||||
github.com/IBM/fp-go/v2/record/record.go:256.108,258.2 1 1
|
||||
github.com/IBM/fp-go/v2/record/record.go:261.86,263.2 1 0
|
||||
github.com/IBM/fp-go/v2/record/record.go:266.120,268.2 1 0
|
||||
github.com/IBM/fp-go/v2/record/record.go:271.69,273.2 1 1
|
||||
github.com/IBM/fp-go/v2/record/record.go:276.71,278.2 1 1
|
||||
github.com/IBM/fp-go/v2/record/record.go:280.78,282.2 1 1
|
||||
github.com/IBM/fp-go/v2/record/record.go:284.74,286.2 1 1
|
||||
github.com/IBM/fp-go/v2/record/record.go:289.51,291.2 1 1
|
||||
github.com/IBM/fp-go/v2/record/record.go:294.80,296.2 1 1
|
||||
github.com/IBM/fp-go/v2/record/record.go:305.88,307.2 1 0
|
||||
github.com/IBM/fp-go/v2/record/record.go:314.73,316.2 1 1
|
||||
github.com/IBM/fp-go/v2/record/record.go:324.60,326.2 1 0
|
||||
github.com/IBM/fp-go/v2/record/record.go:332.55,334.2 1 1
|
||||
github.com/IBM/fp-go/v2/record/record.go:336.105,338.2 1 1
|
||||
github.com/IBM/fp-go/v2/record/record.go:340.106,342.2 1 1
|
||||
github.com/IBM/fp-go/v2/record/record.go:344.48,346.2 1 1
|
||||
github.com/IBM/fp-go/v2/record/semigroup.go:53.81,55.2 1 1
|
||||
github.com/IBM/fp-go/v2/record/semigroup.go:80.69,82.2 1 1
|
||||
github.com/IBM/fp-go/v2/record/semigroup.go:107.70,109.2 1 1
|
||||
github.com/IBM/fp-go/v2/record/traverse.go:27.41,29.2 1 1
|
||||
github.com/IBM/fp-go/v2/record/traverse.go:39.38,41.2 1 1
|
||||
github.com/IBM/fp-go/v2/record/traverse.go:50.23,53.2 1 1
|
||||
@@ -20,11 +20,11 @@ import (
|
||||
G "github.com/IBM/fp-go/v2/record/generic"
|
||||
)
|
||||
|
||||
func Eq[K comparable, V any](e E.Eq[V]) E.Eq[map[K]V] {
|
||||
return G.Eq[map[K]V](e)
|
||||
func Eq[K comparable, V any](e E.Eq[V]) E.Eq[Record[K, V]] {
|
||||
return G.Eq[Record[K, V]](e)
|
||||
}
|
||||
|
||||
// FromStrictEquals constructs an [EQ.Eq] from the canonical comparison function
|
||||
func FromStrictEquals[K, V comparable]() E.Eq[map[K]V] {
|
||||
return G.FromStrictEquals[map[K]V]()
|
||||
func FromStrictEquals[K, V comparable]() E.Eq[Record[K, V]] {
|
||||
return G.FromStrictEquals[Record[K, V]]()
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ import (
|
||||
Mo "github.com/IBM/fp-go/v2/monoid"
|
||||
O "github.com/IBM/fp-go/v2/option"
|
||||
"github.com/IBM/fp-go/v2/ord"
|
||||
T "github.com/IBM/fp-go/v2/tuple"
|
||||
"github.com/IBM/fp-go/v2/pair"
|
||||
)
|
||||
|
||||
func IsEmpty[M ~map[K]V, K comparable, V any](r M) bool {
|
||||
@@ -59,9 +59,9 @@ func ValuesOrd[M ~map[K]V, GV ~[]V, K comparable, V any](o ord.Ord[K]) func(r M)
|
||||
|
||||
func collectOrd[M ~map[K]V, GR ~[]R, K comparable, V, R any](o ord.Ord[K], r M, f func(K, V) R) GR {
|
||||
// create the entries
|
||||
entries := toEntriesOrd[M, []T.Tuple2[K, V]](o, r)
|
||||
entries := toEntriesOrd[M, []pair.Pair[K, V]](o, r)
|
||||
// collect this array
|
||||
ft := T.Tupled2(f)
|
||||
ft := pair.Paired(f)
|
||||
count := len(entries)
|
||||
result := make(GR, count)
|
||||
for i := count - 1; i >= 0; i-- {
|
||||
@@ -73,13 +73,13 @@ func collectOrd[M ~map[K]V, GR ~[]R, K comparable, V, R any](o ord.Ord[K], r M,
|
||||
|
||||
func reduceOrd[M ~map[K]V, K comparable, V, R any](o ord.Ord[K], r M, f func(K, R, V) R, initial R) R {
|
||||
// create the entries
|
||||
entries := toEntriesOrd[M, []T.Tuple2[K, V]](o, r)
|
||||
entries := toEntriesOrd[M, []pair.Pair[K, V]](o, r)
|
||||
// collect this array
|
||||
current := initial
|
||||
count := len(entries)
|
||||
for i := 0; i < count; i++ {
|
||||
t := entries[i]
|
||||
current = f(T.First(t), current, T.Second(t))
|
||||
current = f(pair.Head(t), current, pair.Tail(t))
|
||||
}
|
||||
// done
|
||||
return current
|
||||
@@ -318,32 +318,32 @@ 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 ToArray[M ~map[K]V, GT ~[]pair.Pair[K, V], K comparable, V any](r M) GT {
|
||||
return collect[M, GT](r, pair.MakePair[K, V])
|
||||
}
|
||||
|
||||
func toEntriesOrd[M ~map[K]V, GT ~[]T.Tuple2[K, V], K comparable, V any](o ord.Ord[K], r M) GT {
|
||||
func toEntriesOrd[M ~map[K]V, GT ~[]pair.Pair[K, V], K comparable, V any](o ord.Ord[K], r M) GT {
|
||||
// total number of elements
|
||||
count := len(r)
|
||||
// produce an array that we can sort by key
|
||||
entries := make(GT, count)
|
||||
idx := 0
|
||||
for k, v := range r {
|
||||
entries[idx] = T.MakeTuple2(k, v)
|
||||
entries[idx] = pair.MakePair(k, v)
|
||||
idx++
|
||||
}
|
||||
sort.Slice(entries, func(i, j int) bool {
|
||||
return o.Compare(T.First(entries[i]), T.First(entries[j])) < 0
|
||||
return o.Compare(pair.Head(entries[i]), pair.Head(entries[j])) < 0
|
||||
})
|
||||
// final entries
|
||||
return entries
|
||||
}
|
||||
|
||||
func ToEntriesOrd[M ~map[K]V, GT ~[]T.Tuple2[K, V], K comparable, V any](o ord.Ord[K]) func(r M) GT {
|
||||
func ToEntriesOrd[M ~map[K]V, GT ~[]pair.Pair[K, V], K comparable, V any](o ord.Ord[K]) func(r M) GT {
|
||||
return F.Bind1st(toEntriesOrd[M, GT, K, V], o)
|
||||
}
|
||||
|
||||
func ToEntries[M ~map[K]V, GT ~[]T.Tuple2[K, V], K comparable, V any](r M) GT {
|
||||
func ToEntries[M ~map[K]V, GT ~[]pair.Pair[K, V], K comparable, V any](r M) GT {
|
||||
return ToArray[M, GT](r)
|
||||
}
|
||||
|
||||
@@ -351,7 +351,7 @@ func ToEntries[M ~map[K]V, GT ~[]T.Tuple2[K, V], K comparable, V any](r M) GT {
|
||||
// its values into a tuple. The key and value are then used to populate the map. Duplicate
|
||||
// values are resolved via the provided [Mg.Magma]
|
||||
func FromFoldableMap[
|
||||
FCT ~func(A) T.Tuple2[K, V],
|
||||
FCT ~func(A) pair.Pair[K, V],
|
||||
HKTA any,
|
||||
FOLDABLE ~func(func(M, A) M, M) func(HKTA) M,
|
||||
M ~map[K]V,
|
||||
@@ -364,12 +364,12 @@ func FromFoldableMap[
|
||||
dst = make(M)
|
||||
}
|
||||
e := f(a)
|
||||
k := T.First(e)
|
||||
k := pair.Head(e)
|
||||
old, ok := dst[k]
|
||||
if ok {
|
||||
dst[k] = m.Concat(old, T.Second(e))
|
||||
dst[k] = m.Concat(old, pair.Tail(e))
|
||||
} else {
|
||||
dst[k] = T.Second(e)
|
||||
dst[k] = pair.Tail(e)
|
||||
}
|
||||
return dst
|
||||
}, Empty[M]())
|
||||
@@ -378,15 +378,15 @@ func FromFoldableMap[
|
||||
|
||||
func FromFoldable[
|
||||
HKTA any,
|
||||
FOLDABLE ~func(func(M, T.Tuple2[K, V]) M, M) func(HKTA) M,
|
||||
FOLDABLE ~func(func(M, pair.Pair[K, V]) M, M) func(HKTA) M,
|
||||
M ~map[K]V,
|
||||
K comparable,
|
||||
V any](m Mg.Magma[V], red FOLDABLE) func(fa HKTA) M {
|
||||
return FromFoldableMap[func(T.Tuple2[K, V]) T.Tuple2[K, V]](m, red)(F.Identity[T.Tuple2[K, V]])
|
||||
return FromFoldableMap[func(pair.Pair[K, V]) pair.Pair[K, V]](m, red)(F.Identity[pair.Pair[K, V]])
|
||||
}
|
||||
|
||||
func FromArrayMap[
|
||||
FCT ~func(A) T.Tuple2[K, V],
|
||||
FCT ~func(A) pair.Pair[K, V],
|
||||
GA ~[]A,
|
||||
M ~map[K]V,
|
||||
A any,
|
||||
@@ -396,17 +396,17 @@ func FromArrayMap[
|
||||
}
|
||||
|
||||
func FromArray[
|
||||
GA ~[]T.Tuple2[K, V],
|
||||
GA ~[]pair.Pair[K, V],
|
||||
M ~map[K]V,
|
||||
K comparable,
|
||||
V any](m Mg.Magma[V]) func(fa GA) M {
|
||||
return FromFoldable(m, F.Bind23of3(RAG.Reduce[GA, T.Tuple2[K, V], M]))
|
||||
return FromFoldable(m, F.Bind23of3(RAG.Reduce[GA, pair.Pair[K, V], M]))
|
||||
}
|
||||
|
||||
func FromEntries[M ~map[K]V, GT ~[]T.Tuple2[K, V], K comparable, V any](fa GT) M {
|
||||
func FromEntries[M ~map[K]V, GT ~[]pair.Pair[K, V], K comparable, V any](fa GT) M {
|
||||
m := make(M)
|
||||
for _, t := range fa {
|
||||
upsertAtReadWrite(m, t.F1, t.F2)
|
||||
upsertAtReadWrite(m, pair.Head(t), pair.Tail(t))
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
@@ -16,27 +16,34 @@
|
||||
package record
|
||||
|
||||
import (
|
||||
M "github.com/IBM/fp-go/v2/monoid"
|
||||
G "github.com/IBM/fp-go/v2/record/generic"
|
||||
S "github.com/IBM/fp-go/v2/semigroup"
|
||||
)
|
||||
|
||||
// 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
|
||||
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
|
||||
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
|
||||
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]]()
|
||||
}
|
||||
|
||||
@@ -16,295 +16,316 @@
|
||||
package record
|
||||
|
||||
import (
|
||||
EM "github.com/IBM/fp-go/v2/endomorphism"
|
||||
Mg "github.com/IBM/fp-go/v2/magma"
|
||||
Mo "github.com/IBM/fp-go/v2/monoid"
|
||||
O "github.com/IBM/fp-go/v2/option"
|
||||
"github.com/IBM/fp-go/v2/option"
|
||||
"github.com/IBM/fp-go/v2/ord"
|
||||
G "github.com/IBM/fp-go/v2/record/generic"
|
||||
T "github.com/IBM/fp-go/v2/tuple"
|
||||
)
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// Keys returns the key in a map
|
||||
func Keys[K comparable, V any](r map[K]V) []K {
|
||||
return G.Keys[map[K]V, []K](r)
|
||||
func Keys[K comparable, V any](r Record[K, V]) []K {
|
||||
return G.Keys[Record[K, V], []K](r)
|
||||
}
|
||||
|
||||
// Values returns the values in a map
|
||||
func Values[K comparable, V any](r map[K]V) []V {
|
||||
return G.Values[map[K]V, []V](r)
|
||||
func Values[K comparable, V any](r Record[K, V]) []V {
|
||||
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
|
||||
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 Collect[K comparable, V, R any](f func(K, V) R) func(Record[K, V]) []R {
|
||||
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
|
||||
func CollectOrd[V, R any, K comparable](o ord.Ord[K]) func(func(K, V) R) func(map[K]V) []R {
|
||||
return G.CollectOrd[map[K]V, []R](o)
|
||||
func CollectOrd[V, R any, K comparable](o ord.Ord[K]) func(func(K, V) R) func(Record[K, V]) []R {
|
||||
return G.CollectOrd[Record[K, V], []R](o)
|
||||
}
|
||||
|
||||
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)
|
||||
// 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(Record[K, V]) R {
|
||||
return G.Reduce[Record[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)
|
||||
// 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(Record[K, V]) R {
|
||||
return G.ReduceWithIndex[Record[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)
|
||||
// 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(Record[K, V]) R {
|
||||
return G.ReduceRef[Record[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)
|
||||
// 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(Record[K, V]) R {
|
||||
return G.ReduceRefWithIndex[Record[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)
|
||||
// MonadMap transforms each value in a map using the provided function
|
||||
func MonadMap[K comparable, V, R any](r Record[K, V], f func(V) R) Record[K, R] {
|
||||
return G.MonadMap[Record[K, V], Record[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)
|
||||
// MonadMapWithIndex transforms each key-value pair in a map using the provided function
|
||||
func MonadMapWithIndex[K comparable, V, R any](r Record[K, V], f func(K, V) R) Record[K, R] {
|
||||
return G.MonadMapWithIndex[Record[K, V], Record[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)
|
||||
// MonadMapRefWithIndex transforms each key-value pair in a map using the provided function with value references
|
||||
func MonadMapRefWithIndex[K comparable, V, R any](r Record[K, V], f func(K, *V) R) Record[K, R] {
|
||||
return G.MonadMapRefWithIndex[Record[K, V], Record[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)
|
||||
// MonadMapRef transforms each value in a map using the provided function with value references
|
||||
func MonadMapRef[K comparable, V, R any](r Record[K, V], f func(*V) R) Record[K, R] {
|
||||
return G.MonadMapRef[Record[K, V], Record[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)
|
||||
// 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] {
|
||||
return G.Map[Record[K, V], Record[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)
|
||||
// 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] {
|
||||
return G.MapRef[Record[K, V], Record[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)
|
||||
// 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] {
|
||||
return G.MapWithIndex[Record[K, V], Record[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)
|
||||
// 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] {
|
||||
return G.MapRefWithIndex[Record[K, V], Record[K, R]](f)
|
||||
}
|
||||
|
||||
// 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] {
|
||||
return G.Lookup[map[K]V](k)
|
||||
func Lookup[V any, K comparable](k K) option.Kleisli[Record[K, V], V] {
|
||||
return G.Lookup[Record[K, V]](k)
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
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)
|
||||
// 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(Record[K, V]) Operator[K, V, V] {
|
||||
return G.Union[Record[K, V]](m)
|
||||
}
|
||||
|
||||
// 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) func(map[K]V) map[K]V {
|
||||
func Merge[K comparable, V any](right Record[K, V]) Operator[K, V, V] {
|
||||
return G.Merge(right)
|
||||
}
|
||||
|
||||
// Empty creates an empty map
|
||||
func Empty[K comparable, V any]() map[K]V {
|
||||
return G.Empty[map[K]V]()
|
||||
func Empty[K comparable, V any]() Record[K, V] {
|
||||
return G.Empty[Record[K, V]]()
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
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)
|
||||
// ToArray converts a map to an array of key-value pairs
|
||||
func ToArray[K comparable, V any](r Record[K, V]) Entries[K, V] {
|
||||
return G.ToArray[Record[K, V], Entries[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)
|
||||
// ToEntries converts a map to an array of key-value pairs (alias for ToArray)
|
||||
func ToEntries[K comparable, V any](r Record[K, V]) Entries[K, V] {
|
||||
return G.ToEntries[Record[K, V], Entries[K, V]](r)
|
||||
}
|
||||
|
||||
func FromEntries[K comparable, V any](fa []T.Tuple2[K, V]) map[K]V {
|
||||
return G.FromEntries[map[K]V](fa)
|
||||
// FromEntries creates a map from an array of key-value pairs
|
||||
func FromEntries[K comparable, V any](fa Entries[K, V]) Record[K, V] {
|
||||
return G.FromEntries[Record[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)
|
||||
// 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] {
|
||||
return G.UpsertAt[Record[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)
|
||||
// DeleteAt returns a function that removes a key from a map
|
||||
func DeleteAt[K comparable, V any](k K) Operator[K, V, V] {
|
||||
return G.DeleteAt[Record[K, V]](k)
|
||||
}
|
||||
|
||||
// Singleton creates a new map with a single entry
|
||||
func Singleton[K comparable, V any](k K, v V) map[K]V {
|
||||
return G.Singleton[map[K]V](k, v)
|
||||
func Singleton[K comparable, V any](k K, v V) Record[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
|
||||
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)
|
||||
func FilterMapWithIndex[K comparable, V1, V2 any](f func(K, V1) Option[V2]) Operator[K, V1, V2] {
|
||||
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
|
||||
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)
|
||||
func FilterMap[K comparable, V1, V2 any](f option.Kleisli[V1, V2]) Operator[K, V1, V2] {
|
||||
return G.FilterMap[Record[K, V1], Record[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)
|
||||
func Filter[K comparable, V any](f Predicate[K]) Operator[K, V, V] {
|
||||
return G.Filter[Record[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)
|
||||
func FilterWithIndex[K comparable, V any](f PredicateWithIndex[K, V]) Operator[K, V, V] {
|
||||
return G.FilterWithIndex[Record[K, V]](f)
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// ConstNil return a nil map
|
||||
func ConstNil[K comparable, V any]() map[K]V {
|
||||
return map[K]V(nil)
|
||||
func ConstNil[K comparable, V any]() Record[K, V] {
|
||||
return Record[K, V](nil)
|
||||
}
|
||||
|
||||
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 {
|
||||
// MonadChainWithIndex chains a map transformation function that produces maps, combining results using the provided Monoid
|
||||
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)
|
||||
}
|
||||
|
||||
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 {
|
||||
// MonadChain chains a map transformation function that produces maps, combining results using the provided Monoid
|
||||
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)
|
||||
}
|
||||
|
||||
func ChainWithIndex[V1 any, K comparable, V2 any](m Mo.Monoid[map[K]V2]) func(func(K, V1) map[K]V2) func(map[K]V1) map[K]V2 {
|
||||
return G.ChainWithIndex[map[K]V1](m)
|
||||
// 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 Monoid[Record[K, V2]]) func(KleisliWithIndex[K, V1, V2]) Operator[K, V1, V2] {
|
||||
return G.ChainWithIndex[Record[K, V1]](m)
|
||||
}
|
||||
|
||||
func Chain[V1 any, K comparable, V2 any](m Mo.Monoid[map[K]V2]) func(func(V1) map[K]V2) func(map[K]V1) map[K]V2 {
|
||||
return G.Chain[map[K]V1](m)
|
||||
// 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 Monoid[Record[K, V2]]) func(Kleisli[K, V1, V2]) Operator[K, V1, V2] {
|
||||
return G.Chain[Record[K, V1]](m)
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return G.Flatten[map[K]map[K]V](m)
|
||||
func Flatten[K comparable, V any](m Monoid[Record[K, V]]) func(Record[K, Record[K, V]]) Record[K, V] {
|
||||
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
|
||||
func FilterChainWithIndex[V1 any, K comparable, V2 any](m Mo.Monoid[map[K]V2]) func(func(K, V1) O.Option[map[K]V2]) func(map[K]V1) map[K]V2 {
|
||||
return G.FilterChainWithIndex[map[K]V1](m)
|
||||
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[Record[K, V1]](m)
|
||||
}
|
||||
|
||||
// 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]) func(map[K]V1) map[K]V2 {
|
||||
return G.FilterChain[map[K]V1](m)
|
||||
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[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.
|
||||
func FoldMap[K comparable, A, B any](m Mo.Monoid[B]) func(func(A) B) func(map[K]A) B {
|
||||
return G.FoldMap[map[K]A](m)
|
||||
func FoldMap[K comparable, A, B any](m Monoid[B]) func(func(A) B) func(Record[K, A]) B {
|
||||
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.
|
||||
func FoldMapWithIndex[K comparable, A, B any](m Mo.Monoid[B]) func(func(K, A) B) func(map[K]A) B {
|
||||
return G.FoldMapWithIndex[map[K]A](m)
|
||||
func FoldMapWithIndex[K comparable, A, B any](m Monoid[B]) func(func(K, A) B) func(Record[K, A]) B {
|
||||
return G.FoldMapWithIndex[Record[K, A]](m)
|
||||
}
|
||||
|
||||
// Fold folds the record using the provided Monoid.
|
||||
func Fold[K comparable, A any](m Mo.Monoid[A]) func(map[K]A) A {
|
||||
return G.Fold[map[K]A](m)
|
||||
func Fold[K comparable, A any](m Monoid[A]) func(Record[K, A]) A {
|
||||
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
|
||||
func ReduceOrdWithIndex[V, R any, K comparable](o ord.Ord[K]) func(func(K, R, V) R, R) func(map[K]V) R {
|
||||
return G.ReduceOrdWithIndex[map[K]V, K, V, R](o)
|
||||
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[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
|
||||
func ReduceOrd[V, R any, K comparable](o ord.Ord[K]) func(func(R, V) R, R) func(map[K]V) R {
|
||||
return G.ReduceOrd[map[K]V, K, V, R](o)
|
||||
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[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
|
||||
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 {
|
||||
return G.FoldMapOrd[map[K]A, K, A, B](o)
|
||||
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[Record[K, A], K, A, B](o)
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return G.FoldOrd[map[K]A](o)
|
||||
func FoldOrd[A any, K comparable](o ord.Ord[K]) func(m Monoid[A]) func(Record[K, A]) A {
|
||||
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
|
||||
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 {
|
||||
return G.FoldMapOrdWithIndex[map[K]A, K, A, B](o)
|
||||
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[Record[K, A], K, A, B](o)
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return G.KeysOrd[map[K]V, []K](o)
|
||||
func KeysOrd[V any, K comparable](o ord.Ord[K]) func(r Record[K, V]) []K {
|
||||
return G.KeysOrd[Record[K, V], []K](o)
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return G.ValuesOrd[map[K]V, []V](o)
|
||||
func ValuesOrd[V any, K comparable](o ord.Ord[K]) func(r Record[K, V]) []V {
|
||||
return G.ValuesOrd[Record[K, V], []V](o)
|
||||
}
|
||||
|
||||
func MonadFlap[B any, K comparable, A any](fab map[K]func(A) B, a A) map[K]B {
|
||||
return G.MonadFlap[map[K]func(A) B, map[K]B](fab, a)
|
||||
// MonadFlap applies a value to a map of functions, producing a map of results
|
||||
func MonadFlap[B any, K comparable, A any](fab Record[K, func(A) B], a A) Record[K, B] {
|
||||
return G.MonadFlap[Record[K, func(A) B], Record[K, B]](fab, a)
|
||||
}
|
||||
|
||||
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)
|
||||
// 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) Operator[K, func(A) B, B] {
|
||||
return G.Flap[Record[K, func(A) B], Record[K, B]](a)
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// 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] {
|
||||
return G.Clone[map[K]V](f)
|
||||
func Clone[K comparable, V any](f Endomorphism[V]) Endomorphism[Record[K, V]] {
|
||||
return G.Clone[Record[K, V]](f)
|
||||
}
|
||||
|
||||
// FromFoldableMap converts from a reducer to a map
|
||||
// Duplicate keys are resolved by the provided [Mg.Magma]
|
||||
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,
|
||||
HKTA any,
|
||||
K comparable,
|
||||
V any](m Mg.Magma[V], red FOLDABLE) func(f func(A) T.Tuple2[K, V]) func(fa HKTA) map[K]V {
|
||||
return G.FromFoldableMap[func(A) T.Tuple2[K, V]](m, red)
|
||||
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)
|
||||
}
|
||||
|
||||
// FromArrayMap converts from an array to a map
|
||||
@@ -312,17 +333,17 @@ func FromFoldableMap[
|
||||
func FromArrayMap[
|
||||
A any,
|
||||
K comparable,
|
||||
V any](m Mg.Magma[V]) func(f func(A) T.Tuple2[K, V]) func(fa []A) map[K]V {
|
||||
return G.FromArrayMap[func(A) T.Tuple2[K, V], []A, map[K]V](m)
|
||||
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, Record[K, V]](m)
|
||||
}
|
||||
|
||||
// FromFoldable converts from a reducer to a map
|
||||
// Duplicate keys are resolved by the provided [Mg.Magma]
|
||||
func FromFoldable[
|
||||
HKTA any,
|
||||
FOLDABLE ~func(func(map[K]V, T.Tuple2[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,
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -330,14 +351,21 @@ func FromFoldable[
|
||||
// Duplicate keys are resolved by the provided [Mg.Magma]
|
||||
func FromArray[
|
||||
K comparable,
|
||||
V any](m Mg.Magma[V]) func(fa []T.Tuple2[K, V]) map[K]V {
|
||||
return G.FromArray[[]T.Tuple2[K, V], map[K]V](m)
|
||||
V any](m Mg.Magma[V]) Kleisli[K, Entries[K, V], V] {
|
||||
return G.FromArray[Entries[K, V], Record[K, V]](m)
|
||||
}
|
||||
|
||||
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 {
|
||||
// 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 Monoid[Record[K, B]], fab Record[K, func(A) B], fa Record[K, A]) Record[K, B] {
|
||||
return G.MonadAp(m, fab, fa)
|
||||
}
|
||||
|
||||
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 {
|
||||
return G.Ap[map[K]B, map[K]func(A) B, map[K]A](m)
|
||||
// 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 Monoid[Record[K, B]]) func(fa Record[K, A]) Operator[K, func(A) B, B] {
|
||||
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
|
||||
func Of[K comparable, A any](k K, a A) Record[K, A] {
|
||||
return Record[K, A]{k: a}
|
||||
}
|
||||
|
||||
@@ -25,8 +25,8 @@ import (
|
||||
"github.com/IBM/fp-go/v2/internal/utils"
|
||||
Mg "github.com/IBM/fp-go/v2/magma"
|
||||
O "github.com/IBM/fp-go/v2/option"
|
||||
P "github.com/IBM/fp-go/v2/pair"
|
||||
S "github.com/IBM/fp-go/v2/string"
|
||||
T "github.com/IBM/fp-go/v2/tuple"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -156,7 +156,7 @@ func TestFromArrayMap(t *testing.T) {
|
||||
src1 := A.From("a", "b", "c", "a")
|
||||
frm := FromArrayMap[string, string](Mg.Second[string]())
|
||||
|
||||
f := frm(T.Replicate2[string])
|
||||
f := frm(P.Of[string])
|
||||
|
||||
res1 := f(src1)
|
||||
|
||||
@@ -198,3 +198,555 @@ func TestHas(t *testing.T) {
|
||||
assert.True(t, Has("a", nonEmpty))
|
||||
assert.False(t, Has("c", nonEmpty))
|
||||
}
|
||||
|
||||
func TestCollect(t *testing.T) {
|
||||
data := map[string]int{
|
||||
"a": 1,
|
||||
"b": 2,
|
||||
"c": 3,
|
||||
}
|
||||
collector := Collect[string, int, string](func(k string, v int) string {
|
||||
return fmt.Sprintf("%s=%d", k, v)
|
||||
})
|
||||
result := collector(data)
|
||||
sort.Strings(result)
|
||||
assert.Equal(t, []string{"a=1", "b=2", "c=3"}, result)
|
||||
}
|
||||
|
||||
func TestCollectOrd(t *testing.T) {
|
||||
data := map[string]int{
|
||||
"c": 3,
|
||||
"a": 1,
|
||||
"b": 2,
|
||||
}
|
||||
collector := CollectOrd[int, string](S.Ord)(func(k string, v int) string {
|
||||
return fmt.Sprintf("%s=%d", k, v)
|
||||
})
|
||||
result := collector(data)
|
||||
assert.Equal(t, []string{"a=1", "b=2", "c=3"}, result)
|
||||
}
|
||||
|
||||
func TestReduce(t *testing.T) {
|
||||
data := map[string]int{
|
||||
"a": 1,
|
||||
"b": 2,
|
||||
"c": 3,
|
||||
}
|
||||
sum := Reduce[string, int, int](func(acc, v int) int {
|
||||
return acc + v
|
||||
}, 0)
|
||||
result := sum(data)
|
||||
assert.Equal(t, 6, result)
|
||||
}
|
||||
|
||||
func TestReduceWithIndex(t *testing.T) {
|
||||
data := map[string]int{
|
||||
"a": 1,
|
||||
"b": 2,
|
||||
"c": 3,
|
||||
}
|
||||
concat := ReduceWithIndex[string, int, string](func(k string, acc string, v int) string {
|
||||
if acc == "" {
|
||||
return fmt.Sprintf("%s:%d", k, v)
|
||||
}
|
||||
return fmt.Sprintf("%s,%s:%d", acc, k, v)
|
||||
}, "")
|
||||
result := concat(data)
|
||||
// Result order is non-deterministic, so check it contains all parts
|
||||
assert.Contains(t, result, "a:1")
|
||||
assert.Contains(t, result, "b:2")
|
||||
assert.Contains(t, result, "c:3")
|
||||
}
|
||||
|
||||
func TestMonadMap(t *testing.T) {
|
||||
data := map[string]int{
|
||||
"a": 1,
|
||||
"b": 2,
|
||||
"c": 3,
|
||||
}
|
||||
result := MonadMap(data, func(v int) int { return v * 2 })
|
||||
assert.Equal(t, map[string]int{"a": 2, "b": 4, "c": 6}, result)
|
||||
}
|
||||
|
||||
func TestMonadMapWithIndex(t *testing.T) {
|
||||
data := map[string]int{
|
||||
"a": 1,
|
||||
"b": 2,
|
||||
}
|
||||
result := MonadMapWithIndex(data, func(k string, v int) string {
|
||||
return fmt.Sprintf("%s=%d", k, v)
|
||||
})
|
||||
assert.Equal(t, map[string]string{"a": "a=1", "b": "b=2"}, result)
|
||||
}
|
||||
|
||||
func TestMapWithIndex(t *testing.T) {
|
||||
data := map[string]int{
|
||||
"a": 1,
|
||||
"b": 2,
|
||||
}
|
||||
mapper := MapWithIndex[string, int, string](func(k string, v int) string {
|
||||
return fmt.Sprintf("%s=%d", k, v)
|
||||
})
|
||||
result := mapper(data)
|
||||
assert.Equal(t, map[string]string{"a": "a=1", "b": "b=2"}, result)
|
||||
}
|
||||
|
||||
func TestMonadLookup(t *testing.T) {
|
||||
data := map[string]int{
|
||||
"a": 1,
|
||||
"b": 2,
|
||||
}
|
||||
assert.Equal(t, O.Some(1), MonadLookup(data, "a"))
|
||||
assert.Equal(t, O.None[int](), MonadLookup(data, "c"))
|
||||
}
|
||||
|
||||
func TestMerge(t *testing.T) {
|
||||
left := map[string]int{"a": 1, "b": 2}
|
||||
right := map[string]int{"b": 3, "c": 4}
|
||||
result := Merge(right)(left)
|
||||
assert.Equal(t, map[string]int{"a": 1, "b": 3, "c": 4}, result)
|
||||
}
|
||||
|
||||
func TestSize(t *testing.T) {
|
||||
data := map[string]int{"a": 1, "b": 2, "c": 3}
|
||||
assert.Equal(t, 3, Size(data))
|
||||
assert.Equal(t, 0, Size(Empty[string, int]()))
|
||||
}
|
||||
|
||||
func TestToArray(t *testing.T) {
|
||||
data := map[string]int{"a": 1, "b": 2}
|
||||
result := ToArray(data)
|
||||
assert.Len(t, result, 2)
|
||||
// Check both entries exist (order is non-deterministic)
|
||||
found := make(map[string]int)
|
||||
for _, entry := range result {
|
||||
found[P.Head(entry)] = P.Tail(entry)
|
||||
}
|
||||
assert.Equal(t, data, found)
|
||||
}
|
||||
|
||||
func TestToEntries(t *testing.T) {
|
||||
data := map[string]int{"a": 1, "b": 2}
|
||||
result := ToEntries(data)
|
||||
assert.Len(t, result, 2)
|
||||
}
|
||||
|
||||
func TestFromEntries(t *testing.T) {
|
||||
entries := Entries[string, int]{
|
||||
P.MakePair("a", 1),
|
||||
P.MakePair("b", 2),
|
||||
}
|
||||
result := FromEntries(entries)
|
||||
assert.Equal(t, map[string]int{"a": 1, "b": 2}, result)
|
||||
}
|
||||
|
||||
func TestUpsertAt(t *testing.T) {
|
||||
data := map[string]int{"a": 1, "b": 2}
|
||||
result := UpsertAt("c", 3)(data)
|
||||
assert.Equal(t, map[string]int{"a": 1, "b": 2, "c": 3}, result)
|
||||
// Original should be unchanged
|
||||
assert.Equal(t, map[string]int{"a": 1, "b": 2}, data)
|
||||
|
||||
// Update existing
|
||||
result2 := UpsertAt("a", 10)(data)
|
||||
assert.Equal(t, map[string]int{"a": 10, "b": 2}, result2)
|
||||
}
|
||||
|
||||
func TestDeleteAt(t *testing.T) {
|
||||
data := map[string]int{"a": 1, "b": 2, "c": 3}
|
||||
result := DeleteAt[string, int]("b")(data)
|
||||
assert.Equal(t, map[string]int{"a": 1, "c": 3}, result)
|
||||
// Original should be unchanged
|
||||
assert.Equal(t, map[string]int{"a": 1, "b": 2, "c": 3}, data)
|
||||
}
|
||||
|
||||
func TestSingleton(t *testing.T) {
|
||||
result := Singleton("key", 42)
|
||||
assert.Equal(t, map[string]int{"key": 42}, result)
|
||||
}
|
||||
|
||||
func TestFilterMapWithIndex(t *testing.T) {
|
||||
data := map[string]int{"a": 1, "b": 2, "c": 3}
|
||||
filter := FilterMapWithIndex[string, int, int](func(k string, v int) O.Option[int] {
|
||||
if v%2 == 0 {
|
||||
return O.Some(v * 10)
|
||||
}
|
||||
return O.None[int]()
|
||||
})
|
||||
result := filter(data)
|
||||
assert.Equal(t, map[string]int{"b": 20}, result)
|
||||
}
|
||||
|
||||
func TestFilterMap(t *testing.T) {
|
||||
data := map[string]int{"a": 1, "b": 2, "c": 3}
|
||||
filter := FilterMap[string, int, int](func(v int) O.Option[int] {
|
||||
if v%2 == 0 {
|
||||
return O.Some(v * 10)
|
||||
}
|
||||
return O.None[int]()
|
||||
})
|
||||
result := filter(data)
|
||||
assert.Equal(t, map[string]int{"b": 20}, result)
|
||||
}
|
||||
|
||||
func TestFilter(t *testing.T) {
|
||||
data := map[string]int{"a": 1, "b": 2, "c": 3}
|
||||
filter := Filter[string, int](func(k string) bool {
|
||||
return k != "b"
|
||||
})
|
||||
result := filter(data)
|
||||
assert.Equal(t, map[string]int{"a": 1, "c": 3}, result)
|
||||
}
|
||||
|
||||
func TestFilterWithIndex(t *testing.T) {
|
||||
data := map[string]int{"a": 1, "b": 2, "c": 3}
|
||||
filter := FilterWithIndex[string, int](func(k string, v int) bool {
|
||||
return v%2 == 0
|
||||
})
|
||||
result := filter(data)
|
||||
assert.Equal(t, map[string]int{"b": 2}, result)
|
||||
}
|
||||
|
||||
func TestIsNil(t *testing.T) {
|
||||
var nilMap map[string]int
|
||||
nonNilMap := map[string]int{}
|
||||
|
||||
assert.True(t, IsNil(nilMap))
|
||||
assert.False(t, IsNil(nonNilMap))
|
||||
}
|
||||
|
||||
func TestIsNonNil(t *testing.T) {
|
||||
var nilMap map[string]int
|
||||
nonNilMap := map[string]int{}
|
||||
|
||||
assert.False(t, IsNonNil(nilMap))
|
||||
assert.True(t, IsNonNil(nonNilMap))
|
||||
}
|
||||
|
||||
func TestConstNil(t *testing.T) {
|
||||
result := ConstNil[string, int]()
|
||||
assert.Nil(t, result)
|
||||
assert.True(t, IsNil(result))
|
||||
}
|
||||
|
||||
func TestMonadChain(t *testing.T) {
|
||||
data := map[string]int{"a": 1, "b": 2}
|
||||
monoid := MergeMonoid[string, int]()
|
||||
result := MonadChain(monoid, data, func(v int) map[string]int {
|
||||
return map[string]int{
|
||||
fmt.Sprintf("x%d", v): v * 10,
|
||||
}
|
||||
})
|
||||
assert.Equal(t, map[string]int{"x1": 10, "x2": 20}, result)
|
||||
}
|
||||
|
||||
func TestChain(t *testing.T) {
|
||||
data := map[string]int{"a": 1, "b": 2}
|
||||
monoid := MergeMonoid[string, int]()
|
||||
chain := Chain[int, string, int](monoid)(func(v int) map[string]int {
|
||||
return map[string]int{
|
||||
fmt.Sprintf("x%d", v): v * 10,
|
||||
}
|
||||
})
|
||||
result := chain(data)
|
||||
assert.Equal(t, map[string]int{"x1": 10, "x2": 20}, result)
|
||||
}
|
||||
|
||||
func TestFlatten(t *testing.T) {
|
||||
nested := map[string]map[string]int{
|
||||
"a": {"x": 1, "y": 2},
|
||||
"b": {"z": 3},
|
||||
}
|
||||
monoid := MergeMonoid[string, int]()
|
||||
flatten := Flatten(monoid)
|
||||
result := flatten(nested)
|
||||
assert.Equal(t, map[string]int{"x": 1, "y": 2, "z": 3}, result)
|
||||
}
|
||||
|
||||
func TestFoldMap(t *testing.T) {
|
||||
data := map[string]int{"a": 1, "b": 2, "c": 3}
|
||||
// Use string monoid for simplicity
|
||||
fold := FoldMap[string, int, string](S.Monoid)(func(v int) string {
|
||||
return fmt.Sprintf("%d", v)
|
||||
})
|
||||
result := fold(data)
|
||||
// Result contains all digits but order is non-deterministic
|
||||
assert.Contains(t, result, "1")
|
||||
assert.Contains(t, result, "2")
|
||||
assert.Contains(t, result, "3")
|
||||
}
|
||||
|
||||
func TestFold(t *testing.T) {
|
||||
data := map[string]string{"a": "A", "b": "B", "c": "C"}
|
||||
fold := Fold[string](S.Monoid)
|
||||
result := fold(data)
|
||||
// Result contains all letters but order is non-deterministic
|
||||
assert.Contains(t, result, "A")
|
||||
assert.Contains(t, result, "B")
|
||||
assert.Contains(t, result, "C")
|
||||
}
|
||||
|
||||
func TestKeysOrd(t *testing.T) {
|
||||
data := map[string]int{"c": 3, "a": 1, "b": 2}
|
||||
keys := KeysOrd[int](S.Ord)(data)
|
||||
assert.Equal(t, []string{"a", "b", "c"}, keys)
|
||||
}
|
||||
|
||||
func TestMonadFlap(t *testing.T) {
|
||||
fns := map[string]func(int) int{
|
||||
"double": func(x int) int { return x * 2 },
|
||||
"triple": func(x int) int { return x * 3 },
|
||||
}
|
||||
result := MonadFlap(fns, 5)
|
||||
assert.Equal(t, map[string]int{"double": 10, "triple": 15}, result)
|
||||
}
|
||||
|
||||
func TestFlap(t *testing.T) {
|
||||
fns := map[string]func(int) int{
|
||||
"double": func(x int) int { return x * 2 },
|
||||
"triple": func(x int) int { return x * 3 },
|
||||
}
|
||||
flap := Flap[int, string, int](5)
|
||||
result := flap(fns)
|
||||
assert.Equal(t, map[string]int{"double": 10, "triple": 15}, result)
|
||||
}
|
||||
|
||||
func TestFromArray(t *testing.T) {
|
||||
entries := Entries[string, int]{
|
||||
P.MakePair("a", 1),
|
||||
P.MakePair("b", 2),
|
||||
P.MakePair("a", 3), // Duplicate key
|
||||
}
|
||||
// Use Second magma to keep last value
|
||||
from := FromArray[string, int](Mg.Second[int]())
|
||||
result := from(entries)
|
||||
assert.Equal(t, map[string]int{"a": 3, "b": 2}, result)
|
||||
}
|
||||
|
||||
func TestMonadAp(t *testing.T) {
|
||||
fns := map[string]func(int) int{
|
||||
"double": func(x int) int { return x * 2 },
|
||||
}
|
||||
vals := map[string]int{
|
||||
"double": 5,
|
||||
}
|
||||
monoid := MergeMonoid[string, int]()
|
||||
result := MonadAp(monoid, fns, vals)
|
||||
assert.Equal(t, map[string]int{"double": 10}, result)
|
||||
}
|
||||
|
||||
func TestAp(t *testing.T) {
|
||||
fns := map[string]func(int) int{
|
||||
"double": func(x int) int { return x * 2 },
|
||||
}
|
||||
vals := map[string]int{
|
||||
"double": 5,
|
||||
}
|
||||
monoid := MergeMonoid[string, int]()
|
||||
ap := Ap[int, string, int](monoid)(vals)
|
||||
result := ap(fns)
|
||||
assert.Equal(t, map[string]int{"double": 10}, result)
|
||||
}
|
||||
|
||||
func TestOf(t *testing.T) {
|
||||
result := Of("key", 42)
|
||||
assert.Equal(t, map[string]int{"key": 42}, result)
|
||||
}
|
||||
|
||||
func TestReduceRef(t *testing.T) {
|
||||
data := map[string]int{"a": 1, "b": 2, "c": 3}
|
||||
sum := ReduceRef[string, int, int](func(acc int, v *int) int {
|
||||
return acc + *v
|
||||
}, 0)
|
||||
result := sum(data)
|
||||
assert.Equal(t, 6, result)
|
||||
}
|
||||
|
||||
func TestReduceRefWithIndex(t *testing.T) {
|
||||
data := map[string]int{"a": 1, "b": 2}
|
||||
concat := ReduceRefWithIndex[string, int, string](func(k string, acc string, v *int) string {
|
||||
if acc == "" {
|
||||
return fmt.Sprintf("%s:%d", k, *v)
|
||||
}
|
||||
return fmt.Sprintf("%s,%s:%d", acc, k, *v)
|
||||
}, "")
|
||||
result := concat(data)
|
||||
assert.Contains(t, result, "a:1")
|
||||
assert.Contains(t, result, "b:2")
|
||||
}
|
||||
|
||||
func TestMonadMapRef(t *testing.T) {
|
||||
data := map[string]int{"a": 1, "b": 2}
|
||||
result := MonadMapRef(data, func(v *int) int { return *v * 2 })
|
||||
assert.Equal(t, map[string]int{"a": 2, "b": 4}, result)
|
||||
}
|
||||
|
||||
func TestMapRef(t *testing.T) {
|
||||
data := map[string]int{"a": 1, "b": 2}
|
||||
mapper := MapRef[string, int, int](func(v *int) int { return *v * 2 })
|
||||
result := mapper(data)
|
||||
assert.Equal(t, map[string]int{"a": 2, "b": 4}, result)
|
||||
}
|
||||
|
||||
func TestMonadMapRefWithIndex(t *testing.T) {
|
||||
data := map[string]int{"a": 1, "b": 2}
|
||||
result := MonadMapRefWithIndex(data, func(k string, v *int) string {
|
||||
return fmt.Sprintf("%s=%d", k, *v)
|
||||
})
|
||||
assert.Equal(t, map[string]string{"a": "a=1", "b": "b=2"}, result)
|
||||
}
|
||||
|
||||
func TestMapRefWithIndex(t *testing.T) {
|
||||
data := map[string]int{"a": 1, "b": 2}
|
||||
mapper := MapRefWithIndex[string, int, string](func(k string, v *int) string {
|
||||
return fmt.Sprintf("%s=%d", k, *v)
|
||||
})
|
||||
result := mapper(data)
|
||||
assert.Equal(t, map[string]string{"a": "a=1", "b": "b=2"}, result)
|
||||
}
|
||||
|
||||
func TestUnion(t *testing.T) {
|
||||
left := map[string]int{"a": 1, "b": 2}
|
||||
right := map[string]int{"b": 3, "c": 4}
|
||||
// Union combines maps, with the magma resolving conflicts
|
||||
// The order is union(left)(right), which means right is merged into left
|
||||
// First magma keeps the first value (from right in this case)
|
||||
union := Union[string, int](Mg.First[int]())
|
||||
result := union(left)(right)
|
||||
assert.Equal(t, map[string]int{"a": 1, "b": 3, "c": 4}, result)
|
||||
|
||||
// Second magma keeps the second value (from left in this case)
|
||||
union2 := Union[string, int](Mg.Second[int]())
|
||||
result2 := union2(left)(right)
|
||||
assert.Equal(t, map[string]int{"a": 1, "b": 2, "c": 4}, result2)
|
||||
}
|
||||
|
||||
func TestMonadChainWithIndex(t *testing.T) {
|
||||
data := map[string]int{"a": 1, "b": 2}
|
||||
monoid := MergeMonoid[string, string]()
|
||||
result := MonadChainWithIndex(monoid, data, func(k string, v int) map[string]string {
|
||||
return map[string]string{
|
||||
fmt.Sprintf("%s%d", k, v): fmt.Sprintf("val%d", v),
|
||||
}
|
||||
})
|
||||
assert.Equal(t, map[string]string{"a1": "val1", "b2": "val2"}, result)
|
||||
}
|
||||
|
||||
func TestChainWithIndex(t *testing.T) {
|
||||
data := map[string]int{"a": 1, "b": 2}
|
||||
monoid := MergeMonoid[string, string]()
|
||||
chain := ChainWithIndex[int, string, string](monoid)(func(k string, v int) map[string]string {
|
||||
return map[string]string{
|
||||
fmt.Sprintf("%s%d", k, v): fmt.Sprintf("val%d", v),
|
||||
}
|
||||
})
|
||||
result := chain(data)
|
||||
assert.Equal(t, map[string]string{"a1": "val1", "b2": "val2"}, result)
|
||||
}
|
||||
|
||||
func TestFilterChainWithIndex(t *testing.T) {
|
||||
src := map[string]int{
|
||||
"a": 1,
|
||||
"b": 2,
|
||||
"c": 3,
|
||||
}
|
||||
|
||||
f := func(k string, value int) O.Option[map[string]string] {
|
||||
if value%2 != 0 {
|
||||
return O.Of(map[string]string{
|
||||
k: fmt.Sprintf("%s%d", k, value),
|
||||
})
|
||||
}
|
||||
return O.None[map[string]string]()
|
||||
}
|
||||
|
||||
monoid := MergeMonoid[string, string]()
|
||||
res := FilterChainWithIndex[int](monoid)(f)(src)
|
||||
|
||||
assert.Equal(t, map[string]string{
|
||||
"a": "a1",
|
||||
"c": "c3",
|
||||
}, res)
|
||||
}
|
||||
|
||||
func TestFoldMapWithIndex(t *testing.T) {
|
||||
data := map[string]int{"a": 1, "b": 2, "c": 3}
|
||||
fold := FoldMapWithIndex[string, int, string](S.Monoid)(func(k string, v int) string {
|
||||
return fmt.Sprintf("%s:%d", k, v)
|
||||
})
|
||||
result := fold(data)
|
||||
// Result contains all pairs but order is non-deterministic
|
||||
assert.Contains(t, result, "a:1")
|
||||
assert.Contains(t, result, "b:2")
|
||||
assert.Contains(t, result, "c:3")
|
||||
}
|
||||
|
||||
func TestReduceOrd(t *testing.T) {
|
||||
data := map[string]int{"c": 3, "a": 1, "b": 2}
|
||||
sum := ReduceOrd[int, int](S.Ord)(func(acc, v int) int {
|
||||
return acc + v
|
||||
}, 0)
|
||||
result := sum(data)
|
||||
assert.Equal(t, 6, result)
|
||||
}
|
||||
|
||||
func TestReduceOrdWithIndex(t *testing.T) {
|
||||
data := map[string]int{"c": 3, "a": 1, "b": 2}
|
||||
concat := ReduceOrdWithIndex[int, string](S.Ord)(func(k string, acc string, v int) string {
|
||||
if acc == "" {
|
||||
return fmt.Sprintf("%s:%d", k, v)
|
||||
}
|
||||
return fmt.Sprintf("%s,%s:%d", acc, k, v)
|
||||
}, "")
|
||||
result := concat(data)
|
||||
// With Ord, keys should be in order
|
||||
assert.Equal(t, "a:1,b:2,c:3", result)
|
||||
}
|
||||
|
||||
func TestFoldMapOrdWithIndex(t *testing.T) {
|
||||
data := map[string]int{"c": 3, "a": 1, "b": 2}
|
||||
fold := FoldMapOrdWithIndex[string, int, string](S.Ord)(S.Monoid)(func(k string, v int) string {
|
||||
return fmt.Sprintf("%s:%d,", k, v)
|
||||
})
|
||||
result := fold(data)
|
||||
assert.Equal(t, "a:1,b:2,c:3,", result)
|
||||
}
|
||||
|
||||
func TestFoldOrd(t *testing.T) {
|
||||
data := map[string]string{"c": "C", "a": "A", "b": "B"}
|
||||
fold := FoldOrd[string](S.Ord)(S.Monoid)
|
||||
result := fold(data)
|
||||
assert.Equal(t, "ABC", result)
|
||||
}
|
||||
|
||||
func TestFromFoldableMap(t *testing.T) {
|
||||
src := A.From("a", "b", "c", "a")
|
||||
// Create a reducer function
|
||||
reducer := A.Reduce[string, map[string]string]
|
||||
from := FromFoldableMap[func(func(map[string]string, string) map[string]string, map[string]string) func([]string) map[string]string, string, []string, string, string](
|
||||
Mg.Second[string](),
|
||||
reducer,
|
||||
)
|
||||
f := from(P.Of[string])
|
||||
result := f(src)
|
||||
assert.Equal(t, map[string]string{
|
||||
"a": "a",
|
||||
"b": "b",
|
||||
"c": "c",
|
||||
}, result)
|
||||
}
|
||||
|
||||
func TestFromFoldable(t *testing.T) {
|
||||
entries := Entries[string, int]{
|
||||
P.MakePair("a", 1),
|
||||
P.MakePair("b", 2),
|
||||
P.MakePair("a", 3), // Duplicate key
|
||||
}
|
||||
reducer := A.Reduce[Entry[string, int], map[string]int]
|
||||
from := FromFoldable[[]Entry[string, int], func(func(map[string]int, Entry[string, int]) map[string]int, map[string]int) func([]Entry[string, int]) map[string]int, string, int](
|
||||
Mg.Second[int](),
|
||||
reducer,
|
||||
)
|
||||
result := from(entries)
|
||||
assert.Equal(t, map[string]int{"a": 3, "b": 2}, result)
|
||||
}
|
||||
|
||||
@@ -17,17 +17,98 @@ package record
|
||||
|
||||
import (
|
||||
G "github.com/IBM/fp-go/v2/record/generic"
|
||||
S "github.com/IBM/fp-go/v2/semigroup"
|
||||
)
|
||||
|
||||
func UnionSemigroup[K comparable, V any](s S.Semigroup[V]) S.Semigroup[map[K]V] {
|
||||
return G.UnionSemigroup[map[K]V](s)
|
||||
// UnionSemigroup creates a semigroup for maps that combines two maps using the provided
|
||||
// semigroup for resolving conflicts when the same key exists in both maps.
|
||||
//
|
||||
// When concatenating two maps:
|
||||
// - Keys that exist in only one map are included in the result
|
||||
// - Keys that exist in both maps have their values combined using the provided semigroup
|
||||
//
|
||||
// This is useful when you want custom conflict resolution logic beyond simple "first wins"
|
||||
// or "last wins" semantics.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// // Create a semigroup that sums values for duplicate keys
|
||||
// sumSemigroup := number.SemigroupSum[int]()
|
||||
// mapSemigroup := UnionSemigroup[string, int](sumSemigroup)
|
||||
//
|
||||
// map1 := map[string]int{"a": 1, "b": 2}
|
||||
// map2 := map[string]int{"b": 3, "c": 4}
|
||||
// result := mapSemigroup.Concat(map1, map2)
|
||||
// // result: {"a": 1, "b": 5, "c": 4} // b values are summed: 2 + 3 = 5
|
||||
//
|
||||
// Example with string concatenation:
|
||||
//
|
||||
// stringSemigroup := string.Semigroup
|
||||
// mapSemigroup := UnionSemigroup[string, string](stringSemigroup)
|
||||
//
|
||||
// map1 := map[string]string{"a": "Hello", "b": "World"}
|
||||
// map2 := map[string]string{"b": "!", "c": "Goodbye"}
|
||||
// result := mapSemigroup.Concat(map1, map2)
|
||||
// // result: {"a": "Hello", "b": "World!", "c": "Goodbye"}
|
||||
//
|
||||
//go:inline
|
||||
func UnionSemigroup[K comparable, V any](s Semigroup[V]) Semigroup[Record[K, V]] {
|
||||
return G.UnionSemigroup[Record[K, V]](s)
|
||||
}
|
||||
|
||||
func UnionLastSemigroup[K comparable, V any]() S.Semigroup[map[K]V] {
|
||||
return G.UnionLastSemigroup[map[K]V]()
|
||||
// UnionLastSemigroup creates a semigroup for maps where the last (right) value wins
|
||||
// when the same key exists in both maps being concatenated.
|
||||
//
|
||||
// This is the most common conflict resolution strategy and is equivalent to using
|
||||
// the standard map merge operation where right-side values take precedence.
|
||||
//
|
||||
// When concatenating two maps:
|
||||
// - Keys that exist in only one map are included in the result
|
||||
// - Keys that exist in both maps take the value from the second (right) map
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// semigroup := UnionLastSemigroup[string, int]()
|
||||
//
|
||||
// map1 := map[string]int{"a": 1, "b": 2}
|
||||
// map2 := map[string]int{"b": 3, "c": 4}
|
||||
// result := semigroup.Concat(map1, map2)
|
||||
// // result: {"a": 1, "b": 3, "c": 4} // b takes value from map2 (last wins)
|
||||
//
|
||||
// This is useful for:
|
||||
// - Configuration overrides (later configs override earlier ones)
|
||||
// - Applying updates to a base map
|
||||
// - Merging user preferences where newer values should win
|
||||
//
|
||||
//go:inline
|
||||
func UnionLastSemigroup[K comparable, V any]() Semigroup[Record[K, V]] {
|
||||
return G.UnionLastSemigroup[Record[K, V]]()
|
||||
}
|
||||
|
||||
func UnionFirstSemigroup[K comparable, V any]() S.Semigroup[map[K]V] {
|
||||
return G.UnionFirstSemigroup[map[K]V]()
|
||||
// UnionFirstSemigroup creates a semigroup for maps where the first (left) value wins
|
||||
// when the same key exists in both maps being concatenated.
|
||||
//
|
||||
// This is useful when you want to preserve original values and ignore updates for
|
||||
// keys that already exist.
|
||||
//
|
||||
// When concatenating two maps:
|
||||
// - Keys that exist in only one map are included in the result
|
||||
// - Keys that exist in both maps keep the value from the first (left) map
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// semigroup := UnionFirstSemigroup[string, int]()
|
||||
//
|
||||
// map1 := map[string]int{"a": 1, "b": 2}
|
||||
// map2 := map[string]int{"b": 3, "c": 4}
|
||||
// result := semigroup.Concat(map1, map2)
|
||||
// // result: {"a": 1, "b": 2, "c": 4} // b keeps value from map1 (first wins)
|
||||
//
|
||||
// This is useful for:
|
||||
// - Default values (defaults are set first, user values don't override)
|
||||
// - Caching (first cached value is kept, subsequent updates ignored)
|
||||
// - Immutable registries (first registration wins, duplicates are ignored)
|
||||
//
|
||||
//go:inline
|
||||
func UnionFirstSemigroup[K comparable, V any]() Semigroup[Record[K, V]] {
|
||||
return G.UnionFirstSemigroup[Record[K, V]]()
|
||||
}
|
||||
|
||||
229
v2/record/semigroup_test.go
Normal file
229
v2/record/semigroup_test.go
Normal file
@@ -0,0 +1,229 @@
|
||||
// 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"
|
||||
|
||||
N "github.com/IBM/fp-go/v2/number"
|
||||
S "github.com/IBM/fp-go/v2/string"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestUnionSemigroup(t *testing.T) {
|
||||
// Test with sum semigroup - values should be added for duplicate keys
|
||||
sumSemigroup := N.SemigroupSum[int]()
|
||||
mapSemigroup := UnionSemigroup[string, int](sumSemigroup)
|
||||
|
||||
map1 := map[string]int{"a": 1, "b": 2}
|
||||
map2 := map[string]int{"b": 3, "c": 4}
|
||||
result := mapSemigroup.Concat(map1, map2)
|
||||
|
||||
expected := map[string]int{"a": 1, "b": 5, "c": 4}
|
||||
assert.Equal(t, expected, result)
|
||||
}
|
||||
|
||||
func TestUnionSemigroupString(t *testing.T) {
|
||||
// Test with string semigroup - strings should be concatenated
|
||||
stringSemigroup := S.Semigroup
|
||||
mapSemigroup := UnionSemigroup[string, string](stringSemigroup)
|
||||
|
||||
map1 := map[string]string{"a": "Hello", "b": "World"}
|
||||
map2 := map[string]string{"b": "!", "c": "Goodbye"}
|
||||
result := mapSemigroup.Concat(map1, map2)
|
||||
|
||||
expected := map[string]string{"a": "Hello", "b": "World!", "c": "Goodbye"}
|
||||
assert.Equal(t, expected, result)
|
||||
}
|
||||
|
||||
func TestUnionSemigroupProduct(t *testing.T) {
|
||||
// Test with product semigroup - values should be multiplied
|
||||
prodSemigroup := N.SemigroupProduct[int]()
|
||||
mapSemigroup := UnionSemigroup[string, int](prodSemigroup)
|
||||
|
||||
map1 := map[string]int{"a": 2, "b": 3}
|
||||
map2 := map[string]int{"b": 4, "c": 5}
|
||||
result := mapSemigroup.Concat(map1, map2)
|
||||
|
||||
expected := map[string]int{"a": 2, "b": 12, "c": 5}
|
||||
assert.Equal(t, expected, result)
|
||||
}
|
||||
|
||||
func TestUnionSemigroupEmpty(t *testing.T) {
|
||||
// Test with empty maps
|
||||
sumSemigroup := N.SemigroupSum[int]()
|
||||
mapSemigroup := UnionSemigroup[string, int](sumSemigroup)
|
||||
|
||||
map1 := map[string]int{"a": 1}
|
||||
empty := map[string]int{}
|
||||
|
||||
result1 := mapSemigroup.Concat(map1, empty)
|
||||
assert.Equal(t, map1, result1)
|
||||
|
||||
result2 := mapSemigroup.Concat(empty, map1)
|
||||
assert.Equal(t, map1, result2)
|
||||
}
|
||||
|
||||
func TestUnionLastSemigroup(t *testing.T) {
|
||||
// Test that last (right) value wins for duplicate keys
|
||||
semigroup := UnionLastSemigroup[string, int]()
|
||||
|
||||
map1 := map[string]int{"a": 1, "b": 2}
|
||||
map2 := map[string]int{"b": 3, "c": 4}
|
||||
result := semigroup.Concat(map1, map2)
|
||||
|
||||
expected := map[string]int{"a": 1, "b": 3, "c": 4}
|
||||
assert.Equal(t, expected, result)
|
||||
}
|
||||
|
||||
func TestUnionLastSemigroupNoOverlap(t *testing.T) {
|
||||
// Test with no overlapping keys
|
||||
semigroup := UnionLastSemigroup[string, int]()
|
||||
|
||||
map1 := map[string]int{"a": 1, "b": 2}
|
||||
map2 := map[string]int{"c": 3, "d": 4}
|
||||
result := semigroup.Concat(map1, map2)
|
||||
|
||||
expected := map[string]int{"a": 1, "b": 2, "c": 3, "d": 4}
|
||||
assert.Equal(t, expected, result)
|
||||
}
|
||||
|
||||
func TestUnionLastSemigroupAllOverlap(t *testing.T) {
|
||||
// Test with all keys overlapping
|
||||
semigroup := UnionLastSemigroup[string, int]()
|
||||
|
||||
map1 := map[string]int{"a": 1, "b": 2}
|
||||
map2 := map[string]int{"a": 10, "b": 20}
|
||||
result := semigroup.Concat(map1, map2)
|
||||
|
||||
expected := map[string]int{"a": 10, "b": 20}
|
||||
assert.Equal(t, expected, result)
|
||||
}
|
||||
|
||||
func TestUnionLastSemigroupEmpty(t *testing.T) {
|
||||
// Test with empty maps
|
||||
semigroup := UnionLastSemigroup[string, int]()
|
||||
|
||||
map1 := map[string]int{"a": 1}
|
||||
empty := map[string]int{}
|
||||
|
||||
result1 := semigroup.Concat(map1, empty)
|
||||
assert.Equal(t, map1, result1)
|
||||
|
||||
result2 := semigroup.Concat(empty, map1)
|
||||
assert.Equal(t, map1, result2)
|
||||
}
|
||||
|
||||
func TestUnionFirstSemigroup(t *testing.T) {
|
||||
// Test that first (left) value wins for duplicate keys
|
||||
semigroup := UnionFirstSemigroup[string, int]()
|
||||
|
||||
map1 := map[string]int{"a": 1, "b": 2}
|
||||
map2 := map[string]int{"b": 3, "c": 4}
|
||||
result := semigroup.Concat(map1, map2)
|
||||
|
||||
expected := map[string]int{"a": 1, "b": 2, "c": 4}
|
||||
assert.Equal(t, expected, result)
|
||||
}
|
||||
|
||||
func TestUnionFirstSemigroupNoOverlap(t *testing.T) {
|
||||
// Test with no overlapping keys
|
||||
semigroup := UnionFirstSemigroup[string, int]()
|
||||
|
||||
map1 := map[string]int{"a": 1, "b": 2}
|
||||
map2 := map[string]int{"c": 3, "d": 4}
|
||||
result := semigroup.Concat(map1, map2)
|
||||
|
||||
expected := map[string]int{"a": 1, "b": 2, "c": 3, "d": 4}
|
||||
assert.Equal(t, expected, result)
|
||||
}
|
||||
|
||||
func TestUnionFirstSemigroupAllOverlap(t *testing.T) {
|
||||
// Test with all keys overlapping
|
||||
semigroup := UnionFirstSemigroup[string, int]()
|
||||
|
||||
map1 := map[string]int{"a": 1, "b": 2}
|
||||
map2 := map[string]int{"a": 10, "b": 20}
|
||||
result := semigroup.Concat(map1, map2)
|
||||
|
||||
expected := map[string]int{"a": 1, "b": 2}
|
||||
assert.Equal(t, expected, result)
|
||||
}
|
||||
|
||||
func TestUnionFirstSemigroupEmpty(t *testing.T) {
|
||||
// Test with empty maps
|
||||
semigroup := UnionFirstSemigroup[string, int]()
|
||||
|
||||
map1 := map[string]int{"a": 1}
|
||||
empty := map[string]int{}
|
||||
|
||||
result1 := semigroup.Concat(map1, empty)
|
||||
assert.Equal(t, map1, result1)
|
||||
|
||||
result2 := semigroup.Concat(empty, map1)
|
||||
assert.Equal(t, map1, result2)
|
||||
}
|
||||
|
||||
// Test associativity law for UnionSemigroup
|
||||
func TestUnionSemigroupAssociativity(t *testing.T) {
|
||||
sumSemigroup := N.SemigroupSum[int]()
|
||||
mapSemigroup := UnionSemigroup[string, int](sumSemigroup)
|
||||
|
||||
map1 := map[string]int{"a": 1}
|
||||
map2 := map[string]int{"a": 2, "b": 3}
|
||||
map3 := map[string]int{"b": 4, "c": 5}
|
||||
|
||||
// (map1 + map2) + map3
|
||||
left := mapSemigroup.Concat(mapSemigroup.Concat(map1, map2), map3)
|
||||
// map1 + (map2 + map3)
|
||||
right := mapSemigroup.Concat(map1, mapSemigroup.Concat(map2, map3))
|
||||
|
||||
assert.Equal(t, left, right)
|
||||
}
|
||||
|
||||
// Test associativity law for UnionLastSemigroup
|
||||
func TestUnionLastSemigroupAssociativity(t *testing.T) {
|
||||
semigroup := UnionLastSemigroup[string, int]()
|
||||
|
||||
map1 := map[string]int{"a": 1}
|
||||
map2 := map[string]int{"a": 2, "b": 3}
|
||||
map3 := map[string]int{"b": 4, "c": 5}
|
||||
|
||||
// (map1 + map2) + map3
|
||||
left := semigroup.Concat(semigroup.Concat(map1, map2), map3)
|
||||
// map1 + (map2 + map3)
|
||||
right := semigroup.Concat(map1, semigroup.Concat(map2, map3))
|
||||
|
||||
assert.Equal(t, left, right)
|
||||
}
|
||||
|
||||
// Test associativity law for UnionFirstSemigroup
|
||||
func TestUnionFirstSemigroupAssociativity(t *testing.T) {
|
||||
semigroup := UnionFirstSemigroup[string, int]()
|
||||
|
||||
map1 := map[string]int{"a": 1}
|
||||
map2 := map[string]int{"a": 2, "b": 3}
|
||||
map3 := map[string]int{"b": 4, "c": 5}
|
||||
|
||||
// (map1 + map2) + map3
|
||||
left := semigroup.Concat(semigroup.Concat(map1, map2), map3)
|
||||
// map1 + (map2 + map3)
|
||||
right := semigroup.Concat(map1, semigroup.Concat(map2, map3))
|
||||
|
||||
assert.Equal(t, left, right)
|
||||
}
|
||||
|
||||
// Made with Bob
|
||||
@@ -19,6 +19,36 @@ import (
|
||||
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](
|
||||
fof func(map[K]B) HKTRB,
|
||||
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)
|
||||
}
|
||||
|
||||
// HKTA = HKT<A>
|
||||
// HKTB = HKT<B>
|
||||
// HKTAB = HKT<func(A)B>
|
||||
// HKTRB = HKT<map[K]B>
|
||||
// Traverse transforms a map of values into a value of a map by applying an effectful function
|
||||
// to each value. Unlike TraverseWithIndex, this function does not provide access to the keys.
|
||||
//
|
||||
// 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](
|
||||
fof func(map[K]B) HKTRB,
|
||||
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)
|
||||
}
|
||||
|
||||
// HKTA = HKT[A]
|
||||
// HKTAA = HKT[func(A)map[K]A]
|
||||
// HKTRA = HKT[map[K]A]
|
||||
// Sequence transforms a map of effects into an effect of a map.
|
||||
// This is the dual of Traverse where the transformation function is the identity.
|
||||
//
|
||||
// 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](
|
||||
fof func(map[K]A) HKTRA,
|
||||
fmap func(func(map[K]A) func(A) map[K]A) func(HKTRA) HKTAA,
|
||||
|
||||
162
v2/record/types.go
Normal file
162
v2/record/types.go
Normal file
@@ -0,0 +1,162 @@
|
||||
// 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 (
|
||||
"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/predicate"
|
||||
"github.com/IBM/fp-go/v2/semigroup"
|
||||
)
|
||||
|
||||
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.
|
||||
// This is the primary data structure for the record package, providing
|
||||
// functional operations over Go's native map type.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// type UserRecord = Record[string, User]
|
||||
// users := UserRecord{
|
||||
// "alice": User{Name: "Alice", Age: 30},
|
||||
// "bob": User{Name: "Bob", Age: 25},
|
||||
// }
|
||||
Record[K comparable, V any] = map[K]V
|
||||
|
||||
// Predicate is a function that tests whether a key satisfies a condition.
|
||||
// Used in filtering operations to determine which entries to keep.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// isVowel := func(k string) bool {
|
||||
// return strings.ContainsAny(k, "aeiou")
|
||||
// }
|
||||
Predicate[K any] = predicate.Predicate[K]
|
||||
|
||||
// PredicateWithIndex is a function that tests whether a key-value pair satisfies a condition.
|
||||
// Used in filtering operations that need access to both key and value.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// isAdult := func(name string, user User) bool {
|
||||
// return user.Age >= 18
|
||||
// }
|
||||
PredicateWithIndex[K comparable, V any] = func(K, V) bool
|
||||
|
||||
// Operator transforms a record from one value type to another while preserving keys.
|
||||
// This is the fundamental transformation type for record operations.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// doubleValues := Map(func(x int) int { return x * 2 })
|
||||
// result := doubleValues(Record[string, int]{"a": 1, "b": 2})
|
||||
// // result: {"a": 2, "b": 4}
|
||||
Operator[K comparable, V1, V2 any] = func(Record[K, V1]) Record[K, V2]
|
||||
|
||||
// OperatorWithIndex transforms a record using both key and value information.
|
||||
// Useful when the transformation depends on the key.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// prefixWithKey := MapWithIndex(func(k string, v string) string {
|
||||
// return k + ":" + v
|
||||
// })
|
||||
OperatorWithIndex[K comparable, V1, V2 any] = func(func(K, V1) V2) Operator[K, V1, V2]
|
||||
|
||||
// Kleisli represents a monadic function that transforms a value into a record.
|
||||
// Used in chain operations for composing record-producing functions.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// expand := func(x int) Record[string, int] {
|
||||
// return Record[string, int]{
|
||||
// "double": x * 2,
|
||||
// "triple": x * 3,
|
||||
// }
|
||||
// }
|
||||
Kleisli[K comparable, V1, V2 any] = func(V1) Record[K, V2]
|
||||
|
||||
// KleisliWithIndex is a monadic function that uses both key and value to produce a record.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// expandWithKey := func(k string, v int) Record[string, int] {
|
||||
// return Record[string, int]{
|
||||
// k + "_double": v * 2,
|
||||
// k + "_triple": v * 3,
|
||||
// }
|
||||
// }
|
||||
KleisliWithIndex[K comparable, V1, V2 any] = func(K, V1) Record[K, V2]
|
||||
|
||||
// Reducer accumulates values from a record into a single result.
|
||||
// The function receives the accumulator and current value, returning the new accumulator.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// sum := Reduce(func(acc int, v int) int {
|
||||
// return acc + v
|
||||
// }, 0)
|
||||
Reducer[V, R any] = func(R, V) R
|
||||
|
||||
// ReducerWithIndex accumulates values using both key and value information.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// weightedSum := ReduceWithIndex(func(k string, acc int, v int) int {
|
||||
// weight := len(k)
|
||||
// return acc + (v * weight)
|
||||
// }, 0)
|
||||
ReducerWithIndex[K comparable, V, R any] = func(K, R, V) R
|
||||
|
||||
// Collector transforms key-value pairs into a result type and collects them into an array.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// toStrings := Collect(func(k string, v int) string {
|
||||
// return fmt.Sprintf("%s=%d", k, v)
|
||||
// })
|
||||
Collector[K comparable, V, R any] = func(K, V) R
|
||||
|
||||
// Entry represents a single key-value pair from a record.
|
||||
// This is an alias for Tuple2 to provide semantic clarity.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// entries := ToEntries(record)
|
||||
// for _, entry := range entries {
|
||||
// key := entry.F1
|
||||
// value := entry.F2
|
||||
// }
|
||||
Entry[K comparable, V any] = pair.Pair[K, V]
|
||||
|
||||
// Entries is a slice of key-value pairs.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// entries := Entries[string, int]{
|
||||
// T.MakeTuple2("a", 1),
|
||||
// T.MakeTuple2("b", 2),
|
||||
// }
|
||||
// record := FromEntries(entries)
|
||||
Entries[K comparable, V any] = []Entry[K, V]
|
||||
)
|
||||
Binary file not shown.
Reference in New Issue
Block a user