Author: Schelte Bron <[email protected]>
Vote-Results: 4/0/0 accepted
Votes-For: FV, KW, JN, SL
This tip proposes to eliminate the "feature" that a variable trace set on an array does not trigger when elements of the array are accessed through a reference created via the [upvar] command.
Currently, when a link to an array element has been created using [upvar], any variable traces set on the array as a whole will not be triggered when the array element is accessed through the link. While this is documented in the [upvar] manual page, it is not logical. Variable traces are generally installed to be notified about access to a variable. If not all normal methods to access the variable will trigger the trace, the feature cannot really be relied upon.
The reason for the current behavior seems to be that array elements have no knowledge to which array they belong. Adding this information to each element would have a significant memory impact. For this reason, the decision was apparently made in the past to document the discrepancy, rather than to fix it. This turned a bug into a feature, preventing it to be fixed in a point load.
Each array element has a pointer to the array's backing hashtable. Adding a link that points to the parent array in this table will only require one additional pointer per array. This can hopefully be considered an acceptable expense.
With part 1 of the proposed change, a subsequent problem shows up: When the accessed variable is a scalar, that is an alias for an array element, only the name of the alias would currently be provided to the callback function. In most scenarios with traces on an entire array, the callback function will need to know the actual array element that was accessed to be able to do something useful.
This problem is overcome by decoupling the 'name1' and 'name2' arguments added to the callback. As before, 'name1' will still point to the variable being accessed. But 'name2' provides the index into the array, if the accessed variable ultimately refers to an array element. The only noticeable difference happens in the mentioned scenario: 'name1' is a scalar that refers to an array element. In that case 'name2' will no longer be an empty string. Instead it provides valuable additional information.
It is not possible to make an alias for an array index. If 'name1' refers to an array, the index into that array has to be the same as the index into the original array. It doesn't matter to which array the trace proc applies the 'name2' argument.
The trace proc should not attempt to determine whether 'name1' is an array by checking if 'name2' is empty. But that was never a good strategy; the empty string is a valid array index. The proper way has always been to use [array exists]
This document has been placed in the public domain.