AMG: Tcl values, a.k.a.
Tcl_Objs, are conceptually immutable. "Changing" a value produces a new value (
copy-on-write), and any references to the old value continue to point to the old value.
In practice,
unshared values can be modified in-place without being copied. This is allowed because it's fast and it does not violate the useful properties of
the immutability of values.
If you're a data structure or a piece of code and you have a reference to a value, you can count on that value never changing. If you want to change it, you have to operate on an unshared copy. Behind the scenes, that unshared copy might not be a copy at all; the important thing is that it's unshared.
I'm pretty sure that changing the type of the internal (non-string) representation of a value can only legally be done on an unshared copy. Can someone please confirm?
DGP That's false. So long as the string rep is not changed, the internal rep can move from one
Tcl_ObjType to another within a shared Tcl_Obj. Holders of a pointer to a Tcl_Obj should not assume otherwise.
AMG: Does this mean frequent
shimmering can occur if two data structures happen to share a Tcl_Obj, if the elements two data structures are accessed in different ways? Let's find out...
% set data {a 1 b 2 c 3}
a 1 b 2 c 3
% set dictstruct [dict create key1 $data key2 ...]
key1 {a 1 b 2 c 3} key2 ...
% set liststruct [dict create key1 $data key2 ...]
key1 {a 1 b 2 c 3} key2 ...
% tcl::unsupported::representation $data
value is a pure string with a refcount of 6, object pointer at 01483578, string representation "a 1 b 2 c 3".
% tcl::unsupported::representation [dict get $dictstruct key1]
value is a pure string with a refcount of 6, object pointer at 01483578, string representation "a 1 b 2 c 3".
% tcl::unsupported::representation [dict get $liststruct key1]
value is a pure string with a refcount of 6, object pointer at 01483578, string representation "a 1 b 2 c 3".
% dict keys [dict get $dictstruct key1]
a b c
% tcl::unsupported::representation $data
value is a dict with a refcount of 6, object pointer at 01483578, internal representation 014A0230:00000000, string representation "a 1 b 2 c 3".
% tcl::unsupported::representation [dict get $dictstruct key1]
value is a dict with a refcount of 6, object pointer at 01483578, internal representation 014A0230:00000000, string representation "a 1 b 2 c 3".
% tcl::unsupported::representation [dict get $liststruct key1]
value is a dict with a refcount of 6, object pointer at 01483578, internal representation 014A0230:00000000, string representation "a 1 b 2 c 3".
% llength [dict get $liststruct key1]
6
% tcl::unsupported::representation $data
value is a list with a refcount of 6, object pointer at 01483578, internal representation 01490F90:00000000, string representation "a 1 b 2 c 3".
% tcl::unsupported::representation [dict get $dictstruct key1]
value is a list with a refcount of 6, object pointer at 01483578, internal representation 01490F90:00000000, string representation "a 1 b 2 c 3".
% tcl::unsupported::representation [dict get $liststruct key1]
value is a list with a refcount of 6, object pointer at 01483578, internal representation 01490F90:00000000, string representation "a 1 b 2 c 3".
Yup, it appears you are correct. No copies of $data are created (all share the same object pointer address), and $data's internal representation changes from string to dict to list even though it's being accessed through different "containers" each time.
AK, simultaneous with
AMG's edit: DGP is quite right. See also
shimmering.