Documentation ¶
Overview ¶
Package change helps to keep track of changed values in Go programs. It has three different concepts to do so that vary in memory overhead, time overhead and features: The simple Val, the Do with a single callback hook and the full-featured observable Obs. All implement a common interface Changeable and are implemented as generics.
Compare the following plain-Go example with the examples from Val, On and Obs:
Example ¶
user := struct { Name string Logins int }{ Name: "John Doe", Logins: 0, } // Tedious with plain Go, simpler with “change”: var chg uint64 if user.Name != "John Doe" { user.Name = "John Doe" chg |= 1 // = (1<<0) = 0b01 } if user.Logins != 1 { user.Logins = 1 // = (1<<1) = 0b10 chg |= 2 } fmt.Printf("Changes: 0b%b\n", chg) if chg&1 == 0 { fmt.Println("Name did not change") } else { fmt.Println("Name changed") }
Output: Changes: 0b10 Name did not change
Index ¶
- Constants
- type Changeable
- type Changed
- type Event
- type FlagHook
- type Flags
- type HookFunc
- type Obs
- type Observable
- type ObservableBase
- func (b ObservableBase) ObsDefaults() (tag any, chg Flags)
- func (b *ObservableBase) ObsEach(do func(tag any, o Observer))
- func (b *ObservableBase) ObsLastVeto() *Veto
- func (b *ObservableBase) ObsRegister(prio int, tag any, o Observer)
- func (b *ObservableBase) ObsUnregister(o Observer)
- func (b *ObservableBase) SetObsDefaults(tag any, chg Flags)
- type Observer
- type On
- type Val
- type ValueChange
- type Veto
Examples ¶
Constants ¶
const AllFlags = ^Flags(0)
AllFlags is a Flags set with all flags set – just to have said that.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Changeable ¶ added in v0.4.0
type Changeable[T comparable] interface { Get() T Set(v T, chg Flags) Flags }
type Changed ¶ added in v0.4.0
type Changed[T comparable] struct { // contains filtered or unexported fields }
func (Changed) Source ¶ added in v0.4.0
func (e Changed) Source() Observable
type Event ¶ added in v0.4.0
type Event interface { // Source returns the Observable that issued the Event. Source() Observable // Chg returns the change Flags for the state change. Chg() Flags }
Event is the base interface for all state change events.
type FlagHook ¶ added in v0.4.0
type FlagHook[_ comparable] Flags
FlagHook's Flag method always returns the same Flags value.
type Flags ¶
type Flags uint64
Flags is used by all Set methods to return to the caller if a Set operation did change a value. Generally Flags==0 means nothing changed. The flags from different Set calls can efficiently be combined with the '|' binary or operator. However there is room for no more than 64 different flags.
The specific flags for a call to Set are generally provided by the caller. It depends on the sub package what it means to pass 0 flags to a Set method. E.g. there may be defaults that kick in if one passes 0 flags to the actual call.
func (Flags) Map ¶
Map remaps bits of c to a new combination of bits.
If the number of bits elements is odd then the last element is added to res if c is not zero. The even number of leading elements is treated as a list of pairs. The first element of a pair is checked to have any bits common with c. If so, the second element of the pair is added to res.
Example ¶
fmt.Println(Flags(0xf0).Map(4)) // 0xf0 != 0 → 4 fmt.Println(Flags(0xf0).Map(0x0f, 2, 1)) // 0xf0 & 0x0f == 0 → 0 and 0x0f != 0 → 1 ⇒ 0|1 = 1 fmt.Println(Flags(0x18).Map(0x0f, 2, 1)) // 0x18 & 0x0f != 0 → 2 and 0x0f != 0 → 1 ⇒ 2|1 = 3 fmt.Println(Flags(0x18).Map(0x0f, 2)) // 0x18 & 0x0f != 0 → 2
Output: 4 1 3 2
type HookFunc ¶ added in v0.4.0
type HookFunc[T comparable] func(src *On[T], oldval, newval T, check bool) Flags
HookFunc functions can be hooked into On values. They get passed the On object src for which the Set method was called, the old value odlval and the to be set value newval and the check flag. See also the description of On.
type Obs ¶ added in v0.4.0
type Obs[T comparable] struct { ObservableBase // contains filtered or unexported fields }
Obs implements Changeable as a full featured Observable.
Example (String) ¶
str := NewObs("", "example string", 4711) str.ObsRegister(0, nil, UpdateFunc(func(tag any, e Event) { chg := e.(Changed[string]) fmt.Printf("Tag: %+v\n", tag) fmt.Printf("Event: '%s'→'%s': %d\n", chg.OldValue(), chg.NewValue(), e.Chg()) })) fmt.Println(str.Set("Observe this change!", 0), str.ObsLastVeto())
Output: Tag: example string Event: ''→'Observe this change!': 4711 4711 <nil>
type Observable ¶ added in v0.4.0
type Observable interface { // ObsDefaults returns the default tag and change Flags of the observable. ObsDefaults() (tag any, chg Flags) // SetObsDefaults sets the default tag and change Flags of the observable. SetObsDefaults(tag any, chg Flags) // ObsRegister registres a new Observer with this Observable. If tag is not // nil, the Observer will be notified with this specific tag, not the // default tag. ObsRegister(prio int, tag any, o Observer) // ObsUnregister removes the Observer o from the observable if it is // registered. Otherwise ObsUnregister odes nothing. ObsUnregister(o Observer) // ObsLastVeto returns the Veto from the last Set call, if any. ObsLastVeto() *Veto // ObsEach calls do for all registered Observers in noitification order // with the same tag that would be used for notifications. ObsEach(do func(tag any, o Observer)) }
Observable objects will notify registered Observers about state changes. Observables use tags to let Observers easily distinguish the different subject they observe. In addition to change Events, Observables also use change Flags to descibe chages – much like Val and On values. Observers are notified in order of their registered priority, highest first. Observers with the same priority are notified in order of their registration.
type ObservableBase ¶ added in v0.4.0
type ObservableBase struct {
// contains filtered or unexported fields
}
func (ObservableBase) ObsDefaults ¶ added in v0.4.0
func (b ObservableBase) ObsDefaults() (tag any, chg Flags)
func (*ObservableBase) ObsEach ¶ added in v0.4.0
func (b *ObservableBase) ObsEach(do func(tag any, o Observer))
func (*ObservableBase) ObsLastVeto ¶ added in v0.4.0
func (b *ObservableBase) ObsLastVeto() *Veto
func (*ObservableBase) ObsRegister ¶ added in v0.4.0
func (b *ObservableBase) ObsRegister(prio int, tag any, o Observer)
func (*ObservableBase) ObsUnregister ¶ added in v0.4.0
func (b *ObservableBase) ObsUnregister(o Observer)
func (*ObservableBase) SetObsDefaults ¶ added in v0.4.0
func (b *ObservableBase) SetObsDefaults(tag any, chg Flags)
type Observer ¶ added in v0.4.0
type Observer interface { // Check lets the observer inspect the hypothetical change Event e // before it is executed. By returning a non-nil veto the // observer can block the change. Check also can override the // default change Flags used for the Set operation by returning // chg != 0. Check(tag any, e Event) *Veto // Update notifies the observer about a change Event e. Update(tag any, e Event) }
Observers can be registered with Observables to be notified about state changes of the Observable. An Observer must be comparable to make Observable.ObsRegister() and Observable.ObsUnregister() work.
func UpdateFunc ¶ added in v0.4.0
Use UpdateFunc to wrap an update function so that it can be used as an Observer. Note that the pointer to the UpdateFunc object will be used to implement equality. This is because Go functions are not comparable.
type On ¶ added in v0.4.0
type On[T comparable] struct { // contains filtered or unexported fields }
On implements Changeable with a hook that is called on value changes. When the Set method is called, first the hook is called before a change is made with check=true. With check==true the hook decides if it blocks the set operation. To block the set operation, the hook returns 0. Otherwise the hook provides the change flags for the Set method. The flags from the hook are overridden if non-zero flags are passed to the value's Set method. If the hook is nil, On behave exactly like Val.
Example ¶
package main import "fmt" var _ Changeable[int] = (*On[int])(nil) func main() { user := struct { Name On[string] Logins On[int] }{ Name: NewOn("John Doe", FlagHook[string](1).Func), Logins: NewOn(0, func(_ *On[int], o, n int, check bool) Flags { if !check { fmt.Printf("changed logins from %d to %d\n", o, n) } return 2 }), } chg := user.Name.Set("John Doe", 0) chg |= user.Logins.Set(1, 0) fmt.Printf("Changes: 0b%b\n", chg) if chg&1 == 0 { fmt.Println("Name did not change") } else { fmt.Println("Name changed") } }
Output: changed logins from 0 to 1 Changes: 0b10 Name did not change
func NewOn ¶ added in v0.4.0
func NewOn[T comparable](init T, hook HookFunc[T]) On[T]
type Val ¶ added in v0.4.0
type Val[T comparable] struct { // contains filtered or unexported fields }
Val is a Changeable that tracks changes with a simple set of Flags. The flag(s) passed to a Set method call are returned if the underlying value changed. Otherwise the passed value is not assigned and 0 is returned. Note that if one passes flag=0 to Set the returned value will always be 0, which makes it rather uninteresting. However this will not affect the actual value change.
While these changeable values are rather bare bones they come without memory overhead – unlike most observable libraries.
Example ¶
user := struct { Name Val[string] Logins Val[int] }{ Name: NewVal("John Doe"), Logins: NewVal(0), } chg := user.Name.Set("John Doe", 1) // 1 = (1<<0) = 0b01 chg |= user.Logins.Set(1, 2) // 2 = (1<<1) = 0b10 fmt.Printf("Changes: 0b%b\n", chg) if chg&1 == 0 { fmt.Println("Name did not change") } else { fmt.Println("Name changed") }
Output: Changes: 0b10 Name did not change
func NewVal ¶ added in v0.4.0
func NewVal[T comparable](init T) Val[T]
type ValueChange ¶ added in v0.4.0
type Veto ¶ added in v0.4.0
type Veto struct {
// contains filtered or unexported fields
}
Veto keeps the information why a Set operation was stopped by which Obersver. The Veto can be requested from each Observable until the next call to its Set method.
Veto also implements an unwrappable Go error.