Skip to content

Commit

Permalink
fix: implement foldMap for records
Browse files Browse the repository at this point in the history
Signed-off-by: Dr. Carsten Leue <[email protected]>
  • Loading branch information
CarstenLeue committed Jan 12, 2024
1 parent e742854 commit 9ed9f8a
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 2 deletions.
57 changes: 57 additions & 0 deletions record/generic/record.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"sort"

F "github.com/IBM/fp-go/function"
RAG "github.com/IBM/fp-go/internal/array"
FC "github.com/IBM/fp-go/internal/functor"
G "github.com/IBM/fp-go/internal/record"
Mg "github.com/IBM/fp-go/magma"
Expand Down Expand Up @@ -334,6 +335,62 @@ func ToEntries[M ~map[K]V, GT ~[]T.Tuple2[K, V], K comparable, V any](r M) GT {
return ToArray[M, GT](r)
}

// FromFoldableMap uses the reduce method for a higher kinded type to transform
// 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],
HKTA any,
FOLDABLE ~func(func(M, A) M, M) func(HKTA) M,
M ~map[K]V,
A any,
K comparable,
V any](m Mg.Magma[V], fld FOLDABLE) func(f FCT) func(fa HKTA) M {
return func(f FCT) func(fa HKTA) M {
return fld(func(dst M, a A) M {
if IsEmpty(dst) {
dst = make(M)
}
e := f(a)
k := T.First(e)
old, ok := dst[k]
if ok {
dst[k] = m.Concat(old, T.Second(e))
} else {
dst[k] = T.Second(e)
}
return dst
}, Empty[M]())
}
}

func FromFoldable[
HKTA any,
FOLDABLE ~func(func(M, T.Tuple2[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], HKTA, FOLDABLE](m, red)(F.Identity[T.Tuple2[K, V]])
}

func FromArrayMap[
FCT ~func(A) T.Tuple2[K, V],
GA ~[]A,
M ~map[K]V,
A any,
K comparable,
V any](m Mg.Magma[V]) func(f FCT) func(fa GA) M {
return FromFoldableMap[FCT](m, F.Bind23of3(RAG.Reduce[GA, A, M]))
}

func FromArray[
GA ~[]T.Tuple2[K, V],
M ~map[K]V,
K comparable,
V any](m Mg.Magma[V]) func(fa GA) M {
return FromFoldable[GA](m, F.Bind23of3(RAG.Reduce[GA, T.Tuple2[K, V], M]))
}

func FromEntries[M ~map[K]V, GT ~[]T.Tuple2[K, V], K comparable, V any](fa GT) M {
m := make(M)
for _, t := range fa {
Expand Down
41 changes: 40 additions & 1 deletion record/record.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package record

import (
EM "github.com/IBM/fp-go/endomorphism"
Mg "github.com/IBM/fp-go/magma"
Mo "github.com/IBM/fp-go/monoid"
O "github.com/IBM/fp-go/option"
Expand Down Expand Up @@ -291,6 +292,44 @@ func Copy[K comparable, V any](m map[K]V) map[K]V {
}

// Clone creates a deep copy of the map using the provided endomorphism to clone the values
func Clone[K comparable, V any](f func(V) V) func(m map[K]V) map[K]V {
func Clone[K comparable, V any](f EM.Endomorphism[V]) EM.Endomorphism[map[K]V] {
return G.Clone[map[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
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)
}

// FromArrayMap converts from an array to a map
// Duplicate keys are resolved by the provided [Mg.Magma]
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)
}

// 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
K comparable,
V any](m Mg.Magma[V], red FOLDABLE) func(fa HKTA) map[K]V {
return G.FromFoldable[HKTA, FOLDABLE](m, red)
}

// FromArray converts from an array to a map
// 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)
}
29 changes: 28 additions & 1 deletion record/record_test.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Copyright (c) 2023 IBM Corp.
// All rights reserved.
// Copyright (c) 2023 IBM Corp.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand All @@ -23,8 +23,10 @@ import (

A "github.com/IBM/fp-go/array"
"github.com/IBM/fp-go/internal/utils"
Mg "github.com/IBM/fp-go/magma"
O "github.com/IBM/fp-go/option"
S "github.com/IBM/fp-go/string"
T "github.com/IBM/fp-go/tuple"
"github.com/stretchr/testify/assert"
)

Expand Down Expand Up @@ -149,3 +151,28 @@ func TestCopyVsClone(t *testing.T) {
assert.NotEqual(t, cpy, cln)
assert.Equal(t, src, cpy)
}

func TestFromArrayMap(t *testing.T) {
src1 := A.From("a", "b", "c", "a")
frm := FromArrayMap[string, string](Mg.Second[string]())

f := frm(T.Replicate2[string])

res1 := f(src1)

assert.Equal(t, map[string]string{
"a": "a",
"b": "b",
"c": "c",
}, res1)

src2 := A.From("A", "B", "C", "A")

res2 := f(src2)

assert.Equal(t, map[string]string{
"A": "A",
"B": "B",
"C": "C",
}, res2)
}

0 comments on commit 9ed9f8a

Please sign in to comment.