nah
parent
09274b41d4
commit
e435841c72
@ -0,0 +1 @@
|
||||
package merge2
|
@ -0,0 +1,107 @@
|
||||
package merge2
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type Merges[X any] interface {
|
||||
MergeInto(*X) error
|
||||
}
|
||||
|
||||
// func Together[X Merges[Y], Y any](vals ...X) (Y, error) {
|
||||
// var dest Y
|
||||
// for _, v := range vals {
|
||||
// if err := v.MergeInto(&dest); err != nil {
|
||||
// return dest, fmt.Errorf("error merging values: %w", err)
|
||||
// }
|
||||
// }
|
||||
// return dest, nil
|
||||
// }
|
||||
|
||||
// Into merges any number of values into some commonly-targetable value.
|
||||
func Into[X Merges[Y], Y any](dest *Y, sources ...X) error {
|
||||
for _, src := range sources {
|
||||
if err := src.MergeInto(dest); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func Maps[K comparable, V Merges[T], T any](dest map[K]T, src map[K]V) error {
|
||||
for k, v := range src {
|
||||
dv := dest[k]
|
||||
if err := v.MergeInto(&dv); err != nil {
|
||||
return fmt.Errorf("map merge error at key %v: %w", k, err)
|
||||
}
|
||||
dest[k] = dv
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// hmmmmm is it possible to make a map type where the keys are anythign
|
||||
// comparable and the values are any mixed set of values that merge into a
|
||||
// single type?
|
||||
//
|
||||
// I sorta want this:
|
||||
// Map[K comparable, V Merges[T], T any]
|
||||
|
||||
type Map[K comparable, V Merges[V]] map[K]V
|
||||
|
||||
func (m Map[K, V]) MergeInto(dest map[K]V) error {
|
||||
for k, v := range m {
|
||||
dv := dest[k]
|
||||
if err := v.MergeInto(&dv); err != nil {
|
||||
return fmt.Errorf("merge failed on key %v: %w", k, err)
|
||||
}
|
||||
dest[k] = dv
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Boxed struct {
|
||||
p interface{}
|
||||
merge func(interface{}) error
|
||||
}
|
||||
|
||||
func (b Boxed) String() string {
|
||||
return fmt.Sprint(b.p)
|
||||
}
|
||||
|
||||
func (b Boxed) IsEmpty() bool {
|
||||
return b.p == nil
|
||||
}
|
||||
|
||||
func Box[X Merges[X]](x X) Boxed {
|
||||
return Boxed{
|
||||
p: &x,
|
||||
merge: func(dest interface{}) error {
|
||||
return fmt.Errorf("not yet")
|
||||
// dp, ok := dest.p.(*X)
|
||||
// if !ok {
|
||||
// return fmt.Errorf("boxed val tried to merge into %T but can only merge into %T", dest, dp)
|
||||
// }
|
||||
// if dp == nil {
|
||||
// return fmt.Errorf("that shit is empty fuck you")
|
||||
// }
|
||||
// fmt.Printf("merging %v into %v\n", x, dp)
|
||||
// return nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (b Boxed) MergeInto(dest interface{}) error {
|
||||
if b.IsEmpty() {
|
||||
return nil
|
||||
}
|
||||
return b.merge(dest)
|
||||
}
|
||||
|
||||
func Unbox[X Merges[Y], Y any](b Boxed) (X, error) {
|
||||
p, ok := b.p.(*X)
|
||||
if !ok {
|
||||
var zero X
|
||||
return zero, fmt.Errorf("type error")
|
||||
}
|
||||
return *p, nil
|
||||
}
|
@ -0,0 +1,163 @@
|
||||
package merge2
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"constraints"
|
||||
)
|
||||
|
||||
type additive int
|
||||
|
||||
func (a additive) MergeInto(dest *additive) error {
|
||||
*dest += a
|
||||
return nil
|
||||
}
|
||||
|
||||
type multiplicative int
|
||||
|
||||
func (m multiplicative) MergeInto(dest *multiplicative) error {
|
||||
*dest *= m
|
||||
return nil
|
||||
}
|
||||
|
||||
func newGCounter[ID comparable]() gcounter[ID] {
|
||||
return gcounter[ID]{
|
||||
slots: make(map[ID]int),
|
||||
}
|
||||
}
|
||||
|
||||
type gcounter[ID comparable] struct {
|
||||
slots map[ID]int
|
||||
}
|
||||
|
||||
func (g gcounter[ID]) incr(id ID) {
|
||||
g.slots[id]++
|
||||
}
|
||||
|
||||
func (g gcounter[ID]) add(id ID, n int) {
|
||||
if n < 0 {
|
||||
panic("no")
|
||||
}
|
||||
g.slots[id] += n
|
||||
}
|
||||
|
||||
func max[N constraints.Ordered](a, b N) N {
|
||||
if a >= b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func (g gcounter[ID]) MergeInto(dest *gcounter[ID]) error {
|
||||
if dest.slots == nil {
|
||||
dest.slots = make(map[ID]int)
|
||||
}
|
||||
for id, count := range g.slots {
|
||||
dest.slots[id] = max(count, dest.slots[id])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// func TestTogether(t *testing.T) {
|
||||
// a, b, c := additive(3), additive(10), additive(17)
|
||||
// total, err := Together[Merges[*additive], additive](a, b, c)
|
||||
// if err != nil {
|
||||
// t.Fatalf("total error: %v", err)
|
||||
// }
|
||||
// if total != 30 {
|
||||
// t.Fatalf("%d != 30", total)
|
||||
// }
|
||||
// }
|
||||
|
||||
func TestInto(t *testing.T) {
|
||||
a, b, c := additive(3), additive(10), additive(17)
|
||||
if err := Into(&a, b, c); err != nil {
|
||||
t.Fatalf("error: %v", err)
|
||||
}
|
||||
if a != 30 {
|
||||
t.Fatalf("%d != 30", a)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMaps(t *testing.T) {
|
||||
alice := map[string]additive{
|
||||
"vanilla": 3,
|
||||
"chocolate": 5,
|
||||
"strawberry": 2,
|
||||
}
|
||||
|
||||
bob := map[string]additive{
|
||||
"vanilla": 2,
|
||||
"chocolate": 3,
|
||||
"pistacchio": 5,
|
||||
}
|
||||
|
||||
totals := make(map[string]additive)
|
||||
if err := Maps(totals, alice); err != nil {
|
||||
t.Fatalf("map error: %v", err)
|
||||
}
|
||||
Maps(totals, bob)
|
||||
t.Log(totals)
|
||||
}
|
||||
|
||||
func TestMap(t *testing.T) {
|
||||
alice := Map[string, additive]{
|
||||
"vanilla": 3,
|
||||
"chocolate": 5,
|
||||
"strawberry": 2,
|
||||
}
|
||||
t.Log(alice)
|
||||
}
|
||||
|
||||
func TestBox(t *testing.T) {
|
||||
t.Run("empty", func (t *testing.T) {
|
||||
var a Boxed
|
||||
var b Boxed
|
||||
if err := a.MergeInto(&b); err != nil {
|
||||
t.Fatalf("empty boxes failed to merge: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
// an empty box can merge into anything and it should be a no-op
|
||||
t.Run("empty source", func (t *testing.T) {
|
||||
var a Boxed
|
||||
b := additive(3)
|
||||
if err := a.MergeInto(&b); err != nil {
|
||||
t.Fatalf("empty boxes failed to merge: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("homologous", func(t *testing.T) {
|
||||
a, b := Box(additive(3)), Box(additive(8))
|
||||
if err := a.MergeInto(b); err != nil {
|
||||
t.Fatalf("homologous boxes failed to merge: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
// a, b := Box(additive(3)), Box(additive(5))
|
||||
// if err := b.MergeInto(&a); err != nil {
|
||||
// t.Errorf("merge boxed failed: %v", err)
|
||||
// }
|
||||
|
||||
// total, err := Unbox[additive](a)
|
||||
// if err != nil {
|
||||
// t.Errorf("unbox failed: %v", err)
|
||||
// }
|
||||
// if total != 8 {
|
||||
// t.Errorf("%d != 8", total)
|
||||
// }
|
||||
|
||||
// alice := Map[string, Boxed]{
|
||||
// "vanilla": Box(additive(3)),
|
||||
// "chocolate": Box(multiplicative(5)),
|
||||
// "strawberry": Box(multiplicative(2)),
|
||||
// }
|
||||
|
||||
// bob := Map[string, Boxed]{
|
||||
// "vanilla": Box(additive(3)),
|
||||
// "chocolate": Box(additive(5)),
|
||||
// "pistacchio": Box(additive(2)),
|
||||
// }
|
||||
|
||||
// Maps(alice, bob)
|
||||
// t.Log(alice)
|
||||
}
|
Loading…
Reference in New Issue