Skip to content

Commit ea810d6

Browse files
committed
Implement watches for vars
Signed-off-by: James Hamlin <jfhamlin@gmail.com>
1 parent 861552a commit ea810d6

File tree

1 file changed

+31
-9
lines changed

1 file changed

+31
-9
lines changed

pkg/lang/var.go

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ type (
2222
dynamic bool
2323
dynamicBound atomic.Bool
2424

25+
watches IPersistentMap
26+
2527
syncLock sync.Mutex
2628
}
2729

@@ -93,8 +95,9 @@ func InternVarName(nsSym, nameSym *Symbol) *Var {
9395

9496
func NewVar(ns *Namespace, sym *Symbol) *Var {
9597
v := &Var{
96-
ns: ns,
97-
sym: sym,
98+
ns: ns,
99+
sym: sym,
100+
watches: emptyMap,
98101
}
99102
v.root.Store(Box{val: &UnboundVar{v: v}})
100103
v.meta.Store(NewBox(emptyMap))
@@ -127,7 +130,8 @@ func (v *Var) HasRoot() bool {
127130

128131
func (v *Var) BindRoot(root interface{}) {
129132
// TODO: handle metadata correctly
130-
v.root.Store(Box{val: root})
133+
old := v.root.Swap(Box{val: root})
134+
v.notifyWatches(old.(Box).val, root)
131135
}
132136

133137
func (v *Var) IsBound() bool {
@@ -151,7 +155,9 @@ func (v *Var) Set(val interface{}) interface{} {
151155
if b == nil {
152156
panic(fmt.Sprintf("can't change/establish root binding of: %s", v))
153157
}
158+
old := b.val
154159
b.val = val
160+
v.notifyWatches(old, val)
155161
return val
156162
}
157163

@@ -230,9 +236,9 @@ func (v *Var) AlterRoot(alter IFn, args ISeq) interface{} {
230236
v.syncLock.Lock()
231237
defer v.syncLock.Unlock()
232238

233-
newRoot := alter.ApplyTo(NewCons(v.Get(), args))
234-
// TODO: validate, ++rev, notifyWatches
235-
// oldRoot := v.Get()
239+
oldRoot := v.Get()
240+
newRoot := alter.ApplyTo(NewCons(oldRoot, args))
241+
// TODO: validate, ++rev
236242
v.Set(newRoot)
237243
return newRoot
238244
}
@@ -246,15 +252,31 @@ func (v *Var) Validator() IFn {
246252
}
247253

248254
func (v *Var) Watches() IPersistentMap {
249-
panic("not implemented")
255+
return v.watches
250256
}
251257

252258
func (v *Var) AddWatch(key interface{}, fn IFn) IRef {
253-
panic("not implemented")
259+
v.watches = v.watches.Assoc(key, fn).(IPersistentMap)
260+
return v
254261
}
255262

256263
func (v *Var) RemoveWatch(key interface{}) {
257-
panic("not implemented")
264+
v.watches = v.watches.Without(key)
265+
}
266+
267+
func (v *Var) notifyWatches(oldVal, newVal interface{}) {
268+
watches := v.watches
269+
if watches == nil || watches.Count() == 0 {
270+
return
271+
}
272+
273+
for seq := watches.Seq(); seq != nil; seq = seq.Next() {
274+
entry := seq.First().(IMapEntry)
275+
key := entry.Key()
276+
fn := entry.Val().(IFn)
277+
// Call watch function with key, ref, old-state, new-state
278+
fn.Invoke(key, v, oldVal, newVal)
279+
}
258280
}
259281

260282
func (v *Var) Hash() uint32 {

0 commit comments

Comments
 (0)