Tcl Part I · Module 2 · The Tcl Execution Model
Command Substitution: [ ... ]
Square brackets run a command and put its result where the brackets were. That is all they do, and it is the single feature that lets Tcl stay small — because a language that can put any command's result anywhere does not need separate syntax for calling functions. This chapter teaches you to read brackets as evaluation order: the inner command finishes first, and what it returns becomes a word in the outer command. If you arrived from C or Python, it also clears up the most common wrong guess, which is that brackets index something.
Foundation13 min readTclPart IExecution ModelSubstitutionEvaluation Order
Part I · Module 2 · Chapter 2.5 · Command Substitution: [ ... ]
1. First, What They Are Not
In C, Python and SystemVerilog, [ ] means indexing — pick element n out of something.
In Tcl it never means that. Nothing in Tcl indexes with square brackets. Getting an element out of a list uses a command called lindex, which you meet in Module 6.
In Tcl, [ ] means: run the command inside, and use what it returns.
Clearing that up now saves a lot of confusion, because the wrong reading of [string length $x] — "something indexed by string length" — produces no useful meaning at all.
2. What They Do
puts [string length "hello"]5Two things happened, in order:
- Tcl ran
string length "hello", which returned5. - Tcl put
5where the brackets were, so the outer command becameputs 5.
The brackets did not survive. They were an instruction, like quotes are.
3. Why This Makes Tcl Small
Most languages need syntax for calling a function and using its value: print(len("hello")). The parentheses are grammar.
Tcl already has a way to run a command. Brackets just say "run one here and use the answer". So Tcl needs no function-call syntax, no operator for it, and no special case in the parser. One mechanism covers every command that returns anything.
That is why Chapter 2.1 could say Tcl has so little syntax. This is the feature that pays for it.
4. Tracing One
Take a line with both kinds of substitution:
set word "hello"
puts [string toupper $word]HELLOStep by step:
Source text:
puts [string toupper $word]
Words:
word 1: puts
word 2: [string toupper $word]
Build word 2 — the brackets must be evaluated:
inner command: string toupper $word
$word → hello
inner becomes: string toupper hello
inner returns: HELLO
Final values:
word 1: puts
word 2: HELLO
Run:
puts HELLOThe inner command is a complete command in its own right. It gets its own words, its own substitutions, and it runs to completion before the outer command exists in final form.
5. Nesting
Because the inner command is an ordinary command, it can contain brackets too:
puts [string toupper [string range "hello world" 0 4]]HELLOWork outwards from the innermost:
1. string range "hello world" 0 4 → hello
2. string toupper hello → HELLO
3. puts HELLO → prints HELLOThe rule is the same at every level: a command inside brackets finishes before the command containing it can run.
6. Brackets Inside Quoted Text
Brackets work inside double quotes, which is how most real output is built:
set width 8
puts "double width = [expr {$width * 2}]"double width = 16The quoted word contains ordinary text and one bracketed command. Tcl ran the command, put 16 in place of the brackets, and the whole thing stayed one word.
7. The Result Is a Value, Not More Tcl
This is the part that makes brackets safe, and it follows from Chapter 2.2.
A command's result is used as a word. It is not read as Tcl source. So a command can return text that looks like a command, and nothing runs:
proc give {} { return {[exit 1]} }
puts "got: [give]"
puts "still running"got: [exit 1]
still runninggive returned the four characters [exit 1]. Those characters became part of a word. They did not become a command.
The same is true of spaces. A result containing a space is still one word:
proc show {args} { puts "received [llength $args] word(s)" }
show [string tolower "TWO WORDS"]received 1 word(s)8. Brackets Inside Braces Do Not Run
The one place brackets are inert:
puts "length is [string length hello]"
puts {length is [string length hello]}length is 5
length is [string length hello]Same characters, different grouping, completely different behaviour. The braced version passed the brackets through as ordinary text.
That is Chapter 2.8, and it is not an accident — it is how a body of code reaches a command without running first.
9. Common Mistakes
Reading brackets as indexing. §1. Use lindex for list elements, in Module 6.
Forgetting the brackets. puts string length hello passes four words to puts instead of running anything. You will get an argument error, not a wrong answer.
Expecting a returned string to execute. §7.
Deep nesting for its own sake. Three levels is usually a line that should have been two lines.
Using brackets where you wanted literal text. If you need to print [ and ], escape them or use braces — Chapter 2.6.
10. Exercises
Exercise 1 — predict the result. What does each line print?
puts [string length "hello"]
puts [string length hello]
puts "[string length hello]"
puts {[string length hello]}Three of these print the same thing. Explain why the fourth is different.
Exercise 2 — trace it yourself. Write out the stages, as in §4, for:
set name "ada"
puts "Hello, [string toupper $name]!"Then run it and check your trace against the output.
Exercise 3 — unnest it. Rewrite this as two lines using a variable, and decide which version you would rather maintain:
puts [string toupper [string range "hello world" 0 4]]Exercise 4 — the forgotten brackets. Run puts string length hello and read the error. How many words did puts receive, and what does that tell you about what brackets are for?
Exercise 5 — order of operations. Predict the output, then run it:
proc loud {} { puts "loud ran" ; return "LOUD" }
puts "before [loud] after"Which appears first, loud ran or the before ... after line? Explain why.
11. Remember
[ ]runs the command inside and uses its result. It is never indexing.- The inner command finishes first. Its result becomes a word in the outer command.
- Nesting works inside-out, as deep as you like.
- Brackets work inside double quotes, which is how most messages are built.
- A result is a value, not source. Text that looks like a command does not run.
- A result containing spaces is still one word.
- Brackets inside braces do not run. Chapter 2.8.
Next: the third substitution, and the one that lets you put a literal $ or [ in your text.
Where this fits
Part of the Tcl curriculum.
