switchin puters this stuff doesn't work

master
Jordan Orelli 3 years ago
parent acdbd20a53
commit bd41cba861

@ -0,0 +1,2 @@
// crdt provides conflict-free replicated data types
package crdt

@ -16,10 +16,19 @@ func NewGCounter[K comparable]() GCounter[K] {
return GCounter[K]{slots: make(map[K]int)}
}
// GCounter is a grow-only counter.
//
// In the general case, some N hosts will read and write to the gcounter and
// periodically merge their state, providing eventual consistency and allowing
// all nodes to write. Each node must have a unique ID, and should write into
// the slot that associates to that ID. The slot ID is not embedded into the
// gcounter itself, and the assignment of IDs to nodes is not provided.
type GCounter[K comparable] struct {
slots map[K]int `json:"slots"`
}
// Incr increments the value in the gcounter at the provided slot. Callers must
// provide the slot to be incremeneted.
func (g GCounter[K]) Incr(slot K) error {
var zero K
if slot == zero {

@ -0,0 +1,6 @@
package crdt
// PNCounter is a positive-negative counter
type PNCounter[K comparable] struct {
slots map[K][2]int
}

@ -2,9 +2,16 @@ package merge2
import (
"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 {
// 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
}
@ -17,9 +24,11 @@ type Merges[X any] interface {
// }
// 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 {
// 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 {
if err := src.MergeInto(dest); err != nil {
return err
@ -28,6 +37,9 @@ func Into[X Merges[Y], Y any](dest *Y, sources ...X) error {
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 {
for k, v := range src {
dv := dest[k]
@ -59,49 +71,140 @@ func (m Map[K, V]) MergeInto(dest map[K]V) error {
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 {
p interface{}
merge func(interface{}) error
// the value inside of the box. Since the only way to put a value in a box
// 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 {
return fmt.Sprint(b.p)
return fmt.Sprint(b.val)
}
// IsEmpty is true for boxes that contain nothing
func (b Boxed) IsEmpty() bool {
return b.p == nil
return b.val == 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")
// 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
// }
// fmt.Printf("merging %v into %v\n", x, dp)
// return nil
},
}
// 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{
val: x,
mkdest: nunu[Y](),
homologous: sametype[X, Y](),
mergeInto: erasef1e(x.MergeInto),
}
}
// 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 {
if b.IsEmpty() {
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) {
p, ok := b.p.(*X)
if !ok {
var zero X
return zero, fmt.Errorf("type error")
db.mergeInto = erasef1e(v.MergeInto)
db.mkdest = b.mkdest
}
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
// }

@ -109,30 +109,48 @@ func TestMap(t *testing.T) {
}
func TestBox(t *testing.T) {
t.Run("empty", func (t *testing.T) {
t.Run("both empty", func (t *testing.T) {
var a Boxed
var b Boxed
if err := a.MergeInto(&b); err != nil {
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) {
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)
}
})
b := Box[additive, additive](additive(3))
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)
t.Error(err.Error())
}
})
// t.Run("empty destination", func(t *testing.T) {
// a := Box[additive, additive](additive(3))
// var b Boxed
// if err := a.MergeInto(b); err != nil {
// t.Error(err.Error())
// }
// })
// 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)

@ -19,4 +19,4 @@ type Ref[T any] struct { ptr *T }
// Val reads the value for this reference
func (r Ref[T]) Val() T { return *r.ptr }
func (r Ref[T]) String() string { return fmt.Sprintf("ref{%s}", *r.ptr) }
func (r Ref[T]) String() string { return fmt.Sprintf("ref{%v}", *r.ptr) }

Loading…
Cancel
Save