<?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 07:19:41 GMT 2012 -->
<!-- TIP AutoGenerator - written by Donal K. Fellows -->

<TIP number='33'>
<header><title>Add &apos;lset&apos; Command to Assign to List Elements.</title><author address="mailto:kennykb@acm.org">Kevin Kenny</author><status type='project' state='final' tclversion="8.4" vote='after'>$Revision: 1.13 $</status><history></history><created day='15' month='may' year='2001' /><discussions url='news:comp.lang.tcl'/><discussions url='mailto:kennykb@acm.org'/></header>
<abstract>Most popular programming languages provide some sort of indexed array construct, where array subscripts are integers. Tcl&apos;s lists are implemented internally as indexed arrays, but it is difficult to use them as such because there is no convenient way to assign to individual elements. This TIP proposes a new command, <emph style="italic">lset</emph>, to rectify this limitation.</abstract>
<body><section title="Rationale">
<para>The implementation of lists in Tcl has evolved far beyond the original conception. While lists were originally conceived to be strings with a particular syntax that allowed them to be parsed as lists, the internal representation of a list is now an array of pointers to <emph style="italic">Tcl_Obj</emph> structures.</para>
<para>Tcl programmers, for the most part, have not taken advantage of this evolution. Code that uses hash tables where linear arrays would be a more appropriate structure is still extremely common. Moreover, it is difficult to update lists in place, even if their internal representations are known not to be shared. One example of this difficulty is seen in the discussions [<url ref="http://purl.org/thecliff/tcl/wiki/941"/>] of how best to shuffle a list of items. The discussion began with a naïve implementation of Jon Bentley&apos;s method of performing random swaps:</para>
<verbatim><vline encoding='base64'>ICBwcm9jIHNodWZmbGUxIHsgbGlzdCB9IHs=</vline><vline encoding='base64'>ICAgICAgc2V0IG4gW2xsZW5ndGggJGxpc3Rd</vline><vline encoding='base64'>ICAgICAgZm9yIHsgc2V0IGkgMCB9IHsgJGkgPCAkbiB9IHsgaW5jciBpIH0gew==</vline><vline encoding='base64'>ICAgICAgICAgIHNldCBqIFtleHByIHtpbnQocmFuZCgpKiRuKX1d</vline><vline encoding='base64'>ICAgICAgICAgIHNldCB0ZW1wIFtsaW5kZXggJGxpc3QgJGpd</vline><vline encoding='base64'>ICAgICAgICAgIHNldCBsaXN0IFtscmVwbGFjZSAkbGlzdCAkaiAkaiBbbGluZGV4ICRsaXN0ICRpXV0=</vline><vline encoding='base64'>ICAgICAgICAgIHNldCBsaXN0IFtscmVwbGFjZSAkbGlzdCAkaSAkaSAkdGVtcF0=</vline><vline encoding='base64'>ICAgICAgfQ==</vline><vline encoding='base64'>ICAgICAgcmV0dXJuICRsaXN0</vline><vline encoding='base64'>ICB9</vline></verbatim>
<para>Aside from the fact that the syntax obscures what the program is doing, the implementation suffers from an obscure performance problem. When the <emph style="italic">lreplace</emph> calls in the <emph style="italic">shuffle1</emph> procedure are executed, the internal representation of <emph style="italic">list</emph> has two references: the value of the variable, and the parameter passed to <emph style="italic">lreplace</emph>. The multiple references force <emph style="italic">lreplace</emph> to copy the list, leading to quadratic performance when large lists are shuffled.</para>
<para>It is possible, albeit difficult, to alleviate this problem by careful management of the lifetime of <emph style="italic">Tcl_Obj</emph> structures, but this change complicates the code. The simplest way to fix the performance is probably to use Donal Fellows&apos;s implementation of the <emph style="italic">K</emph> combinator:</para>
<verbatim><vline encoding='base64'>IHByb2MgSyB7IHggeSB9IHsgc2V0IHggfQ==</vline></verbatim>
<para>which allows the caller of <emph style="italic">lreplace</emph> to extract the value of <emph style="italic">list</emph>, change the value of <emph style="italic">list</emph> so that the extracted value is unshared, and then pass the extracted value as a parameter to <emph style="italic">lreplace:</emph></para>
<verbatim><vline encoding='base64'>ICBwcm9jIHNodWZmbGUxYSB7IGxpc3QgfSB7</vline><vline encoding='base64'>ICAgICAgc2V0IG4gW2xsZW5ndGggJGxpc3Rd</vline><vline encoding='base64'>ICAgICAgZm9yIHsgc2V0IGkgMCB9IHsgJGkgPCAkbiB9IHsgaW5jciBpIH0gew==</vline><vline encoding='base64'>ICAgICAgICAgIHNldCBqIFtleHByIHtpbnQocmFuZCgpKiRuKX1d</vline><vline encoding='base64'>ICAgICAgICAgIHNldCB0ZW1wMSBbbGluZGV4ICRsaXN0ICRqXQ==</vline><vline encoding='base64'>ICAgICAgICAgIHNldCB0ZW1wMiBbbGluZGV4ICRsaXN0ICRpXQ==</vline><vline encoding='base64'>ICAgICAgICAgIHNldCBsaXN0IFtscmVwbGFjZSBbSyAkbGlzdCBbc2V0IGxpc3Qge31dXSAkaiAkaiAkdGVtcDJd</vline><vline encoding='base64'>ICAgICAgICAgIHNldCBsaXN0IFtscmVwbGFjZSBbSyAkbGlzdCBbc2V0IGxpc3Qge31dXSAkaSAkaSAkdGVtcDFd</vline><vline encoding='base64'>ICAgICAgfQ==</vline><vline encoding='base64'>ICAgICAgcmV0dXJuICRsaXN0</vline><vline encoding='base64'>ICB9</vline></verbatim>
<para>Now the performance of the code is <emph style="italic">O(n)</emph> where <emph style="italic">n</emph> is the length of the list, but the programmer&apos;s intent has been seriously obscured! Moreover, the performance is still rather poor: Tcl makes an atrocious showing, for instance, in Doug Bagley&apos;s &apos;Great Computer Language Shootout&apos; [<url ref="http://www.bagley.org/~doug/shootout/"/>].</para>
</section>
<section title="Specification">
<para>This TIP proposes an &apos;lset&apos; command with the syntax:</para>
<verbatim><vline encoding='base64'>ICBsc2V0IHZhck5hbWUgaW5kZXhMaXN0IHZhbHVl</vline></verbatim>
<para>or</para>
<verbatim><vline encoding='base64'>ICBsc2V0IHZhck5hbWUgaW5kZXgxIGluZGV4Mi4uLiB2YWx1ZQ==</vline></verbatim>
<para>where:</para>
<quote><emph style="italic">varName</emph> is the name of a variable in the caller&apos;s scope.</quote>
<quote>If <emph style="italic">objc==4</emph>, then the <emph style="italic">indexList</emph> parameter is interpreted as a list of <emph style="italic">index</emph> arguments; if <emph style="italic">objc&gt;4</emph>, then the <emph style="italic">index</emph> arguments are inline on the command line.</quote>
<quote>In either case, Each <emph style="italic">index</emph> argument is an index in the content of <emph style="italic">varName</emph> or one of its sublists (see below). The format of <emph style="italic">index</emph> is either an integer whose value is at least zero and less than the length of the corresponding list, or else the literal string <emph style="italic">end</emph>, optionally followed with a hyphen and an integer whose value is at least zero and less than the length of the corresponding list.</quote>
<quote><emph style="italic">value</emph> is a value that is to be stored as a list element.</quote>
<para>The return value of the command, if successful, is the new value of <emph style="italic">varName.</emph></para>
<para>The simplest form of the command:</para>
<verbatim><vline encoding='base64'>ICBsc2V0IHZhck5hbWUgaW5kZXggdmFsdWU=</vline></verbatim>
<para>replaces, in place, the <emph style="italic">index</emph> element of <emph style="italic">varName</emph> with the specified <emph style="italic">value</emph>. For example, the code:</para>
<verbatim><vline encoding='base64'>ICBzZXQgeCB7YSBiIGN9</vline><vline encoding='base64'>ICBsc2V0IHggMSBk</vline></verbatim>
<para>results in <emph style="italic">x</emph> having the value <emph style="italic">a d c</emph>. The result, except for performance considerations and the details of error reporting, is roughly the same as the Tcl code:</para>
<verbatim><vline encoding='base64'>ICBwcm9jIGxzZXQgeyB2YXJOYW1lIGluZGV4IHZhbHVlIH0gew==</vline><vline encoding='base64'>ICAgICAgdXB2YXIgMSAkdmFyTmFtZSBsaXN0</vline><vline encoding='base64'>ICAgICAgc2V0IGxpc3QgW2xyZXBsYWNlICRsaXN0ICRpbmRleCAkaW5kZXggJHZhbHVlXQ==</vline><vline encoding='base64'>ICAgICAgcmV0dXJuICRsaXN0</vline><vline encoding='base64'>ICB9</vline></verbatim>
<para>except that where the <emph style="italic">lreplace</emph> command permits indices outside the existing list elements, the proposed <emph style="italic">lset</emph> command forbids them.</para>
<para>If multiple <emph style="italic">index</emph> arguments are supplied to the <emph style="italic">lset</emph> command, they refer to successive sublists in a hierarchical fashion. Thus,</para>
<verbatim><vline encoding='base64'>ICAgbHNldCB2YXJOYW1lICRpICRqIHZhbHVl</vline></verbatim>
<para>or, equivalently,</para>
<verbatim><vline encoding='base64'>ICAgbHNldCB2YXJOYW1lIFtsaXN0ICRpICRqXSB2YWx1ZQ==</vline></verbatim>
<para>asks to change the value of the <emph style="italic">j</emph>th element in the <emph style="italic">i</emph>th sublist of <emph style="italic">varName</emph>. Hence, the code:</para>
<verbatim><vline encoding='base64'>ICAgc2V0IHgge3thIGIgY30ge2QgZSBmfSB7ZyBoIGl9fQ==</vline><vline encoding='base64'>ICAgbHNldCB4IDEgMSBqOyAjIC1vci0gbHNldCB4IHsxIDF9IGo=</vline></verbatim>
<para>changes the value of <emph style="italic">x</emph> to</para>
<verbatim><vline encoding='base64'>ICAge2EgYiBjfSB7ZCBqIGZ9IHtnIGggaX0=</vline></verbatim>
<para>and the code</para>
<verbatim><vline encoding='base64'>ICAgc2V0IHkge3t7YSBifSB7YyBkfX0ge3tlIGZ9IHtnIGh9fX0=</vline><vline encoding='base64'>ICAgbHNldCB5IDEgMCAxIGk7ICMgLW9yLSBsc2V0IHkgezEgMCAxfSBp</vline></verbatim>
<para>changes the value of <emph style="italic">y</emph> to</para>
<verbatim><vline encoding='base64'>ICAge3thIGJ9IHtjIGR9fSB7e2UgaX0ge2cgaH19</vline></verbatim>
<para>This notation also dovetails prettily with the extension of the <emph style="italic">lindex</emph> command proposed in <tipref type="text" tip="22"/>. The command</para>
<verbatim><vline encoding='base64'>ICAgbGluZGV4ICR5IDEgMCAxOyAjIC1vci0gbGluZGV4IHkgezEgMCAxfQ==</vline></verbatim>
<para>will extract the element that is set by the command</para>
<verbatim><vline encoding='base64'>ICAgbHNldCAkeSAxIDAgMSAkdmFsdWU=</vline></verbatim>
<para>The <emph style="italic">lset</emph> command will throw an error and leave the variable unchanged if it is presented with fewer than three arguments, if any of the <emph style="italic">index</emph> arguments is out of range or ill-formed, or if any of the data being manipulated cannot be converted to lists. It will throw an error after modifying the variable if any write trace on the variable fails.</para>
<para>With the proposed <emph style="italic">lset</emph> command, the procedure to shuffle a list becomes much more straightforward:</para>
<verbatim><vline encoding='base64'>ICBwcm9jIHNodWZmbGUxYiB7IGxpc3QgfSB7</vline><vline encoding='base64'>ICAgICAgc2V0IG4gW2xsZW5ndGggJGxpc3Rd</vline><vline encoding='base64'>ICAgICAgZm9yIHsgc2V0IGkgMCB9IHsgJGkgPCAkbiB9IHsgaW5jciBpIH0gew==</vline><vline encoding='base64'>ICAgICAgICAgIHNldCBqIFtleHByIHtpbnQocmFuZCgpKiRuKX1d</vline><vline encoding='base64'>ICAgICAgICAgIHNldCB0ZW1wIFtsaW5kZXggJGxpc3QgJGpd</vline><vline encoding='base64'>ICAgICAgICAgIGxzZXQgbGlzdCAkaiBbbGluZGV4ICRsaXN0ICRpXQ==</vline><vline encoding='base64'>ICAgICAgICAgIGxzZXQgbGlzdCAkaSAkdGVtcA==</vline><vline encoding='base64'>ICAgICAgfQ==</vline><vline encoding='base64'>ICAgICAgcmV0dXJuICRsaXN0</vline><vline encoding='base64'>ICB9</vline></verbatim>
<para>The given implementation copies the list only once, the first time that the line:</para>
<verbatim><vline encoding='base64'>ICAgICAgICAgIGxzZXQgbGlzdCAkaiBbbGluZGV4ICRsaXN0ICRpXQ==</vline></verbatim>
<para>is executed. Thereafter, the list is an unshared object, and the replacements are performed in place.</para>
</section>
<section title="Reference Implementation">
<para>The author has implemented a simpler variant of the proposed command as an object command, and also proposes to bytecode compile it, although the implementation of bytecode compilation is incomplete. The reference implementation also does not yet expand <emph style="italic">objv[2]</emph> as a list in the case where <emph style="italic">objc==4,</emph> and is known to have memory leaks where ill-formed index arguments are presented. It is given here as <emph style="italic">concept code</emph> and to present its impact on performance of some common list operations. (Obviously, it will be completed and reviewed with the relevant maintainers prior to being committed to the Core.)</para>
<para>The core of the implementation is the following procedure:</para>
<verbatim><vline encoding='base64'>aW50</vline><vline encoding='base64'>VGNsX0xzZXRPYmpDbWQoIGNsaWVudERhdGEsIGludGVycCwgb2JqYywgb2JqdiAp</vline><vline encoding='base64'>ICAgIENsaWVudERhdGEgY2xpZW50RGF0YTsgICAgICAvKiBOb3QgdXNlZC4gKi8=</vline><vline encoding='base64'>ICAgIFRjbF9JbnRlcnAgKmludGVycDsgICAgICAgICAvKiBDdXJyZW50IGludGVycHJldGVyLiAqLw==</vline><vline encoding='base64'>ICAgIGludCBvYmpjOyAgICAgICAgICAgICAgICAgICAvKiBOdW1iZXIgb2YgYXJndW1lbnRzLiAqLw==</vline><vline encoding='base64'>ICAgIFRjbF9PYmogKkNPTlNUIG9ianZbXTsgICAgICAvKiBBcmd1bWVudCB2YWx1ZXMuICov</vline><vline encoding='base64'>ew==</vline><vline encoding='base64'></vline><vline encoding='base64'>ICAgIFRjbF9PYmoqIGxpc3RQdHI7ICAgICAgICAgICAvKiBQb2ludGVyIHRvIHRoZSBsaXN0IGJlaW5nIGFsdGVyZWQuICov</vline><vline encoding='base64'>ICAgIFRjbF9PYmoqIHN1Ykxpc3RQdHI7ICAgICAgICAvKiBQb2ludGVyIHRvIGEgc3VibGlzdCBvZiB0aGUgbGlzdCAqLw==</vline><vline encoding='base64'>ICAgIFRjbF9PYmoqIGZpbmFsVmFsdWVQdHI7ICAgICAvKiBWYWx1ZSBmaW5hbGx5IGFzc2lnbmVkIHRvIHRoZSB2YXJpYWJsZSAqLw==</vline><vline encoding='base64'>ICAgIGludCBpbmRleDsgICAgICAgICAgICAgICAgICAvKiBJbmRleCBvZiB0aGUgZWxlbWVudCBiZWluZyByZXBsYWNlZCAqLw==</vline><vline encoding='base64'>ICAgIGludCByZXN1bHQ7ICAgICAgICAgICAgICAgICAvKiBSZXN1bHQgdG8gcmV0dXJuIGZyb20gdGhpcyBmdW5jdGlvbiAqLw==</vline><vline encoding='base64'>ICAgIGludCBsaXN0TGVuOyAgICAgICAgICAgICAgICAvKiBMZW5ndGggb2YgYSBsaXN0IGJlaW5nIGV4YW1pbmVkICov</vline><vline encoding='base64'>ICAgIFRjbF9PYmoqKiBlbGVtUHRyczsgICAgICAgICAvKiBQb2ludGVycyB0byB0aGUgZWxlbWVudHMgb2YgYQ==</vline><vline encoding='base64'>ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKiBsaXN0IGJlaW5nIGV4YW1pbmVkICov</vline><vline encoding='base64'>ICAgIGludCBpOw==</vline><vline encoding='base64'></vline><vline encoding='base64'>ICAgIC8qIENoZWNrIHBhcmFtZXRlciBjb3VudCAqLw==</vline><vline encoding='base64'></vline><vline encoding='base64'>ICAgIGlmICggb2JqYyA8IDQgKSB7</vline><vline encoding='base64'>ICAgICAgICBUY2xfV3JvbmdOdW1BcmdzKCBpbnRlcnAsIDEsIG9ianYsICJsaXN0VmFyIGluZGV4ID9pbmRleC4uLj8gdmFsdWUiICk7</vline><vline encoding='base64'>ICAgICAgICByZXR1cm4gVENMX0VSUk9SOw==</vline><vline encoding='base64'>ICAgIH0=</vline><vline encoding='base64'></vline><vline encoding='base64'>ICAgIC8qIExvb2sgdXAgdGhlIGxpc3QgdmFyaWFibGUgKi8=</vline><vline encoding='base64'></vline><vline encoding='base64'>ICAgIGxpc3RQdHIgPSBUY2xfT2JqR2V0VmFyMiggaW50ZXJwLCBvYmp2WyAxIF0sIChUY2xfT2JqKikgTlVMTCw=</vline><vline encoding='base64'>ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVENMX0xFQVZFX0VSUl9NU0cgKTs=</vline><vline encoding='base64'>ICAgIGlmICggbGlzdFB0ciA9PSBOVUxMICkgew==</vline><vline encoding='base64'>ICAgICAgICByZXR1cm4gVENMX0VSUk9SOw==</vline><vline encoding='base64'>ICAgIH0=</vline><vline encoding='base64'></vline><vline encoding='base64'>ICAgIC8qIE1ha2Ugc3VyZSB0aGF0IHRoZSBsaXN0IHZhbHVlIGlzIHVuc2hhcmVkLiAqLw==</vline><vline encoding='base64'></vline><vline encoding='base64'>ICAgIGlmICggVGNsX0lzU2hhcmVkKCBsaXN0UHRyICkgKSB7</vline><vline encoding='base64'>ICAgICAgICBsaXN0UHRyID0gVGNsX0R1cGxpY2F0ZU9iaiggbGlzdFB0ciApOw==</vline><vline encoding='base64'>ICAgIH0=</vline><vline encoding='base64'></vline><vline encoding='base64'>ICAgIGZpbmFsVmFsdWVQdHIgPSBsaXN0UHRyOw==</vline><vline encoding='base64'></vline><vline encoding='base64'>ICAgIC8q</vline><vline encoding='base64'>ICAgICAqIElmIHRoZXJlIGFyZSBtdWx0aXBsZSAnaW5kZXgnIGFyZ3MsIGhhbmRsZSBlYWNoIGFyZyBleGNlcHQgdGhl</vline><vline encoding='base64'>ICAgICAqIGxhc3QgYnkgZGl2aW5nIGludG8gYSBzdWJsaXN0Lg==</vline><vline encoding='base64'>ICAgICAqLw==</vline><vline encoding='base64'></vline><vline encoding='base64'>ICAgIGZvciAoIGkgPSAyOyA7ICsraSApIHs=</vline><vline encoding='base64'></vline><vline encoding='base64'>ICAgICAgICAvKiBUYWtlIGFwYXJ0IHRoZSBsaXN0ICov</vline><vline encoding='base64'></vline><vline encoding='base64'>ICAgICAgICByZXN1bHQgPSBUY2xfTGlzdE9iakdldEVsZW1lbnRzKCBpbnRlcnAsIGxpc3RQdHIs</vline><vline encoding='base64'>ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAmbGlzdExlbiwgJmVsZW1QdHJzICk7</vline><vline encoding='base64'>ICAgICAgICBpZiAoIHJlc3VsdCAhPSBUQ0xfT0sgKSB7</vline><vline encoding='base64'>ICAgICAgICAgICAgcmV0dXJuIHJlc3VsdDs=</vline><vline encoding='base64'>ICAgICAgICB9</vline><vline encoding='base64'></vline><vline encoding='base64'>ICAgICAgICAvKiBEZXJpdmUgdGhlIGluZGV4IG9mIHRoZSByZXF1ZXN0ZWQgc3VibGlzdCAqLw==</vline><vline encoding='base64'></vline><vline encoding='base64'>ICAgICAgICByZXN1bHQgPSBUY2xHZXRJbnRGb3JJbmRleCggaW50ZXJwLCBvYmp2W2ldLCAobGlzdExlbiAtIDEpLCAmaW5kZXggKTs=</vline><vline encoding='base64'>ICAgICAgICBpZiAoIHJlc3VsdCAhPSBUQ0xfT0sgKSB7</vline><vline encoding='base64'>ICAgICAgICAgICAgcmV0dXJuIHJlc3VsdDs=</vline><vline encoding='base64'>ICAgICAgICB9</vline><vline encoding='base64'></vline><vline encoding='base64'>ICAgICAgICBpZiAoICggaW5kZXggPCAwICkgfHwgKCBpbmRleCA+PSBsaXN0TGVuICkgKSB7</vline><vline encoding='base64'></vline><vline encoding='base64'>ICAgICAgICAgICAgVGNsX1NldE9ialJlc3VsdCggaW50ZXJwLA==</vline><vline encoding='base64'>ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVGNsX05ld1N0cmluZ09iaiggImxpc3QgaW5kZXggb3V0IG9mIHJhbmdlIiw=</vline><vline encoding='base64'>ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLTEgKSApOw==</vline><vline encoding='base64'>ICAgICAgICAgICAgcmV0dXJuIFRDTF9FUlJPUjs=</vline><vline encoding='base64'>ICAgICAgICB9</vline><vline encoding='base64'></vline><vline encoding='base64'>ICAgICAgICAvKiBCcmVhayBvdXQgb2YgdGhlIGxvb3AgaWYgd2UndmUgZXh0cmFjdGVkIHRoZSBpbm5lcm1vc3Qgc3VibGlzdC4gKi8=</vline><vline encoding='base64'></vline><vline encoding='base64'>ICAgICAgICBpZiAoIGkgPj0gKCBvYmpjIC0gMiApICkgew==</vline><vline encoding='base64'>ICAgICAgICAgICAgYnJlYWs7</vline><vline encoding='base64'>ICAgICAgICB9</vline><vline encoding='base64'></vline><vline encoding='base64'>ICAgICAgICAvKg==</vline><vline encoding='base64'>ICAgICAgICAgKiBFeHRyYWN0IHRoZSBhcHByb3ByaWF0ZSBzdWJsaXN0LCBhbmQgbWFrZSBzdXJlIHRoYXQgaXQgaXMgdW5zaGFyZWQu</vline><vline encoding='base64'>ICAgICAgICAgKi8=</vline><vline encoding='base64'></vline><vline encoding='base64'>ICAgICAgICBzdWJMaXN0UHRyID0gZWxlbVB0cnNbIGluZGV4IF07</vline><vline encoding='base64'>ICAgICAgICBpZiAoIFRjbF9Jc1NoYXJlZCggc3ViTGlzdFB0ciApICkgew==</vline><vline encoding='base64'>ICAgICAgICAgICAgc3ViTGlzdFB0ciA9IFRjbF9EdXBsaWNhdGVPYmooIHN1Ykxpc3RQdHIgKTs=</vline><vline encoding='base64'>ICAgICAgICAgICAgcmVzdWx0ID0gVGNsX0xpc3RPYmpTZXRFbGVtZW50KCBpbnRlcnAsIGxpc3RQdHIsIGluZGV4LA==</vline><vline encoding='base64'>ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdWJMaXN0UHRyICk7</vline><vline encoding='base64'>ICAgICAgICAgICAgaWYgKCByZXN1bHQgIT0gVENMX09LICkgew==</vline><vline encoding='base64'>ICAgICAgICAgICAgICAgIHJldHVybiBUQ0xfRVJST1I7</vline><vline encoding='base64'>ICAgICAgICAgICAgfQ==</vline><vline encoding='base64'>ICAgICAgICB9IGVsc2Ugew==</vline><vline encoding='base64'>ICAgICAgICAgICAgVGNsX0ludmFsaWRhdGVTdHJpbmdSZXAoIGxpc3RQdHIgKTs=</vline><vline encoding='base64'>ICAgICAgICB9</vline><vline encoding='base64'></vline><vline encoding='base64'>ICAgICAgICBsaXN0UHRyID0gc3ViTGlzdFB0cjs=</vline><vline encoding='base64'>ICAgIH0=</vline><vline encoding='base64'></vline><vline encoding='base64'>ICAgIC8qIFN0b3JlIHRoZSByZXN1bHQgaW4gdGhlIGxpc3QgZWxlbWVudCAqLw==</vline><vline encoding='base64'></vline><vline encoding='base64'>ICAgIHJlc3VsdCA9IFRjbF9MaXN0T2JqU2V0RWxlbWVudCggaW50ZXJwLCBsaXN0UHRyLCBpbmRleCwgb2JqdltvYmpjLTFdICk7</vline><vline encoding='base64'>ICAgIGlmICggcmVzdWx0ICE9IFRDTF9PSyApIHs=</vline><vline encoding='base64'>ICAgICAgICByZXR1cm4gcmVzdWx0Ow==</vline><vline encoding='base64'>ICAgIH0=</vline><vline encoding='base64'></vline><vline encoding='base64'>ICAgIC8qIEZpbmFsbHksIHVwZGF0ZSB0aGUgdmFyaWFibGUgc28gdGhhdCB0cmFjZXMgZmlyZS4gKi8=</vline><vline encoding='base64'></vline><vline encoding='base64'>ICAgIGxpc3RQdHIgPSBUY2xfT2JqU2V0VmFyMiggaW50ZXJwLCBvYmp2WzFdLCBOVUxMLCBmaW5hbFZhbHVlUHRyLA==</vline><vline encoding='base64'>ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVENMX0xFQVZFX0VSUl9NU0cgKTs=</vline><vline encoding='base64'>ICAgIGlmICggbGlzdFB0ciA9PSBOVUxMICkgew==</vline><vline encoding='base64'>ICAgICAgICByZXR1cm4gVENMX0VSUk9SOw==</vline><vline encoding='base64'>ICAgIH0=</vline><vline encoding='base64'>ICAgICAgIA==</vline><vline encoding='base64'>ICAgIFRjbF9TZXRPYmpSZXN1bHQoIGludGVycCwgbGlzdFB0ciApOw==</vline><vline encoding='base64'>ICAgIHJldHVybiByZXN1bHQ7</vline><vline encoding='base64'></vline><vline encoding='base64'>fQ==</vline></verbatim>
<para>The procedure depends on a new service function, <emph style="italic">Tcl_ListObjSetElement</emph>:</para>
<verbatim><vline encoding='base64'>aW50</vline><vline encoding='base64'>VGNsX0xpc3RPYmpTZXRFbGVtZW50KCBpbnRlcnAsIGxpc3RQdHIsIGluZGV4LCB2YWx1ZVB0ciAp</vline><vline encoding='base64'>ICAgIFRjbF9JbnRlcnAqIGludGVycDsgICAgICAgICAvKiBUY2wgaW50ZXJwcmV0ZXI7IHVzZWQgZm9yIGVycm9yIHJlcG9ydGluZw==</vline><vline encoding='base64'>ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKiBpZiBub3QgTlVMTCAqLw==</vline><vline encoding='base64'>ICAgIFRjbF9PYmoqIGxpc3RQdHI7ICAgICAgICAgICAvKiBMaXN0IG9iamVjdCBpbiB3aGljaCBlbGVtZW50IHNob3VsZCBiZQ==</vline><vline encoding='base64'>ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKiBzdG9yZWQgKi8=</vline><vline encoding='base64'>ICAgIGludCBpbmRleDsgICAgICAgICAgICAgICAgICAvKiBJbmRleCBvZiBlbGVtZW50IHRvIHN0b3JlICov</vline><vline encoding='base64'>ICAgIFRjbF9PYmoqIHZhbHVlUHRyOyAgICAgICAgICAvKiBUY2wgb2JqZWN0IHRvIHN0b3JlIGluIHRoZSBkZXNpZ25hdGVk</vline><vline encoding='base64'>ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKiBsaXN0IGVsZW1lbnQgKi8=</vline><vline encoding='base64'>ew==</vline><vline encoding='base64'>ICAgIGludCByZXN1bHQ7ICAgICAgICAgICAgICAgICAvKiBSZXR1cm4gdmFsdWUgZnJvbSB0aGlzIGZ1bmN0aW9uICov</vline><vline encoding='base64'>ICAgIExpc3QqIGxpc3RSZXBQdHI7ICAgICAgICAgICAvKiBJbnRlcm5hbCByZXByZXNlbnRhdGlvbiBvZiB0aGUgbGlzdA==</vline><vline encoding='base64'>ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKiBiZWluZyBtb2RpZmllZCAqLw==</vline><vline encoding='base64'>ICAgIFRjbF9PYmoqKiBlbGVtUHRyczsgICAgICAgICAvKiBQb2ludGVycyB0byBlbGVtZW50cyBvZiB0aGUgbGlzdCAqLw==</vline><vline encoding='base64'>ICAgIGludCBlbGVtQ291bnQ7ICAgICAgICAgICAgICAvKiBOdW1iZXIgb2YgZWxlbWVudHMgaW4gdGhlIGxpc3QgKi8=</vline><vline encoding='base64'></vline><vline encoding='base64'>ICAgIC8qIEVuc3VyZSB0aGF0IHRoZSBsaXN0UHRyIHBhcmFtZXRlciBkZXNpZ25hdGVzIGFuIHVuc2hhcmVkIGxpc3QgKi8=</vline><vline encoding='base64'></vline><vline encoding='base64'>ICAgIGlmICggVGNsX0lzU2hhcmVkKCBsaXN0UHRyICkgKSB7</vline><vline encoding='base64'>ICAgICAgICBwYW5pYyggIlRjbF9MaXN0T2JqU2V0RWxlbWVudCBjYWxsZWQgd2l0aCBzaGFyZWQgb2JqZWN0IiApOw==</vline><vline encoding='base64'>ICAgIH0=</vline><vline encoding='base64'>ICAgIGlmICggbGlzdFB0ci0+dHlwZVB0ciAhPSAmdGNsTGlzdFR5cGUgKSB7</vline><vline encoding='base64'>ICAgICAgICByZXN1bHQgPSBTZXRMaXN0RnJvbUFueSggaW50ZXJwLCBsaXN0UHRyICk7</vline><vline encoding='base64'>ICAgICAgICBpZiAoIHJlc3VsdCAhPSBUQ0xfT0sgKSB7</vline><vline encoding='base64'>ICAgICAgICAgICAgcmV0dXJuIHJlc3VsdDs=</vline><vline encoding='base64'>ICAgICAgICB9</vline><vline encoding='base64'>ICAgIH0=</vline><vline encoding='base64'>ICAgIGxpc3RSZXBQdHIgPSAoTGlzdCopIGxpc3RQdHItPmludGVybmFsUmVwLm90aGVyVmFsdWVQdHI7</vline><vline encoding='base64'>ICAgIGVsZW1QdHJzID0gbGlzdFJlcFB0ci0+ZWxlbWVudHM7</vline><vline encoding='base64'>ICAgIGVsZW1Db3VudCA9IGxpc3RSZXBQdHItPmVsZW1Db3VudDs=</vline><vline encoding='base64'></vline><vline encoding='base64'>ICAgIC8qIEVuc3VyZSB0aGF0IHRoZSBpbmRleCBpcyBpbiBib3VuZHMgKi8=</vline><vline encoding='base64'></vline><vline encoding='base64'>ICAgIGlmICggaW5kZXggPCAwIHx8IGluZGV4ID49IGVsZW1Db3VudCApIHs=</vline><vline encoding='base64'>ICAgICAgICBpZiAoIGludGVycCAhPSBOVUxMICkgew==</vline><vline encoding='base64'>ICAgICAgICAgICAgVGNsX1NldE9ialJlc3VsdCggaW50ZXJwLA==</vline><vline encoding='base64'>ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVGNsX05ld1N0cmluZ09iaiggImxpc3QgaW5kZXggb3V0IG9mIHJhbmdlIiw=</vline><vline encoding='base64'>ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLTEgKSApOw==</vline><vline encoding='base64'>ICAgICAgICAgICAgcmV0dXJuIFRDTF9FUlJPUjs=</vline><vline encoding='base64'>ICAgICAgICB9</vline><vline encoding='base64'>ICAgIH0=</vline><vline encoding='base64'></vline><vline encoding='base64'>ICAgIC8qIEFkZCBhIHJlZmVyZW5jZSB0byB0aGUgbmV3IGxpc3QgZWxlbWVudCAqLw==</vline><vline encoding='base64'></vline><vline encoding='base64'>ICAgIFRjbF9JbmNyUmVmQ291bnQoIHZhbHVlUHRyICk7</vline><vline encoding='base64'></vline><vline encoding='base64'>ICAgIC8qIFJlbW92ZSBhIHJlZmVyZW5jZSBmcm9tIHRoZSBvbGQgbGlzdCBlbGVtZW50ICov</vline><vline encoding='base64'></vline><vline encoding='base64'>ICAgIFRjbF9EZWNyUmVmQ291bnQoIGVsZW1QdHJzWyBpbmRleCBdICk7</vline><vline encoding='base64'></vline><vline encoding='base64'>ICAgIC8qIFN0YXNoIHRoZSBuZXcgb2JqZWN0IGluIHRoZSBsaXN0ICov</vline><vline encoding='base64'></vline><vline encoding='base64'>ICAgIGVsZW1QdHJzWyBpbmRleCBdID0gdmFsdWVQdHI7</vline><vline encoding='base64'></vline><vline encoding='base64'>ICAgIC8qIEludmFsaWRhdGUgYW5kIGZyZWUgYW55IG9sZCBzdHJpbmcgcmVwcmVzZW50YXRpb24gKi8=</vline><vline encoding='base64'></vline><vline encoding='base64'>ICAgIFRjbF9JbnZhbGlkYXRlU3RyaW5nUmVwKCBsaXN0UHRyICk7</vline><vline encoding='base64'></vline><vline encoding='base64'>ICAgIHJldHVybiBUQ0xfT0s7</vline><vline encoding='base64'>ICAgIA==</vline><vline encoding='base64'>fQ==</vline></verbatim>
<para>Even without bytecode compilation, the performance improvement of array-based applications that can be achieved by the <emph style="italic">lset</emph> command is substantial. The following table shows run times in microseconds (on a 550 MHz Pentium III laptop, running a modified Tcl 8.4 on Windows NT 4.0, Service Pack #6) of the three implementations of <emph style="italic">shuffle</emph> that appear in this TIP.</para>
<verbatim><vline encoding='base64'>ICAgICAgICAgICAgICAgICAgICBSVU4gVElNRVMgSU4gTUlDUk9TRUNPTkRT</vline><vline encoding='base64'></vline><vline encoding='base64'>ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBWZXJzaW9u</vline><vline encoding='base64'>ICAgICAgICAgICAgICAgICAgICAgc2h1ZmZsZTEgICAgICAgc2h1ZmZsZTFhICAgICAgIHNodWZmbGUxYg==</vline><vline encoding='base64'>ICAgICAgICAgICAgICAgICAgICAgKE5haXZlKSAgICAgIChLIGNvbWJpbmF0b3IpICAobHNldCBjb21tYW5kKQ==</vline><vline encoding='base64'>IExpc3QgbGVuZ3Ro</vline><vline encoding='base64'>ICAgICAgICAxICAgICAgICAgICAgICAgICAyNiAgICAgICAgICAgICAgIDMyICAgICAgICAgICAgICAyNyAgICAgICAgICAgIA==</vline><vline encoding='base64'>ICAgICAgIDEwICAgICAgICAgICAgICAgIDEwOCAgICAgICAgICAgICAgMTUyICAgICAgICAgICAgIDEwMQ==</vline><vline encoding='base64'>ICAgICAgMTAwICAgICAgICAgICAgICAgMTYyNyAgICAgICAgICAgICAxNDYyICAgICAgICAgICAgIDkzNg==</vline><vline encoding='base64'>ICAgICAxMDAwICAgICAgICAgICAgIDExNzgzMSAgICAgICAgICAgIDE0Nzg5ICAgICAgICAgICAgOTU3NA==</vline><vline encoding='base64'>ICAgIDEwMDAwICAgICAgIFRlc3Qgc3RvcHBlZCAgICAgICAgICAgMTUyODUzICAgICAgICAgICA5NjkxMg==</vline></verbatim>
<para>Similar (30-50%) improvements are observed on many of the array related benchmarks that have been proposed. Bytecode compilation is expected to produce even greater improvements.</para>
<para>Another area where <emph style="italic">lset</emph> can achieve a major performance gain is in memory usage. The author of this TIP has benchmarked competing implementations of heapsort, one using Tcl arrays, and the other using <emph style="italic">lset</emph> to manipulate lists as linear arrays. When sorting 80000 elements, the Tcl-array-based implementation used 12.7 megabytes of memory; the list-based implementation was faster and used only 5.6 megabytes. The explanation is simple: each entry in the hash table requires an allocated block of twenty bytes of memory, plus the space required for the hash key. The hash key is a string, and requires at least six bytes. When both of these objects are aligned and padded with the overheads imposed by <emph style="italic">ckalloc</emph>, they require about 80 bytes of memory on the Windows NT platform. The memory cost of an element of a Tcl list, by comparison, is four bytes to hold the pointer to the object.</para>
</section>
<section title="Discussion">
<para>There are several objections that can be foreseen to this proposal.</para>
<itemize><item.i><para><emph style="italic">Why implement the command in the Core and not as an extension?</emph></para><para>In a word, <emph style="italic">performance.</emph> At the present state of Tcl development, only Core commands can be bytecoded. The cost of the hash table lookups in the <emph style="italic">Tcl_ObjGetVar2</emph> and <emph style="italic">Tcl_ObjSetVar2</emph> calls is significant, and can be eliminated from many common usages by the bytecode compiler. Since this command is likely to appear in inner loops, it is important to squeeze every bit of possible performance out of it.</para></item.i><item.i><para><emph style="italic">Why a new command in the global namespace?</emph></para><para>The author of this TIP feels that having a single added command that is parallel to the existing list commands is not polluting the namespace excessively. It would be a shame if this proposal founders upon the Naming of Names.</para></item.i><item.i><para><emph style="italic">Why a new command, rather than including this functionality in the proposed functionality of an extensible command for list manipulation?</emph></para><para>The author of this TIP has yet to see a formal proposal of any extensible list manipulation command; the closest thing appears to be Andreas Kupries&apos;s <emph style="italic">listx</emph> package [<url ref="http://www.oche.de/~akupries/tcltk.html"/>]. Given the size and complexity of any such modification, it is unlikely that it will be available in the Core in time for an 8.4 release. The performance improvements achievable by the <emph style="italic">lset</emph> command are needed urgently.</para></item.i><item.i><para><emph style="italic">Isn&apos;t this <tipref type="text" tip="29"/> warmed over?</emph></para><para>Several objectors to <tipref type="text" tip="29"/> indicated that they were willing to consider list element assignment implemented as a new command.</para></item.i><item.i><para><emph style="italic">Doesn&apos;t this proposal depend on multiple </emph>index<emph style="italic"> arguments to </emph>lindex&apos;&apos; (<tipref type="text" tip="22"/>)?</para><para>This proposal can stand alone. If multiple <emph style="italic">index</emph> arguments to <emph style="italic">lindex</emph> are also accepted, the resulting symmetry is pleasing. Having multiple <emph style="italic">index</emph> args to <emph style="italic">lset</emph> is much more important, because it is horribly difficult to implement equivalent functionality in pure Tcl without introducing excessive calls to <emph style="italic">Tcl_DuplicateObj</emph>. In fact, the reference implementation of <emph style="italic">lset</emph> presented in this TIP was motivated by the fact that its author gave up on the task and resorted to C.</para></item.i></itemize>
</section>
<section title="Implementation Notes">
<para>Having two versions of the syntax for the <emph style="italic">lset</emph> command is perhaps unattractive, but neither can be left out effectively.</para>
<para>The syntax where the indices are packaged as a single list allows a <emph style="italic">cursor</emph> into complex list structure to be maintained in a single variable. The list element that the cursor designates can be altered with a single call to the <emph style="italic">lset</emph> command, without needing to resort to <emph style="italic">eval</emph> (a command that is both expensive and dangerous) to expand the indices inline.</para>
<para>The syntax where each index is a first-class object is motivated by the performance of array-based algorithms. Programmers who are using lists as arrays know exactly how many subscripts they have, and in fact are generally iterating through them. A typical sort of usage might be the naïve matrix multiplication shown below.</para>
<verbatim><vline encoding='base64'>ICMgQ29uc3RydWN0IGEgbWF0cml4IHdpdGggJ3Jvd3MnIHJvd3MgYW5kICdjb2x1bW5zJyBjb2x1bW5z</vline><vline encoding='base64'>ICMgaGF2aW5nIGFuIGluaXRpYWwgdmFsdWUgb2YgJ2luaXRDZWxsVmFsdWUnIGluIGVhY2ggY2VsbC4=</vline><vline encoding='base64'>IA==</vline><vline encoding='base64'>IHByb2MgbWF0cml4IHsgcm93cyBjb2x1bW5zIHsgaW5pdENlbGxWYWx1ZSB7fSB9IH0gew==</vline><vline encoding='base64'>ICAgICBzZXQgb25lUm93IHt9</vline><vline encoding='base64'>ICAgICBmb3IgeyBzZXQgaSAwIH0geyAkaSA8ICRjb2x1bW5zIH0geyBpbmNyIGkgfSB7</vline><vline encoding='base64'>ICAgICAgICAgbGFwcGVuZCBvbmVSb3cgJGluaXRDZWxsVmFsdWU=</vline><vline encoding='base64'>ICAgICB9</vline><vline encoding='base64'>ICAgICBzZXQgbWF0cml4IHt9</vline><vline encoding='base64'>ICAgICBmb3IgeyBzZXQgaSAwIH0geyAkaSA8ICRyb3dzIH0geyBpbmNyIGkgfSB7</vline><vline encoding='base64'>ICAgICAgICAgbGFwcGVuZCBtYXRyaXggJG9uZVJvdw==</vline><vline encoding='base64'>ICAgICB9</vline><vline encoding='base64'>ICAgICByZXR1cm4gJG1hdHJpeA==</vline><vline encoding='base64'>IH0=</vline><vline encoding='base64'>IA==</vline><vline encoding='base64'>ICMgTXVsdGlwbHkgdHdvIG1hdHJpY2Vz</vline><vline encoding='base64'>IA==</vline><vline encoding='base64'>IHByb2MgbWF0bXVsdCB7IHggeSB9IHs=</vline><vline encoding='base64'>IA==</vline><vline encoding='base64'>ICAgICBzZXQgbSBbbGxlbmd0aCAkeF07ICAgICAgICAgICAgICAgICAjIE51bWJlciBvZiByb3dzIG9mIGxlZnQgbWF0cml4</vline><vline encoding='base64'>ICAgICBzZXQgbiBbbGxlbmd0aCBbbGluZGV4ICR4IDBdXTsgICAgICAjIE51bWJlciBvZiBjb2x1bW5zIG9mIGxlZnQgbWF0cml4</vline><vline encoding='base64'>IA==</vline><vline encoding='base64'>ICAgICBpZiB7ICRuICE9IFtsbGVuZ3RoICR5XSB9IHs=</vline><vline encoding='base64'>ICAgICAgICAgcmV0dXJuIC1jb2RlIGVycm9yICJyYW5rIGVycm9yOiBsZWZ0IG9wZXJhbmQgaGFzICRuIGNvbHVtbnNc</vline><vline encoding='base64'>ICAgICAgICAgICAgICAgICAgICAgICAgICAgICB3aGlsZSByaWdodCBvcGVyYW5kIGhhcyBbbGxlbmd0aCAkeV0gcm93cyI=</vline><vline encoding='base64'>ICAgICB9</vline><vline encoding='base64'>IA==</vline><vline encoding='base64'>ICAgICBzZXQgayBbbGxlbmd0aCBbbGluZGV4ICR5IDBdXTsgICAgICAjIE51bWJlciBvZiBjb2x1bW5zIG9mIHJpZ2h0IG1hdHJpeA==</vline><vline encoding='base64'>IA==</vline><vline encoding='base64'>ICAgICAjIENvbnN0cnVjdCBhIG1hdHJpeCB0byBob2xkIHRoZSBwcm9kdWN0</vline><vline encoding='base64'>IA==</vline><vline encoding='base64'>ICAgICBzZXQgcHJvZHVjdCBbbWF0cml4ICRtICRrXQ==</vline><vline encoding='base64'>IA==</vline><vline encoding='base64'>ICAgICBmb3IgeyBzZXQgaSAwIH0geyAkaSA8ICRtIH0geyBpbmNyIGkgfSB7</vline><vline encoding='base64'>ICAgICAgICAgZm9yIHsgc2V0IGogMCB9IHsgJGogPCAkayB9IHsgaW5jciBqIH0gew==</vline><vline encoding='base64'>ICAgICAgICAgICAgIGxzZXQgcHJvZHVjdCAkaSAkaiAwLjA=</vline><vline encoding='base64'>ICAgICAgICAgICAgIGZvciB7IHNldCByIDAgfSB7ICRyIDwgJG4gfSB7IGluY3IgciB9IHs=</vline><vline encoding='base64'>ICAgICAgICAgICAgICAgICBzZXQgdGVybSBbZXhwciB7IFtsaW5kZXggJHggJGkgJHJdICogW2xpbmRleCAkeSAkciAkal0gfV0=</vline><vline encoding='base64'>ICAgICAgICAgICAgICAgICBsc2V0IHByb2R1Y3QgJGkgJGogW2V4cHIgeyBbbGluZGV4ICRwcm9kdWN0ICRpICRqXSArICR0ZXJtIH1d</vline><vline encoding='base64'>ICAgICAgICAgICAgIH0=</vline><vline encoding='base64'>ICAgICAgICAgfQ==</vline><vline encoding='base64'>ICAgICB9</vline><vline encoding='base64'>IA==</vline><vline encoding='base64'>ICAgICByZXR1cm4gJHByb2R1Y3Q=</vline><vline encoding='base64'>IH0=</vline></verbatim>
<para>Note how we have an [lset] operation in the innermost loop, executed (m*n*k) times.</para>
<para>If in this instance, we have to write:</para>
<verbatim><vline encoding='base64'>ICAgICAgICAgICAgICAgICBzZXQgaW5kaWNlcyBbbGlzdCAkaSAkal0=</vline><vline encoding='base64'>ICAgICAgICAgICAgICAgICBsc2V0IHByb2R1Y3QgJGluZGljZXMgXA==</vline><vline encoding='base64'>ICAgICAgICAgICAgICAgICAgICAgW2V4cHIgeyBbbGluZGV4ICRwcm9kdWN0ICRpbmRpY2VzXSArICR0ZXJtIH1d</vline></verbatim>
<para>in place of the [lset] shown above, we add the cost of forming the list of indices to the cost of the inner loop. This cost is not to be sneezed at -- it&apos;s two expensive calls to <emph style="italic">ckalloc.</emph> (The cost can be avoided, at some cost in readability, by maintaning a variable containing the index list, and altering its elements with other uses of [lset].)</para>
<para>Richard Suchenwirth suggested the compromise that appears in this proposal. This scheme will perilous to performance if implemented naively. If the implementation of [lset] simply calls <emph style="italic">Tcl_ListObjGetElements</emph>, look what happens to the inner loop of our <emph style="italic">shuffle1b</emph> procedure:</para>
<verbatim><vline encoding='base64'>ICAgICAgZm9yIHsgc2V0IGkgMCB9IHsgJGkgPCAkbiB9IHsgaW5jciBpIH0gew==</vline><vline encoding='base64'>ICAgICAgICAgIHNldCBqIFtleHByIHtpbnQocmFuZCgpKiRuKX1d</vline><vline encoding='base64'>ICAgICAgICAgIHNldCB0ZW1wIFtsaW5kZXggJGxpc3QgJGpd</vline><vline encoding='base64'>ICAgICAgICAgIGxzZXQgbGlzdCAkaiBbbGluZGV4ICRsaXN0ICRpXQ==</vline><vline encoding='base64'>ICAgICAgICAgIGxzZXQgbGlzdCAkaSAkdGVtcA==</vline><vline encoding='base64'>ICAgICAgfQ==</vline></verbatim>
<itemize><item.i><para>Initially, {set i 0} sets i to the constant &quot;0&quot;; it is a string.</para></item.i><item.i><para>Evaluating the conditional {$i &lt; $n} will shimmer i to an integer; now it&apos;s an integer. (We had to do a call to strtol here.)</para></item.i><item.i><para>The [lindex $list $i] call now has to consider $i as a list of indices, and shimmers it to the list. This discards the internal rep, parses the string rep into a list, and then reconverts its first element to an integer.</para></item.i><item.i><para>OK, now the &apos;lset&apos; is happy, and no further shimmering occurs...</para></item.i><item.i><para>... until we get to the {incr i}. Now we go back to the string rep once again, shimmer it to an integer (yet another call to strtol), and invalidate the string rep because we&apos;ve incremented the integer.</para></item.i><item.i><para>Now we get back into the [lindex] once again, and need a list rep. This time, we have to format the integer as a string, parse it as a list, take the object representing element 0, and reparse that as an integer.</para></item.i></itemize>
<para>This sequence has converted the integer to and from a string, and performed four calls to <emph style="italic">ckalloc</emph>, but resulted in the same integer that we started with!</para>
<para>It is possible for a sufficiently smart compromise implementation to avoid all this shimmering. In the case where <emph style="italic">objc==4</emph>, the <emph style="italic">lset</emph> command must:</para>
<enumerate><item.e index='1'><para>Test whether <emph style="italic">objv[2]</emph> designates an object whose internal representation holds an integer. If so, simply use it as an index.</para></item.e><item.e index='2'><para>Test whether <emph style="italic">objv[2]</emph> designates an object whose internal representation holds a list. If so, perform the recursive extraction of indexed elements from sublists described above.</para></item.e><item.e index='3'><para>Form the string representation of <emph style="italic">objv[2]</emph> and test whether it is <emph style="italic">end</emph> or <emph style="italic">end-</emph> followed by an integer. If so, use it as an index.</para></item.e><item.e index='4'><para>Attempt to coerce <emph style="italic">objv[2]</emph> to an integer; if successful, use the result as an integer.</para></item.e><item.e index='5'><para>Attempt to coerce <emph style="italic">objv[2]</emph> to a list; if successful, use the result as an index list.</para></item.e><item.e index='6'><para>Report a malformed <emph style="italic">index</emph> argument; the <emph style="italic">indexList</emph> parameter is not a well-formed list.</para></item.e></enumerate>
<para>This logic handles all the cases of singleton lists transparently; it is effectively a simple-minded type inference that optimizes away needless conversions. With it in place, none of the <emph style="italic">lset</emph> examples shown in this TIP will suffer from type shimmering.</para>
<para>In the event that the related <tipref type="text" tip="22"/> is approved, the logic for parsing an index list will likely be combined with that used in the <emph style="italic">lindex</emph> command.</para>
<para>Bytecoding variadic commands like <emph style="italic">lset</emph> presents some interesting technical challenges; a discussion in progress on the Tcl&apos;ers Wiki [<url ref="http://wiki.tcl.tk/1604"/>] is recording the design decisions being made for bytecoding <emph style="italic">lset</emph> so that they can be applied to similar commands in the future.</para>
</section>
<section title="See Also">
<para><tipref type="text" tip="22"/>, <tipref type="text" tip="29"/>.</para>
</section>
<section title="Change History">
<para>This TIP has undergone several revisions by the original author. The most significant was made on 20 May 2001, where the syntax was revised to allow for either several indices inline on the command line or a list of indices.</para>
</section>
<section title="Copyright">
<para>This document has been placed in the public domain.</para>
</section>
</body></TIP>

