What's up with the weird punctuation on this page ?
I think it looks more balanced and flows better . I think it's beautiful . It calms me . Maybe I picked it up from Chinese texts .
Tcl Thoughts
Tcl started off with an ingeniously simple and regular syntax , and has consistently followed through with a knack for exposing just the right foundational capabilities without overbuilding the language . Tcl attracts engineering types who already know how to build things , and provides a helpful toolset rather than a framework one would have to work around . If Guido has a time machine , then the Tcl Core Team has a timeless machine .
Expectations
Here are my guidelines for editing this wiki . I'm not sure how representative it is of the expectations and guidelines of other editors , so feel free to edit , annotate , and express your opinion and make suggestions regarding these guidelines , particularly if they've caused you any discomfort as an editor yourself .
This wiki is a resource for the community , and all content is subject to editing by anyone .
This wiki aims to be comprehensive , but it does not aim to be just an encyclopedia .
This wiki is not an official reference .
This wiki is not a source code management system .
It isn't necessary to have a Ph.D. in computer science to contribute .
No individual owns any page , not even their namesake page . (MJ: it's true)
A page can be as trivial or as complex as one desires .
There is a lot of room on this wiki , and there is room for content of varying quality . More frequently-visited pages are held to a higher standard . Content not meeting that standard may be moved off to less-frequented page .
This wiki tries to find a balance between the single coherent narrative of consensus and the chaos of discussion and individual presentation . The primary strategy to achieve this is to keep each page true to the measure of its creation , often by migrating some content to other pages . Organization and reorganization are constant activities and part of every contributor's duty .
Like ethernet , a wiki is intrinsically contention-based . A contributor should be ready and willing to take the time necessary to defend their position .
Collaborative editing can require extreme patience and indulgence . A contributor should meet unwelcome edits with a good nature and a willingness to engage in the game . It's almost always best to resist the temptation to take offense at other contributors . There is usually something to be learned , even from the conversation with the jerk that's insulting your mother , kicking your dog , and questioning your sexuality .
Clever/smart content is prized . A dumb/lazy contribution will be called out . A contributor may abruptly be pointed to references on how to do things the smart way . This is an opportunity to avoid the temptation to take offense , to think about what the smart way might be , and improve . Being smart is just a matter of practice .
It is never necessary to ask for permission to make an edit .
It is not necessary use external side channels to get approval or build consensus in advance for controversial edits . The wiki is the side channel , and consensus can be hashed out right here on its pages . However , discussions may be moved off to another page .
In the case of an edit conflict , a contributor should attempt to explain their position and rationale – again and again and again , and also to rethink their position – again and again and again .
When there is a succession of alternating edits representing competing viewpoints , to avoid falling into a rut of disagreement each edit should provide some new information or offer some new non-superficial variation of a previous edit . The idea is to keep the content and perspectives evolving . In many cases , a permutation on an idea is a sufficient alternative to direct conversation , but in other cases , it isn't .
Contributors are welcome to make a mess while they play , and even more welcome to put their toys back on the shelves when they are done .
This wiki is a good place to chat with other Tclers .
This wiki may contain content that makes you look stupid to your boss and coworkers .
This wiki is a good place to contribute code written under the influence of hallucinogenic substances .
Personality and flair are welcome , but are no substitute for quality content .
Signal-to-noise ratio is perhaps the top priority for all content.
Any topic even remotely-related to Tcl is fair game . Sometimes just being a Tcler , and having some other interest , is sufficient . That said , the signal-to-noise ratio should remain high . Every contribution should add value . If it doesn't , it might be deleted in short order .
The wiki is a teaching tool . Beginning Tclers are encouraged to ask questions and even to make changes to established content if they think they can improve it . If they are wrong , more experienced Tclers will take the opportunity and time to provide the requisite explanations .
A contributor who is right , and gets angry because another contributor is wrong , might as well go off and maintain their own website , as they are bound to be disappointed here .
This wiki needs a greater diversity of editors . If you are a child , teenager , retiree , or otherwise total newbie to programming , or know someone who is and might want to take up a new hobby , please encourage them to start learning Tcl and contributing to this wiki !
Because SQLite doesn't encode a blob into the database encoding when casting to text , it is possible to create a malformed Tcl_Obj with just a Tcl script .
A standard Tcl list in which each single value is encoded as a list containing only one item , and each sequence of values is a list containing more than one item . This makes it possible to differentiate e.g. , the single value one two three from a list containing the values one , two , and three .
Like switch , but each pattern is a list of patterns to match against the item in string at the corresponding position . All switch options are supported .
A drop-in replacement for proc that evaluates one command of a procedure body at at time , and a hook to inspect and manipulate the command before it is evaluated , and another to react to return conditions of the evaluation .
An escaping style that hews closely to the functional description of braces and quotations in the Tcl language specification , not using quotations when braces would suffice , and not using braces unless some backslash substitution would otherwise be necessary .
Replaced @memoize with an implementation that doesn't require an external variable , and is careful not to generate the string representation of its memory .
Provided the implementation of extend that wraps an existing ensemble of routines , and where extension routines are organized into their own namespaces .
Removed update , various changes to the user interface . Added object system to turn it into a widget that can be instantiated multiple times . Consolidated contributed code examples into one script .
Added an example of using the stubs mechanism on posix systems to initialize Tcl , and another example of accessing the TclOOC interface when embedding Tcl .
Replaced Larry Smith's Code with a rewrite that avoids proliferation of global variables by using stacks and indexes in namespace dictionaries instead .
Started editing code to remove update from loops , but then it turned into a project to practice the art of programming in Tk . Any tips and/or productive modifications to the code are welcome ! This revision runs noticeably faster than the previous one , probably due to changes that take better advantage of byte compilation . It also illustrates a method for distinguishing true key events from auto-repeated ones , which is used to make the accelerating spinbox work with arrow keys . See revision 4 of this page for the previous version .
Doesn't duplicate the string representation when duplicating an object, unless necessary. Remove TclListCopy() in favor of TclDuplicatePureObj(), allowing internal representation to change after the copy rather than before.
Discovered , reported , diagnosed , and fixed in b699959d625540f8 , which also adds a couple of more changes to make Tcl_AppendObjToObj() more efficient by reducing generation of string representations.
Occurs when the handle on the routine that represents the object is accessed during deletion of the namespace for an object , but that routine has already been deleted and the handle nullified . Fixed in 3ddbaef3566385a2 .
Simplify the implementation of Tcl_DeleteNamespace in order to make behaviour more consistent , facilitate the resolution of outstanding issues , and pave the way for further developments .
Use duplicate Tcl_Obj structures to hang onto previous intreps . Eliminates the need to generate a string representation of a list that contains redundant keys when used as a dictionary . string length No longer causes intreps of items in a list or dictionary to be discarded . With a little more work , string range could be modified to take advantage of the cached objects and not generate a string for the entire list or dictionary just to derive some substring . The next step is to take advantage of this patch to never generate the string representation of a full list or dictionary . This patch introduces TclObjLookupTyped() , which returns a Tcl_Obj of the desired type , or NULL if there isn't one handy . Such an interface could prove generally useful in further development of Tcl .
Discovered , diagnosed , reported , and fixed in f5eadcbf9a6b1b4c , although a better fix would be to reform the code base, improving the function signatue of Tcl_UniCharToUTf() and removing code related to "TCL_UTF_MAX = 3".
Corrected spelling errors in source code comments and in documentation {2023 04 12}
Offers the ability to read in messages from a channel , more consistent and straightforward manipulation of message headers , and more flexibility for use as a general Internet Message library . See also commit 37008172d910 .
AMG: I've asked about this before on the coroutine page . Search for "Cloning" . Sorry , not going to happen . Quote from MS: "The real obstacle is that each coroutine has its own Tcl stack , which stores (among other things) pointers to itself . Making a copy of that stack implies fixing those autorefs , which in turn implies storing the nature of each entry in the stack to know if it has to be fixed or not (and how) , which in turn implies a redesign of the tcl stack ."
Assuming the following alternate definition of variable substitution ,
In a word , $ and the variable name immediately following it are replaced by the value of that variable . The variable name can be a sequence of characters in the range 0-9a-zA-Z , or , if the variable name begins with " , [ , { , $ , or \ , the variable name is determined according to the corresponding rule for double quotes , command substitution , braces , variable substitution , or backslash substitution , respectively . Non-whitespace characters may occur immediately after the variable name , i.e. , immediately after the closing " , ] , or } . If the variable is immediately be followed by a string index of the form (index) , index , which may not contain a literal ) , is the name of an element with that array .
Tcl would behave like this:
# warning: hypothetical behaviour
% set {{}} hello
hello
% puts ${{}}
hello
% puts $[join { \{ \} } {}]
hello
% set \{ goodbye
% puts $\{}
goodbye}
% set greeting greetings!
greetings!
% puts $"greeting".yada
greetings!.yada
% set name {{}}
{}
% puts $$name
hello
% puts $"$name"
hello
% set na \{
{
% set me \}
}
% puts $"$na$me"
hello
This makes variable substitution more consistent with the other syntax rules of Tcl , and more flexible as well .
AMG: This functionality is provided by single-argument [set] . Tcl's $ notation is a shorthand for single-argument [set] that only works for variable names that are literal , though substitutions are allowed inside the array index .
I don't understand what you are asking for with your "greetings" example . Tcl already behaves like you show because the period following $greeting isn't treated as part of the variable name .
In Brush I propose a new $"..." notation that works like [set ...] , as shown in your puts $"$name" and puts $"$na$me" examples . If you want to get silly , this notation can be nested because opening $" is distinct from closing " .
$$name is explicitly disallowed because of ambiguity when combined with indexing . Does faux-Brush $$name{0} mean the same as Tcl [set [lindex $name 0]]] or Tcl [lindex [set $name] 0] ? Instead write $"$name{0}" or $"$name"{0} to make the intent clear .
Brush's proposed $[...] notation does not work as shown in your puts $[join ...] example . To get that behavior , instead write $"[join ...]" . Brush's $[...] is a mechanism for applying variable indexing operations to the result of a [script substitution] without having to first save the substitution result into a variable . For instance , Brush $[command](key1 key2){1 2} means precisely the same as Tcl [lindex [dict get key1 key2 [command]]] 1 2] . The C analogue would be roughly command()->key1->key2[1][2] .
PYK 2016-04-07: I've added quotes to the "greetings" example , which is there just to confirm that the behaviour in that case doesn't change , in spite of the fact that normally , extra characters aren't allowed after the closing brace or quote . The apparent ambiguity of $$name(index) doesn't seem too problematic to me ; interpreting it as the equivalnet of of $"$name(index)" is visually straightforward enough . In that last Brush example I think you meant [lindex [dict get [command] key1 key2] 1 2] .
info nameofexecutable is currently used in the code that sets $auto_path in init.tcl , so with the current behaviour , $auto_path is set differently for every symlink to a tclsh executable .
EMJ: Assuming that you mean init.tcl . If you have installed tclsh in some strange place then that place should be on your path anyway (as , of course , is the official location) . I really don't see why you would ever need a symlink to tclsh or wish at all .
Would it scale better to have commands like [append list ...] rather than lappend ?
AMG: I'd much prefer [list append] , but the [list] command is thoroughly taken . Tcl subcommand structures are typically named by putting the noun (class) first , at least when it's not the case that the object instance handle name is the command name .
PYK 2013-09-22: Been thinking about it often , and I still have this suspicion that the world might be a better place if we all went with [verb object ...] rather than [object verb ...] . It still seems reasonable to me that Tcl could dispatch based what type role the object most-recently played .
AMG: I'd be careful about the word "object" in this situation . To me , the word "object" , especially when next door to the word "verb" , is a specific thing being acted on , in other words an instance . In C++ notation , "myList.append(value);" , would have myList as the object , or more properly the instance . When you say "object" you really seem to mean "type" .
So , how would command dispatch work if the hierarchy went the other way around ? I see types as collections of operations , along with established schemas for values of that type . So types are above operations in the hierarchy . In the Tcl command ensemble dispatch concept , that means the type name must go first . Put it second , and things get funny , especially when the ensemble is more than two levels deep . You could implement this if you like , either by making procs for all operations , which take type names as their first arguments , or by making an unknown handler that does custom dispatch .
As for type inference , this is scary in situations where the type cannot be determined by static , lexical analysis , for instance when operating on an argument to a proc . The behavior of the proc would depend on what the caller had most recently done with the arguments . For instance , let's say the length of a list is the number of space-separated words in its string representation , but the length of a dictionary is the number of keys . How does a proc tell how many words there are in a value that was just passed to it ? It would have to do some dummy operation to force the value to be a list . Now this potentially screws up all the code (including extensions , the proc's caller , any holders of shared values) that assumes the value is a dictionary . Far safer to explicitly say "this is a dict" or "this is a list" every time an operation is performed . In other words , what Tcl already does !
should the string command be retired and its subcommands become "top-level" Tcl commands ?
DKF: Ensembles purely organizational . From 8.6 onwards — or is it 8.6.1 ? I forget when the commit went into HEAD — the built-in ones compile themselves out entirely in almost all cases (the exceptions are some of the complex cases , notably including namespace eval and info frame); the implementation is used just as if it was an independent command . This isn't done for user-created ensembles , because we suspect the performance cost to Snit is suspected to be too high there . (I ought to check that assumption . If it's wrong , it's trivial to turn on compilation for all ensembles . Contrast with TclOO where compilation of the dispatch doesn't make sense at all , and which uses other mechanisms to accelerate .)
Should [dict incr] return a single value rather than a key-value pair ?
It would be more consistent with incr if it did , and most of the time , the single value is what I need .
AMG: The goal was consistency with most of the other [dict] commands that take a variable name as their first argument . These commands all return the entire modified dictionary , not just the portion that changed .
If we were to generalize the concept of nested dicts such that a zero-length key path refers to the entire dict , and we were to make all dict commands support key paths , then [dict incr] with zero keys would behave the same as [incr] . (By the way , [dict get] and [dict with] support zero-length key paths , even though [dict set] and [dict unset] do not . However , they still expect their argument to be a valid dict , or at least an even-length list , so they do not exhibit the full generalization I just suggested .)
There are two other [dict] commands that take a variable name as their first argument , but they also take a script as their last argument . Even though they modify the dictionary , these two commands return the return value of the script . This is consistent with non-looping commands like eval , if , try , and [uplevel] that take script arguments .
If a character can't be converted to the target encoding , it is replace with a character that can .
Logical Strings
In the following example ,
set mylist [list some huge list]
set script "lappend somevar {*}[list mylist]"
eval $script
A string representation is generated for $script , and that representation unfortunately includes the string representation of $mylist . In some future version of Tcl where each type of value has an interface which includes the standard operations of a string abstract data type , A "concatenated value" type could provide the logical equivalent of the string representation in $script while not actually generating the full string .
This is one of the fundamental pieces that remains to be added to Tcl in order to give it more of the power of other Lisps , making The Best Lisp (TM) .
Modify uplevel so that when only 1 argument is passed , it won't try to interpret that one argument as the optional level argument .
Add Moose-like Convenience to a Tcl object system
In the Tcl Chatroom on 2014-02-26 , mst described how convenient it is in Moose , an object system in Perl , to create objects with slots for lazy variables , which the ability , if desired , to initialize those variables at the time of object creation . I think something like that would be almost trivial to add to a Tcl object system , perhaps to ycl::ns::object
AMG: How is this different than initializing those variables in the object's instance constructor ?
PYK: mst described a system where the attribute would not be computed until first accessed , but then optionally could be initialized in the constructor . Apparently in Moose there is a nice terse syntax to express this . The difference , of course , might be felt when deriving the value is relatively costly , and that cost can be deferred until the value is actually needed , and then the value could be cached for future reference .
Currently , tclsh switches to interactive mode whenever the Tcl script is read from stdin . This is even true when stdin is a pipeline . One consequence of the current behaviour is that when there are errors in a script that comes through a stdin redirection , the interpreter exit , or to display a stack trace . This can cause a script to behave quite differently depending on whether it is in a named file or redirected from stdin .
Modify tclsh to accept scripts whose name begins with "-"
AMG: I propose the opposite: make tclsh bail out if its arguments , prior to the filename , can't be interpreted as valid options . The current behavior is bizarre . If the first character of the first argument is "-" (and the argument's not "-encoding") , tclsh drops into interactive mode with all arguments placed in $argv . What good is this ?
Right now the only supported option is "-encoding" . I'm not comfortable with allowing scripts whose names being with "-" because we unavoidably get in trouble when the script's name is "-encoding" .
PYK: I don't think Tcl needs to do that much babysitting . If someone is reckless enough to both have a file named -encoding and be using tclsh -encoding , they're asking for it . On the other hand , why should Tcl be able to interpret any file , regardless of name ?
AMG: Why "should" ? I'm going to assume you meant to say "shouldn't" . It may seem unfortunate , but every system must define some limits which its users must observe . In this case , Tcl doesn't allow the startup script's name to begin with a dash . This is done to avoid ambiguity with command-line options . There's an easy workaround though: prefix the script's name with "./" , an absolute path , or any other such construct containing a slash . Many other workarounds exist involve renaming , copying , linking , or sourcing the file .
RLE: There is another alternative . First , tighten up tclsh's (and wish's) option parsing to accept only valid options and refuse invalid ones . Then add support for the "end of options" option "--" , at which point if one wants to have a script named -encoding , one would do:
tclsh -- -encoding
And if one wanted to set an explicit encoding , for a script named -encoding , one would do:
tclsh -encoding utf-8 -- -encoding
This would be consistent with a bunch of tcl commands that already implement the end-of-options option "--" , and with a bunch (most , at least of the GNU variety) command line programs that also accept the end-of-options option .
In the following example , when the first command sets b , the memory usage quadruples . In the second example , it doubles . If possible , modify string replace so that the first example only doubles the memory usage while the second example doesn't change it .
set a [string repeat a 10000000]; set b [string replace $a[set a {}] 0 0 b]
set a [string repeat a 10000000]; set b [string replace $a 0 0 b]
AMG: You sure you don't have that backwards ? I would expect the second line (example?) to cause the larger growth in memory because now both $a and $b contain 10000000-character strings . In fact , I just tested this on my system , and indeed it behaves as I describe .
Run 1:
% set a [string repeat a 10000000] ; exec ps -o size --no-header [pid]
19000
% set b [string replace $a[set a {}] 0 0 b]; exec ps -o size --no-header [pid]
19044
Run 2:
% set a [string repeat a 10000000]; exec ps -o size --no-header [pid]
19000
% set b [string replace $a 0 0 b] ; exec ps -o size --no-header [pid]
28768
I'm not entirely sure I know what you're asking . My best guess is a request to make [string replace] not return a new pure string but rather some Tcl_Obj that references its source string value but contains an annotation saying to replace its first character with b when a string representation is demanded . Indeed that would cut memory usage in half for the examples you give , but creating a new "string replace result" intrep would be a special case of dubious benefit . Indeed , that benefit would vanish as soon as a string representation is needed , which will be pretty much right away , unless you were to never look at or use the value you got .
PYK 2014-04-24: Yes , I had it backwards . My idea for improvement is that if the number of characters being substituted in is the same as the replacement value , and the object is unshared , could the string representation be modified in-place ? Those criteria should be true for the first example:
set a [string repeat a 10000000]; set b [string replace $a[set a {}] 0 0 b]
AMG: This first example already works just fine . My simple profiling above shows no appreciable increase in size . What's the problem ?
eliminate newline substitution in comments ? (tclParse.c/ParseComment)
AMG: How about eliminating backslash-newline substitution inside braces ? No one seems to know what it's good for , and it has caused strange problems for me with templates and subst . Specifically , long lines continued with backslash must have percent signs at the beginning of each physical line when the data is [read] in from disk and must not have percent signs at the beginning of subsequent lines when the data is inside braces .
PYK 2013-03-08: That's an interesting point . I'd never really paid attention to that , but now that you mention it , it does seem odd/useless/obstructive . Maybe it's just an artifact of the original implementation of the parser . If anyone out there can shed more light on the subject , feel free to comment on this page , and I might move the discussion to another page later .
PYK 2013-04-15: I found an example of backslash-newline substitution being put to use at scriptSplit , where it allows the error message to be indented in the source code , but come out de-indented at runtime . That use case led me to wonder if the \<newline>whiteSpace rule shouldn't be changed to \<newline>\<newline>*whiteSpace instead
AMG: Even if backslash-newline substitution inside braces were to be eliminated , the code you reference would still produce error messages all on one line , with indents and line breaks removed . This is because the parser already has special handling for backslash-newline-whitespace when outside braces . Inside quotes , it's a single space . Outside , it's a word (argument) separator . I find it redundant and destructive to do this processing at multiple layers of interpretation .
write a port of the DOS game , Slicks'n'Slides , as mentioned at Car racing in Tcl
AMG: Great idea , I had been considering the same thing . But perhaps there's a way to make a Tcl frontend for TeX or for one of the frontends TeX already has (e.g. LaTeX or DocBook) ?
PYK: I think a front-end for TeX would most definitely be the way to start .
PYK 2014-08-19: Another starting point might be to write a Tcl patoline extension and build on that
Tcl implementation in RPython . May involve writing a Tcl analogue to Rpython
[binary set]
AMG: I assume this would modify individual bits and fields of a structure in-place , thereby avoiding the need to reconstitute the entire structure each time a piece has to change . Kind of like [lset] ...
fix lindex so that [lindex $somevalue] throws an error if $somevalue is not a list
AMG: I'm assuming you're talking about single-argument [lindex] which simply returns its argument . What's the problem with that ? I believe it currently has the correct behavior . [lindex]'s subsequent arguments specify what list indexes should be applied , so if there are no list index arguments , no list indexing should be applied . If no list indexing should be applied , there should be no requirement that the data be a list .
Let's look at it from another perspective . Take this code:
lindex {{{asdf}fff}} 0
This returns {asdf}fff , since that is the one (and only) element of [lindex]'s first argument , which is indeed a list . The fact that {asdf}fff itself isn't a list is totally irrelevant . But then take this:
lindex {{{asdf}fff}} 0 0
Now we have a problem . [lindex] is being asked not to return {asdf}fff but instead to drill inside it and take its first element . Since it's not a list , there is an error .
In summary , it's already the case that [lindex]:
is capable of returning values that aren't lists , and
is unconcerned about the listhood of values (or nested values) when it's not being asked to index into them .
I see no reason to special-case the situation where [lindex] is given only a single argument .
PYK 2014-08-22: I've since become accustomed to using lindex as the identity function , and because anyone providing it with ony 1 argument has surely read the documentation , I no-longer see it as broken . So now , on with the critique of your analysis ! The fact that elements of a list don't themselves have to be lists isn't particularly relevant to the argument , and lindex without an argument already is a special case , both because it is the only time that lindex isn't actually being asked to return an element from a list , and because it's the only time that the first argument is not required to be a list . This special case is a hole that like other such holes , was ingeniously exploited . It's an easter egg , and a very useful one that spices up any script with a touch of wizardry .
AMG: I argue the opposite , that single-argument [lindex] is not a special case nor a hole but rather a natural consequence of the generality of [lindex]'s definition . Furthermore , I argue that the fact it doesn't require its lone argument to be a list is to be a direct consequence of that same definition .
I know I'm repeating myself , but I'll try it anyway to see if I can make my point clearer .
[lindex] must be given one data argument followed by any number (even zero) of list index arguments . Initially , [lindex] lets the data argument be the tentative result . For each index argument n , [lindex] obtains the nth element of the result and lets it be the new result .
That's almost the entire definition . The only part I left out is that the indexes can either be separate arguments or a single list .
From this I'm sure you can see that if there are no index arguments , no list indexing is performed . And if no list indexing is performed , there's no need for the first argument to be a list . It's the result and that's the end of the story .
As for your argument , I will now use similar logic to show that four-argument [lindex] is a special case . It's the only time [lindex] is being asked to return an element from a list in a list in a list , and it's the only time its first argument is required to be a list nested minimum three levels deep along the selected path . Basically I'm saying that the number zero is no more special than the number three .
PYK 2014-08-23: That is a most illuminating and satisfying reply . As long as lindex is going to accept only 1 argument , that operational description of why the current behaviour is the correct behaviour makes perfect sense . I appreciate the twist on my logic for the four-argument case , but it had to drop the key word , isn't , for the word is . Saying that lindex is implicitly not being asked to extract anything from its final result is a bit of a tautology — but wait — what could be more fitting than a tautology to express an identity ? I give up !
[lsearch -filter] . Or even better , have lindex interpret its arguments as lists of indexes
AMG: [lindex] already supports having an indexList argument , and {*} can be used to make any argument work as an index list . Please post an example showing what you have in mind .
lindex {uno dos tres quatro} {1 3}
# -> {dos quatro}
For compatibility with current usage , it might have to be
lindex {uno dos tres quatro} {{1 3} {}}
#-> {dos quatro}]
AMG: Ah , we're talking about two different kinds of index lists . I was talking about a list of indices forming a path to a single element located within nested lists . You're talking about indexing multiple elements . The [lmap] command may help:
proc lindex_multi {data indexes} {
lmap index $indexes {lindex $data $index}
}
lindex_multi {uno dos tres quatro} {1 3}