package values import ( "math/rand/v2" "sync" ) type value[T any] struct { f func() T ready bool cond *sync.Cond } // A Value is a container for a single value of type T, whose initialization is // performed the first time Value() is called. Subsequent calls will return the // same value. The Value() function may block until the value is ready on the // first call. Values are safe to use concurrently. type Value[T any] interface { Value() T } // MutableValue is the read-write counterpart to [Value], created by calling // [Deferred] for some type T. Calling Resolve() or ResolveFunc() will set // the value and unblock any waiting calls to Value(). type MutableValue[T any] interface { Value[T] Resolve(value T) ResolveFunc(fOnce func() T) } // Deferred creates a new read-write [MutableValue] for some type T, // representing a value whose initialization may be deferred to a later time. // Once the value is available, call [MutableValue.Resolve] or // [MutableValue.ResolveFunc] to unblock any waiting calls to Value(). func Deferred[T any]() MutableValue[T] { return &value[T]{ cond: sync.NewCond(&sync.Mutex{}), } } // Const creates a read-only [Value] which will become available immediately // upon calling Value() for the first time; it will never block. func Const[T any](t T) Value[T] { return &value[T]{ f: func() T { return t }, ready: true, cond: sync.NewCond(&sync.Mutex{}), } } func (p *value[T]) Value() T { p.cond.L.Lock() defer p.cond.L.Unlock() for !p.ready { p.cond.Wait() } return p.f() } func (p *value[T]) ResolveFunc(fOnce func() T) { p.cond.L.Lock() p.f = sync.OnceValue(fOnce) p.ready = true p.cond.L.Unlock() p.cond.Broadcast() } func (p *value[T]) Resolve(value T) { p.ResolveFunc(func() T { return value }) } // Bind creates a new [Value] whose ultimate value depends on the result // of another [Value] that may not yet be available. When Value() is called on // the result, it will cascade and trigger the full chain of initialization // functions necessary to produce the final value. // // Care should be taken when using this function, as improper use can lead to // deadlocks and cause values to never become available. func Bind[T any, U any](dt Value[T], callback func(value T) U) Value[U] { du := Deferred[U]() du.ResolveFunc(func() U { return callback(dt.Value()) }) return du } // Bind2 is like [Bind], but can accept two input values. The result will only // become available once all input values become available. // // This function blocks to wait for each input value in sequence, but in a // random order. Do not rely on the order of evaluation of the input values. func Bind2[T any, U any, V any](dt Value[T], du Value[U], callback func(value1 T, value2 U) V) Value[V] { dv := Deferred[V]() dv.ResolveFunc(func() V { if rand.IntN(2) == 0 { //nolint:gosec return callback(dt.Value(), du.Value()) } u := du.Value() t := dt.Value() return callback(t, u) }) return dv } // List is a container for a slice of [Value] of type T, and is also a [Value] // itself, for convenience. The Value() function will return a []T containing // all resolved values for each element in the slice. // // A List's Value() function blocks to wait for each element in the slice in // sequence, but in a random order. Do not rely on the order of evaluation of // the slice elements. type List[T any] []Value[T] func (s List[T]) Value() []T { values := make([]T, len(s)) for _, i := range rand.Perm(len(values)) { values[i] = s[i].Value() } return values } // Chain is like [Bind], returning a Value[V] whose ultimate value depends on // the result of a Value[U] which is obtained from the concrete T given in dt. // It is intended to be used when T has a method returning a Value[U], and // you want to access U, but don't need T. // // Example usage: // // type Foo interface { Number() Value[int] } // var foo Foo // values.Chain(foo, Foo.Number, func(number int) string { ... }) func Chain[T any, U any, V any]( dt Value[T], callback1 func(value T) Value[U], callback2 func(value U) V, ) Value[V] { dv := Deferred[V]() dv.ResolveFunc(func() V { du := callback1(dt.Value()) return callback2(du.Value()) }) return dv } // Chain2 is like [Chain], but with two levels of indirection. // // Example usage: // // type Foo interface { Bar() Value[Bar] } // type Bar interface { Number() Value[int] } // var foo Foo // values.Chain(foo, Foo.Bar, bar.Number, func(number int) string { ... }) func Chain2[T any, U any, V any, W any]( dt Value[T], callback1 func(value T) Value[U], callback2 func(value U) Value[V], callback3 func(value V) W, ) Value[W] { dw := Deferred[W]() dw.ResolveFunc(func() W { du := callback1(dt.Value()) dv := callback2(du.Value()) return callback3(dv.Value()) }) return dw }