|
|
@ -2,9 +2,16 @@ package merge2
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"fmt"
|
|
|
|
|
|
|
|
"errors"
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var typeMismatch = errors.New("type mismatch")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Merges[X] is any type that can merge itself into some *X
|
|
|
|
type Merges[X any] interface {
|
|
|
|
type Merges[X any] interface {
|
|
|
|
|
|
|
|
// MergeInto should merge the receiver into the parameter *X. Callers are
|
|
|
|
|
|
|
|
// expected to mutate the parameter in some way, returning any error
|
|
|
|
|
|
|
|
// encountered when attempting to do so.
|
|
|
|
MergeInto(*X) error
|
|
|
|
MergeInto(*X) error
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -17,9 +24,11 @@ type Merges[X any] interface {
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// return dest, nil
|
|
|
|
// return dest, nil
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
|
|
// Into merges any number of values into some commonly-targetable value.
|
|
|
|
// Into merges any number of values into some commonly-targetable value.
|
|
|
|
func Into[X Merges[Y], Y any](dest *Y, sources ...X) error {
|
|
|
|
// func Into[X any](dest *X, sources ...Merges[X]) error {
|
|
|
|
|
|
|
|
func Into[X any, Y Merges[X]](dest *X, sources ...Y) error {
|
|
|
|
for _, src := range sources {
|
|
|
|
for _, src := range sources {
|
|
|
|
if err := src.MergeInto(dest); err != nil {
|
|
|
|
if err := src.MergeInto(dest); err != nil {
|
|
|
|
return err
|
|
|
|
return err
|
|
|
@ -28,6 +37,9 @@ func Into[X Merges[Y], Y any](dest *Y, sources ...X) error {
|
|
|
|
return nil
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Maps merges two maps. All keys that appear in source are merged into dest.
|
|
|
|
|
|
|
|
// Keys that appear in src but not in dest merge into the zero value of T and
|
|
|
|
|
|
|
|
// then store in dest.
|
|
|
|
func Maps[K comparable, V Merges[T], T any](dest map[K]T, src map[K]V) error {
|
|
|
|
func Maps[K comparable, V Merges[T], T any](dest map[K]T, src map[K]V) error {
|
|
|
|
for k, v := range src {
|
|
|
|
for k, v := range src {
|
|
|
|
dv := dest[k]
|
|
|
|
dv := dest[k]
|
|
|
@ -59,49 +71,140 @@ func (m Map[K, V]) MergeInto(dest map[K]V) error {
|
|
|
|
return nil
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Emerge is an empty merge: given some merge source, emerge merges the merge
|
|
|
|
|
|
|
|
// source into the zero value of the merge destination type.
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// Emerge[Y any](x Merges[Y]) (Y, error)
|
|
|
|
|
|
|
|
func Emerge[X Merges[Y], Y any](x X) (Y, error) {
|
|
|
|
|
|
|
|
var y Y
|
|
|
|
|
|
|
|
return y, x.MergeInto(&y)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Boxed is a type-erased container for merge semantics.
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// Boxed exists in order to facilitate the merging of heterogeneous collections
|
|
|
|
|
|
|
|
// of values.
|
|
|
|
type Boxed struct {
|
|
|
|
type Boxed struct {
|
|
|
|
p interface{}
|
|
|
|
// the value inside of the box. Since the only way to put a value in a box
|
|
|
|
merge func(interface{}) error
|
|
|
|
// is through the Box function or by merging from another box, we know that
|
|
|
|
|
|
|
|
// this value defines some merge function
|
|
|
|
|
|
|
|
val interface{}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// mkdest creates a value of the type to which the val field merges
|
|
|
|
|
|
|
|
mkdest func() interface{}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// homologous describes whether or not the contained value merges with its
|
|
|
|
|
|
|
|
// own type. Any boxed type that defines merge semantics against its own
|
|
|
|
|
|
|
|
// type can merge into an empty box.
|
|
|
|
|
|
|
|
homologous bool
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// mergeInto is a function that defines how to merge the val field into
|
|
|
|
|
|
|
|
// some destination
|
|
|
|
|
|
|
|
mergeInto func(dest interface{}) error
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (b Boxed) String() string {
|
|
|
|
func (b Boxed) String() string {
|
|
|
|
return fmt.Sprint(b.p)
|
|
|
|
return fmt.Sprint(b.val)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// IsEmpty is true for boxes that contain nothing
|
|
|
|
func (b Boxed) IsEmpty() bool {
|
|
|
|
func (b Boxed) IsEmpty() bool {
|
|
|
|
return b.p == nil
|
|
|
|
return b.val == nil
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// IsTerminal describes whether or not the box defines any merge semantics. A
|
|
|
|
|
|
|
|
// box containing a value that does not define merge semantics is a box that
|
|
|
|
|
|
|
|
// terminates a merge chain.
|
|
|
|
|
|
|
|
func (b Boxed) IsTerminal() bool {
|
|
|
|
|
|
|
|
return b.mergeInto == nil
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// erasef1e erases type information from a function of input arity 1 that
|
|
|
|
|
|
|
|
// returns an error
|
|
|
|
|
|
|
|
func erasef1e[X any](f func(X) error) func(interface{}) error {
|
|
|
|
|
|
|
|
return func(v interface{}) error {
|
|
|
|
|
|
|
|
tv, ok := v.(X)
|
|
|
|
|
|
|
|
if !ok {
|
|
|
|
|
|
|
|
return fmt.Errorf("unexpected %T value, expected %T instead: %w", v, tv, typeMismatch)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return f(tv)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func mergeFn[X Merges[Y], Y any](x X) func(interface{}) error {
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
|
|
|
// return func(v interface{}) error {
|
|
|
|
|
|
|
|
// x.MergeInto
|
|
|
|
|
|
|
|
// }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func Box[X Merges[X]](x X) Boxed {
|
|
|
|
// nunu[X] creates a function for the type X that creates a new zero value
|
|
|
|
|
|
|
|
// having the type X, then erases the type information by sticking it in an
|
|
|
|
|
|
|
|
// empty interface. This is a constructor-constructor that creates a
|
|
|
|
|
|
|
|
// type-erased constructor. Pretty gross!
|
|
|
|
|
|
|
|
func nunu[X any]() func() interface {} {
|
|
|
|
|
|
|
|
return func() interface{} {
|
|
|
|
|
|
|
|
var zero X
|
|
|
|
|
|
|
|
return zero
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// sametype determines whether or not some manually instantiated type
|
|
|
|
|
|
|
|
// parameters are or are not the same type.
|
|
|
|
|
|
|
|
func sametype[X, Y any]() bool {
|
|
|
|
|
|
|
|
_, ok := interface{}(*new(X)).(Y)
|
|
|
|
|
|
|
|
return ok
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Box boxes a value. Only values that define -some- merge semantic can go into
|
|
|
|
|
|
|
|
// the box.
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// Box[Y any](x Merges[Y]) Boxed
|
|
|
|
|
|
|
|
func Box[X Merges[Y], Y any](x X) Boxed {
|
|
|
|
return Boxed{
|
|
|
|
return Boxed{
|
|
|
|
p: &x,
|
|
|
|
val: x,
|
|
|
|
merge: func(dest interface{}) error {
|
|
|
|
mkdest: nunu[Y](),
|
|
|
|
return fmt.Errorf("not yet")
|
|
|
|
homologous: sametype[X, Y](),
|
|
|
|
// dp, ok := dest.p.(*X)
|
|
|
|
mergeInto: erasef1e(x.MergeInto),
|
|
|
|
// 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
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// EndBox creates a terminal box in a merge chain. This boxed value contains a
|
|
|
|
|
|
|
|
// value that does not define any merge semantics. It may only be used as a
|
|
|
|
|
|
|
|
// merge destination, not a merge source.
|
|
|
|
|
|
|
|
func EndBox[X any](x X) Boxed {
|
|
|
|
|
|
|
|
return Boxed{
|
|
|
|
|
|
|
|
val: x,
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// MergeInto merges the Boxed value into some destination value. If the boxed
|
|
|
|
|
|
|
|
// value does not merge into the value supplied, the failure occurs at runtime.
|
|
|
|
|
|
|
|
// For merge semantics that are type-checked at compile time... don't box your
|
|
|
|
|
|
|
|
// values, I dunno what to tell you.
|
|
|
|
func (b Boxed) MergeInto(dest interface{}) error {
|
|
|
|
func (b Boxed) MergeInto(dest interface{}) error {
|
|
|
|
if b.IsEmpty() {
|
|
|
|
if b.IsEmpty() {
|
|
|
|
return nil
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return b.merge(dest)
|
|
|
|
if db, ok := dest.(Boxed); ok {
|
|
|
|
}
|
|
|
|
if db.IsEmpty() {
|
|
|
|
|
|
|
|
v := b.mkdest()
|
|
|
|
|
|
|
|
if b.homologous {
|
|
|
|
|
|
|
|
db.homologous = true
|
|
|
|
|
|
|
|
|
|
|
|
func Unbox[X Merges[Y], Y any](b Boxed) (X, error) {
|
|
|
|
db.mergeInto = erasef1e(v.MergeInto)
|
|
|
|
p, ok := b.p.(*X)
|
|
|
|
db.mkdest = b.mkdest
|
|
|
|
if !ok {
|
|
|
|
}
|
|
|
|
var zero X
|
|
|
|
}
|
|
|
|
return zero, fmt.Errorf("type error")
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return *p, nil
|
|
|
|
return b.mergeInto(dest)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// func Unbox[X Merges[Y], Y any](b Boxed) (X, error) {
|
|
|
|
|
|
|
|
// p, ok := b.val.(*X)
|
|
|
|
|
|
|
|
// if !ok {
|
|
|
|
|
|
|
|
// var zero X
|
|
|
|
|
|
|
|
// return zero, fmt.Errorf("type error")
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
// return *p, nil
|
|
|
|
|
|
|
|
// }
|
|
|
|