Tcl Part I · Module 6 · Lists
Building a List: list vs Writing Braces by Hand
If a list is just a string, it is tempting to build one by gluing text together with quotes. That works right up until a value contains a space, at which point the list quietly has more elements than you put in it and nothing reports a problem. This chapter shows that failure, shows what the list command does instead — including a case where it reaches for an escaping form you would never have written by hand — and is honest about the one situation where typing the braces yourself is exactly right.
Foundation12 min readTclPart IListsConstructionCorrectness
Part I · Module 6 · Chapter 6.2 · Building a List
1. The Tempting Mistake
A list is a string, so this looks reasonable:
set name "alpha"
set bad "$name other"
puts "<$bad> -> [llength $bad] elements"<alpha other> -> 2 elementsIt works. Two values in, two elements out.
Now the same code with a value that contains a space:
set name "two words"
set bad "$name other"
puts "<$bad> -> [llength $bad] elements"<two words other> -> 3 elementsThree elements. You supplied two values and got three.
No error. The string is a perfectly valid list — it just is not the list you meant. The space inside the value became a separator, because the value was placed into the text with nothing marking where it began and ended.
2. What list Does
set name "two words"
set good [list $name other]
puts "<$good> -> [llength $good] elements"
puts "element 0: <[lindex $good 0]>"<{two words} other> -> 2 elements
element 0: <two words>Two elements, and the first one is the whole value.
Look at what list produced: {two words} other. It added the braces. You did not write them; list worked out that the first value needed grouping and supplied it.
That is the command's entire job — take values, produce a string in which each value is one element, adding whatever quoting that requires.
3. It Quotes Only What Needs It
puts [list a b c]
puts [list a "b c" d]
puts [list "" a]a b c
a {b c} d
{} aPlain values are left alone. A value with a space gets braces. An empty value gets {}, because otherwise it would vanish entirely.
4. And It Handles Cases You Would Get Wrong
Here is where the argument becomes unarguable. What if a value contains a brace?
puts [list "has {brace" x]has\ \{brace xlist did not use braces — it could not, because the value contains an unbalanced one. It switched to backslash escaping, and the result is still a valid two-element list:
set l [list "has {brace" x]
puts "elements: [llength $l]"
puts "first: <[lindex $l 0]>"elements: 2
first: <has {brace>Two elements, and the first is exactly the original value, brace and all.
5. Braced Literals Are Fine
The rule is about values, not about syntax. When you are typing a fixed list into your source, braces are correct and idiomatic:
set modes {fast slow careful}
puts "[llength $modes] elements: $modes"3 elements: fast slow carefulNobody writes [list fast slow careful] for that, and nobody should. There is nothing to substitute, so there is nothing that can go wrong.
The distinction:
set name "two words"
set literal {alpha beta}
set from_values [list $name "other value"]
puts "literal: [llength $literal] elements"
puts "from values: [llength $from_values] elements"literal: 2 elements
from values: 2 elementsAnd remember from Chapter 2.8 that braces do not substitute:
set name "alpha"
set wrong {$name other}
puts "element 0: <[lindex $wrong 0]>"element 0: <$name>The literal characters $name, not the value. If you want the value, you need list.
Braces for fixed text you type.
listfor anything that came from a variable.
6. Growing a List
For adding to an existing list, lappend does the same quoting:
set found {}
lappend found "two words"
lappend found "other"
puts "<$found> -> [llength $found] elements"<{two words} other> -> 2 elementsSame braces, same correctness. Chapter 6.6 covers it properly; it is mentioned here because set found "$found $value" is the same mistake as §1 and is easy to reach for in a loop.
7. Building a List of Lists
list nests cleanly, because each value is quoted as one element:
set row1 [list clk 1]
set row2 [list "data bus" 64]
set table [list $row1 $row2]
puts "table: $table"
puts "rows: [llength $table]"
puts "row 1: <[lindex $table 1]>"
puts "its name: <[lindex $table 1 0]>"table: {clk 1} {{data bus} 64}
rows: 2
row 1: <{data bus} 64>
its name: <data bus>Two rows, and the two-word name survived two levels of nesting without you writing a single brace. Chapter 6.11 covers nesting properly.
8. Common Mistakes
set l "$a $b". The classic. §1.
set l "$l $new" in a loop. Same mistake, repeated. Use lappend. §6.
Writing braces around a variable: {$name} is the literal text $name. §5.
Using list for a fixed literal. Correct but noisy. §5.
Assuming a value "won't have spaces". Filenames, descriptions, names and error messages all do, eventually.
Reading the braces in output as data. {two words} is Tcl showing you one element that contains a space. lindex returns it without them.
9. Exercises
Exercise 1 — reproduce the corruption. Build a two-value list with "$a $b" where one value has a space. Print the element count. Then fix it with list.
Exercise 2 — watch the quoting. Print the result of list for these argument sets, and explain each: a b, "a b" c, "" a, a "b{c".
Exercise 3 — literal or list. For each, say which to use: a fixed set of three mode names; a pair of values read from a line; a list of column headings you type; a filename plus a size.
Exercise 4 — the brace value. Build a list whose first element is the text a{b. Confirm with llength and lindex that it is one element and the value is intact.
Exercise 5 — nest it. Build a two-row table where one cell contains two words, using only list. Then pull that cell out with one lindex.
Exercise 6 — spot the bug. This builds a list of filenames and is wrong. Say why, and fix it:
set files {}
foreach f {report.txt "my notes.txt" data.csv} {
set files "$files $f"
}
puts [llength $files]10. Remember
- A value placed into text with no quoting loses its boundaries.
"$name other"with a two-word name gives three elements. - Nothing errors. The result is a valid list with the wrong contents.
listadds whatever quoting each value needs — braces, or backslashes when braces will not work.- It quotes only what needs it, and gives an empty value
{}so it does not vanish. - Braced literals are correct for fixed text you type. Braces do not substitute, so
{$name}is literal. - The rule: braces for literals,
listfor values. lappenddoes the same quoting when growing a list.- Braces in printed output are Tcl showing element boundaries, not data.
Next: counting what you have built.
Where this fits
Part of the Tcl curriculum.
