Search code examples
timertcl

Why is there no difference whether a reference is used or not when starting/cancelling a timer?


I was experimenting with blinking label using after. Eventually I got what I wanted, but I discovered one thing, that confused me:

   set data(tm) [after 500 {on-tick data}] 
   set $data(tm) [after 500 {on-tick data}] 

Both variants (with and without dollar sign) work as expected. And it seemed to me that this also goes for cancel:

    after cancel data(tm)
    after cancel $data(tm)

Why is it so? Why is there no difference whether the reference is used or not?


Solution

  • The pair that would look conventional to me would be:

    set data(tm) [after 500 {on-tick data}] 
    after cancel $data(tm)
    

    The other cases are weird and probably not doing what you expect:

    Double Dereferencing

    This:

    set $data(tm) [after 500 {on-tick data}] 
    

    is writing the token into the variable named in the array variable. This is double-dereferencing and is probably a bad idea. It would be expected to pair with this awkward form:

    after cancel [set $data(tm)]
    

    There's been an outstanding feature request for many years to support $$var as a way to do double dereferencing more easily, but it's something that's incredibly low priority on anyone's radar as most cases like that are more readable with $ary($var) or something similar; it's not the same thing, of course, but it usually answers the real need (and trickery with upvar 0 handles the rest). The use of an array to manage the indirection tends to make things more mnemonic, at least to me:

    set timers($data(tm)) [after 500 {on-tick data}]
    after cancel $timers($data(tm))
    

    $ in Tcl doesn't mean "here is a variable" but rather "read from this variable now". (Single argument set does the same thing; very old versions of Tcl just had this and didn't have the $ syntax.)

    Cancel by (Failing) Search

    This:

    after cancel data(tm)
    

    is probably doing nothing. It's looking for a scheduled event whose handler is the literal string data(tm). It's probably not finding one; nothing happens on a failed lookup. That's because nothing bothers to try to keep around a definitive list of all the things you might have once been able to look up, and an expired timer event is simply forgotten as that's simpler and keeps memory management easier.

    If you'd done:

    set data(tm) [after 500 data(tm)]
    

    Then that would be cancelled... and also be a rather odd name for a command! Legal, but strange.