Let's say I have a Table[string,int] with different values.
Is there any way I can "observe" changes to a particular key? I mean, when some key's value is changed to have a specific function executed.
Use a (int, proc(key: string))
And that proc is automatically executed when a value changes?
I can not really believe that, but sure maybe the tables module has that feature, but I never discovered it before.
Maybe he has to define his own "setValue" function for the table which calls the proc whenever a value changes by use of setValue proc.
It's your table, so just enforce that all insertions go through some function of yours. You could do type MyTable = distinct Table[string, int] or
type MyTable = object
t: Table[string, int]
and provide wrappers for the table operations. With distinct, you should be able to use {.borrow.} for most operations (but not the ones you want to watch, of course).
And that proc is automatically executed when a value changes?
It's not. I don't understand @mratsim's idea.
Maybe he has to define his own "setValue" function for the table which calls the proc whenever a value changes by use of setValue proc.
Yup ... see my earlier comment.
Combining @Stefan_Salewski 's and @jibal 's posts, here's an implementation with setValue renamed to the operator []=. ;-)
https://play.nim-lang.org/#ix=2vzb
A downside is that you can't use MyTable where a "plain" tables.Table can be used as an argument.
At first sight, you could use something like https://play.nim-lang.org/#ix=2vzg , but that's leads to an infinite recursion. So I was wondering if it's possible to delegate to the "original" []= for tables.Table somehow.
Ok, this seems to work. :-)
I believe you are seeking for reactive observer. https://play.nim-lang.org/#ix=2w0g , take a look.
from sugar import `=>`, `->`
import strformat
import tables
type Wrapper[TSource, TProp] = ref object
source: TSource
val: TProp
prop: proc(arg: TSource): TProp
callback: proc(arg: TSource)
var storage = newSeq[proc()](0)
proc onChange*[TSource, TProp](source: TSource, prop:TSource -> TProp, callback: proc(src: TSource)): int {.discardable.} =
let wrapper = new(Wrapper[TSource,TProp])
wrapper.source = source
wrapper.prop = prop
wrapper.callback = callback
storage.add(()=>(
let next = wrapper.prop(source);
if wrapper.val!=next:
wrapper.val = next
wrapper.callback(source))
)
return storage.len-1
template handleObserver*(): untyped =
for action_id in 0..<storage.len:
storage[action_id]()
discard
template dispose*(arg: int): untyped =
storage.delete(arg)
type
ComponentObject = ref object
hp: int
name: string
attack: int
type MyTable = Table[string, int]
var table = newTable[string,int]()
table.add("platypus",10)
table.add("alpaca",20)
table.onChange(x=>table["platypus"],(x: TableRef[string, int])=>(echo &"""platypus hp: {x["platypus"]}""" ))
handleObserver()
table["platypus"] = 30
echo "---------------------------"
handleObserver()
you will need to trigger your handleObserver somewhere from your main loop in the real application.