<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE TIP SYSTEM "http://tcl.activestate.com/cgi-bin/tct/tip/tipxml.dtd">
<!-- Converted at Thu Feb 09 15:45:55 GMT 2012 -->
<!-- TIP AutoGenerator - written by Donal K. Fellows -->

<TIP number='278'>
<header><title>Fix Variable Name Resolution Quirks</title><author address="mailto:msofer@users.sourceforge.net">Miguel Sofer</author><author address="mailto:msofer@users.sf.net">Miguel Sofer</author><author address="mailto:kennykb@acm.org">Kevin Kenny</author><status type='project' state='draft' tclversion="8.7" vote='prior'>$Revision: 1.15 $</status><history></history><created day='3' month='oct' year='2006' /></header>
<abstract>This TIP proposes to fix the behaviour for variable name resolution, modelling it on the resolution for namespace names instead of the current command name resolution.</abstract>
<body><section title="Definitions">
<itemize><item.i><para>a variable name is &quot;simple&quot; if it does not contain the character sequence &quot;::&quot;.</para></item.i><item.i><para>a variable name is &quot;absolute&quot; if it starts with the character sequence &quot;::&quot;.</para></item.i><item.i><para>a variable name is &quot;relative&quot; if it is neither simple nor absolute, it contains the character sequence &quot;::&quot;, but not at its beginning.</para></item.i></itemize>
</section>
<section title="Specification">
<para>Variable name resolution shall proceed as follows:</para>
<itemize><item.i><para>a simple name refers to a local variable if within a proc body, to a variable resolved [*] in the current namespace otherwise </para></item.i><item.i><para>an absolute name does not need resolving, ::foo::bar::baz always refers to a variable named &quot;baz&quot; in a namespace named &quot;bar&quot; that is a child of a namespace named &quot;foo&quot; that is a child of the global namespace of the interpreter [*].</para></item.i><item.i><para>a relative name is always resolved [*] starting at the current namespace. In the absence of special resolvers, foo::bar::baz refers to a variable named &quot;baz&quot; in a namespace named &quot;bar&quot; that is a child of a namespace named &quot;foo&quot; that is a child of the current namespace of the interpreter.</para></item.i></itemize>
<para>The changes with respect to the current behaviour is for relative names in all contexts, and simple names outside of proc bodies: the alternative lookup starting from the global namespace is lost.</para>
<para>The resolution is independent of the previous existence of namespaces or variables. The &apos;declaration&apos; of namespace variables with the <emph style="bold">variable</emph> command, currently needed to avoid some confusing behaviour, becomes unnecessary. In short:</para>
<quote><emph style="bold">It is possible to know what variable is meant by just looking at its name and knowing the context, without any interference from the rest of the program.</emph></quote>
<para>These are the same rules as presently used for the resolution of namespace names.</para>
<para>[*] Currently there are hooks in the core for special resolvers that can be attached to a namespace or interpreter, mainly (only?) used by itcl. The present TIP does not address resolvers, except for the specification that an absolute name ignores them. The rule that a simple name in a proc-body always refers to a local variable is not new in that sense, as any resolver hooks in that case create local variables linked to some other vars, in the manner of upvar. </para>
</section>
<section title="Rationale: avoid confusion">
<para>Repeating myself: the rationale is to make it a reality that</para>
<quote><emph style="bold">It is possible to know what variable is meant by just looking at its name and knowing the context, without any interference from the rest of the program.</emph></quote>
<para>Ever since the birth of namespaces, the resolution path for variables has been modelled on the resolution path for commands: if a variable is not found in the current namespace, it will be looked up in the global namespace.</para>
<para>This behaviour hides a few surprises, especially but not only with respect to creative writing. Consider for instance the test</para>
<verbatim><vline encoding='base64'>IHRlc3QgbmFtZXNwYWNlLTE3Ljcge1RjbF9GaW5kTmFtZXNwYWNlVmFyLCByZWxhdGl2ZSBuYW1lIGZvdW5kfSB7</vline><vline encoding='base64'>ICAgICBuYW1lc3BhY2UgZXZhbCB0ZXN0X25zXzEgew==</vline><vline encoding='base64'>ICAgICAgICAgdW5zZXQgeA==</vline><vline encoding='base64'>ICAgICAgICAgc2V0IHggIDsjIG11c3QgYmUgZ2xvYmFsIHggbm93</vline><vline encoding='base64'>ICAgICB9</vline><vline encoding='base64'>IH0gezMxNDE1OX0=</vline></verbatim>
<para>as well as following examples:</para>
<verbatim><vline encoding='base64'>ICAgJSBzZXQgeCAx</vline><vline encoding='base64'>ICAgMQ==</vline><vline encoding='base64'>ICAgJSBuYW1lc3BhY2UgZXZhbCBhIHNldCB4</vline><vline encoding='base64'>ICAgMQ==</vline><vline encoding='base64'>ICAgJSBzZXQgYTo6eA==</vline><vline encoding='base64'>ICAgY2FuJ3QgcmVhZCAiYTo6eCI6IG5vIHN1Y2ggdmFyaWFibGU=</vline><vline encoding='base64'></vline><vline encoding='base64'>ICAgJSBuYW1lc3BhY2UgZXZhbCBiIHt1cHZhciAjMCB4IHl9</vline><vline encoding='base64'>ICAgJSBpbmZvIHZhcnMgeA==</vline><vline encoding='base64'>ICAgJSBuYW1lc3BhY2UgZXZhbCBhIHNldCB4IDE=</vline><vline encoding='base64'>ICAgMQ==</vline><vline encoding='base64'>ICAgJSBzZXQgeA==</vline><vline encoding='base64'>ICAgMQ==</vline><vline encoding='base64'></vline><vline encoding='base64'>ICAgJSBuYW1lc3BhY2UgZXZhbCBhIHNldCB4</vline><vline encoding='base64'>ICAgY2FuJ3QgcmVhZCAieCI6IG5vIHN1Y2ggdmFyaWFibGU=</vline><vline encoding='base64'>ICAgJSBzZXQgeCAx</vline><vline encoding='base64'>ICAgMQ==</vline><vline encoding='base64'>ICAgJSBuYW1lc3BhY2UgZXZhbCBhIHNldCB4</vline><vline encoding='base64'>ICAgMQ==</vline><vline encoding='base64'>ICAgJSB1cHZhciAwIDo6YTo6eCB5</vline><vline encoding='base64'>ICAgJSBuYW1lc3BhY2UgZXZhbCBhIHNldCB4</vline><vline encoding='base64'>ICAgY2FuJ3QgcmVhZCAieCI6IG5vIHN1Y2ggdmFyaWFibGU=</vline><vline encoding='base64'></vline><vline encoding='base64'>ICAgJSBuYW1lc3BhY2UgZXZhbCBhIHNldCB4</vline><vline encoding='base64'>ICAgY2FuJ3QgcmVhZCAieCI6IG5vIHN1Y2ggdmFyaWFibGU=</vline><vline encoding='base64'>ICAgJSBzZXQgeCAx</vline><vline encoding='base64'>ICAgMQ==</vline><vline encoding='base64'>ICAgJSBuYW1lc3BhY2UgZXZhbCBhIHNldCB4</vline><vline encoding='base64'>ICAgMQ==</vline><vline encoding='base64'>ICAgJSB0cmFjZSBhZGQgdmFyaWFibGUgYTo6eCByZWFkIHs7I30=</vline><vline encoding='base64'>ICAgJSBuYW1lc3BhY2UgZXZhbCBhIHNldCB4</vline><vline encoding='base64'>ICAgY2FuJ3QgcmVhZCAieCI6IG5vIHN1Y2ggdmFyaWFibGU=</vline><vline encoding='base64'></vline><vline encoding='base64'>ICAgJSBzZXQgeCAx</vline><vline encoding='base64'>ICAgMQ==</vline><vline encoding='base64'>ICAgJSBuYW1lc3BhY2UgZXZhbCBhIHtzZXQgeCAyOyBzZXQgeSAzfQ==</vline><vline encoding='base64'>ICAgMw==</vline><vline encoding='base64'>ICAgJSBzZXQgeA==</vline><vline encoding='base64'>ICAgMg==</vline><vline encoding='base64'>ICAgJSBpbmZvIHZhcnMgYTo6Kg==</vline><vline encoding='base64'>ICAgOjphOjp5</vline><vline encoding='base64'>ICAgJSBzZXQgYTo6eA==</vline><vline encoding='base64'>ICAgY2FuJ3QgcmVhZCAiYTo6eCI6IG5vIHN1Y2ggdmFyaWFibGU=</vline><vline encoding='base64'>ICAgJSBzZXQgYTo6eQ==</vline><vline encoding='base64'>ICAgMw==</vline></verbatim>
<para>In order to restore some sanity, <emph style="bold">variable</emph> has been invented to selectively force the behaviour that this TIP is proposing (in its usage outside of procedure bodies).</para>
<para>The present behaviour forces a subtle and confusing concept of &quot;variable existence&quot;, forcing some implementation details to be visible to scripts. Internally, a variable may</para>
<itemize><item.i><para>not exist at all</para></item.i><item.i><para>exist in the namespace&apos;s hash table, but be undefined</para></item.i><item.i><para>exist and have a value</para></item.i></itemize>
<para>In principle scripts should not be able to distinguish the first two states - except as to the existence of traces on undefined variables. In particular, the existence of a link to an undefined variable (which forces the target to exist in state 2) should have no influence whatsoever on the concept of variable existence. But it does (see examples in #959052).</para>
<para>This behaviour also causes [namespace which -variable] and [info vars] to give different answers as to the existence of variables: the first looks in the hashtable, the second verifies that the variable has a value or that it has been declared via [variable].</para>
<para>Some of the problems inherent in the current way of things are illustrated by Bugs 959052, 1251123, 1274916, 1274918, 1280497</para>
<para>Bug 1185933 is perhaps particularly illustrative. In it, Kevin Kenny &lt;<url ref="mailto:kennykb@acm.org"/>&gt;, a long-time Tcl maintainer (and reputed expert) had placed in &apos;clock.tcl&apos; the group of lines:</para>
<verbatim><vline encoding='base64'>ICAgIHNldCBpIDA=</vline><vline encoding='base64'>ICAgIGZvcmVhY2ggaiAkRGF5c0luUm9tYW5Nb250aEluTGVhcFllYXIgew==</vline><vline encoding='base64'>ICAgICAgICBsYXBwZW5kIERheXNJblByaW9yTW9udGhzSW5MZWFwWWVhciBbaW5jciBpICRqXQ==</vline><vline encoding='base64'>ICAgIH0=</vline><vline encoding='base64'>ICAgIHVuc2V0IGkgag==</vline></verbatim>
<para>within a [namespace eval] context. This code performed without ill effect for some months, until it was observed that it would cause a failure if a script were to create a variable named &apos;i&apos; or &apos;j&apos; in the global context prior to the first invocation of the [clock] command. This case was also inordinately difficult to simulate in the test suite, because tcltest reads the clock as part of its initialization. The fix was to move the offending code from the [namespace eval] into an &apos;init&apos; procedure (which was called once and then deleted via [rename]). </para>
</section>
<section title="Side Benefit: Code Simplification, Performance">
<para>Variable name resolution has a relatively complicated implementation, and interplays strangely with many core commands - in particular <emph style="bold">variable</emph> and <emph style="bold">upvar</emph>. This TIP would enable a non-negligible simplification of a lot of code.</para>
<para>An optimisation in variable name caching that permits massive speed improvements in namespace variable accesses could also be enabled - it is currently #ifdef&apos;ed out, it was active briefly in Tcl8.5a2. Note that currently it is wrong to cache the pointer to an undefined variable: as the variable has to be kept in the corresponding hashtable, the variable jumps from the first to the second state of inexistence. This may cause breakage in scripts depending on full non-existence. See also Bug 959052.</para>
<para>Quite a few flag values that are currently needed to specify special code behaviour under different circumstances (VAR_NAMESPACE_VAR, LOOKUP_FOR_UPVAR, possibly others) become obsolete: the behaviour is the same under all circumstances.</para>
</section>
<section title="Down-Sides">
<para>This is known to expose some &quot;bugs&quot; in code in the wild, and break at least one program (AlphaTk, see below).</para>
<subsection title="AlphaTk breakage">
<para>AlphaTk breaks with this change [<url ref="http://aspn.activestate.com/ASPN/Mail/Message/Tcl-core/2083396"/>] [<url ref="http://sf.net/tracker/?func=detail&amp;aid=959786&amp;group_id=10894&amp;atid=110894"/>].</para>
<para>This is the result of code of the form</para>
<verbatim><vline encoding='base64'>ICAgbmFtZXNwYWNlIGV2YWwgZm9vIHt9</vline><vline encoding='base64'>ICAgcHJvYyBmb286OmJhciB7fSB7</vline><vline encoding='base64'>ICAgICAgIGdsb2JhbCBmb286Om5hbWU=</vline><vline encoding='base64'>ICAgICAgIHNldCBmb286Om5hbWUgMQ==</vline><vline encoding='base64'>ICAgfQ==</vline></verbatim>
<para>which works since Tcl7.x until now, and would cease to work properly if this change is implemented. It is interesting to understand how this code works:</para>
<itemize><item.i><para><emph style="bold">Tcl7.x</emph> I assume that there is conditional compat code that makes <emph style="bold">namespace</emph> a noop. The code creates a global variable foo::name, the proc accesses it as required by <emph style="bold">global</emph>.</para></item.i><item.i><para><emph style="bold">Tcl8.x</emph> the code links the local variable &quot;name&quot; to the global &quot;::foo::name&quot;; after this, &quot;name&quot; goes unused. The access to the variable is by the name &quot;foo::name&quot;: first &quot;::foo::foo::name&quot; is attempted, and, as it does not exist, &quot;::foo::name&quot;. As this variable exists, in the sense that it is in the global hashtable by virtue of the created link, it is used.</para></item.i></itemize>
<para>Note that the code works in Tcl8.x through a quirk, and that it foregoes the usage of fast local variable access to &quot;name&quot;. Should this TIP be accepted, I commit to helping out with the adaptation of AlphaTk.</para>
<para>Note also that, should both this TIP and <tipref type="text" tip="277"/> be accepted, the code will continue to work as is through a different quirk. In that case, the namespace &quot;::foo::foo&quot; would be created, and the variable &quot;::foo::foo::name&quot; would be getting all the action. The code is however fragile, this aspect is not to be understood as minimising the impact of this TIP.</para>
</subsection>
</section>
<section title="Reference Implementation and Documentation">
<para>A cvs branch tip-278-branch has been opened to test the implementation of this tip; the modifications are logged in the Changelog of the branch.</para>
</section>
<section title="Notes">
<itemize><item.i><para><emph style="bold">namespace which -variable var</emph> becomes relatively useless, as it will always return either {} or [namespace current]::var whenever var is not fully qualified.</para></item.i><item.i><para>the only effect of <emph style="bold">variable</emph> outside of proc bodies is now on <emph style="bold">namespace which -variable var</emph>. This might change again if TIP276 is approved</para></item.i></itemize>
</section>
<section title="Copyright">
<para>This document has been placed in the public domain.</para>
</section>
</body></TIP>

