Skip to content
VLSI Mentor

Tcl Part I · Module 2 · The Tcl Execution Model

Braces: Grouping Without Substitution

Braces group text into one word, like quotes do, but they do not let substitutions run inside. Most explanations stop there, which makes braces sound like a defensive measure for when you want to avoid something. That framing never explains why real Tcl code is full of them. The real reason is positive: braces are how a block of code reaches a command unchanged, so the command can decide whether to run it and when. Every command in Tcl that takes a body of code depends on this.

Foundation12 min readTclPart IExecution ModelGroupingBraces

Part I · Module 2 · Chapter 2.8 · Braces: Grouping Without Substitution

1. What They Do

Braces group text into one word, and nothing inside is substituted:

Azvya Education Pvt. Ltd.VLSI Mentor
basic.tcl
set name Maya

puts "Hello, $name"
puts {Hello, $name}
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
Hello, Maya
Hello, $name

Same characters inside. The quoted version substituted $name. The braced version passed the two characters $name straight through.

Brackets behave the same way:

Azvya Education Pvt. Ltd.VLSI Mentor
brackets.tcl
puts "length is [string length hello]"
puts {length is [string length hello]}
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
length is 5
length is [string length hello]

In the braced version, the command inside the brackets never ran.

2. Braces Group, Exactly Like Quotes

Before the differences, the thing they share. Braces make one word:

Azvya Education Pvt. Ltd.VLSI Mentor
grouping.tcl
proc show {args} {
    puts "received [llength $args] word(s)"
    set i 1
    foreach w $args {
        puts "  word $i: $w"
        incr i
    }
}

show {my report.txt}
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
received 1 word(s)
  word 1: my report.txt

One word, same as "my report.txt" would give. Grouping is the job both forms do. What differs is what happens inside.

3. What Gets Through Unchanged

Everything that would otherwise be special:

Azvya Education Pvt. Ltd.VLSI Mentor
untouched.tcl
puts {a dollar $here, brackets [here], and a quote " here}
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
a dollar $here, brackets [here], and a quote " here

No escaping needed for any of it. This is the easiest way to produce text containing Tcl's special characters — compare the escaping you needed in Chapter 2.6.

4. Braces Nest

An inner pair of braces does not end the group:

Azvya Education Pvt. Ltd.VLSI Mentor
nested.tcl
show {outer {inner} outer}
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
received 1 word(s)
  word 1: outer {inner} outer

Tcl matched the outer braces by counting. The inner pair came through as ordinary characters.

This matters more than it looks. Every block of real code contains braces, so a body could not be passed as one word at all if nesting did not work.

5. The Real Purpose: Code as Data

Now the part that explains idiomatic Tcl.

Chapter 2.1 §5 showed a command receiving a body of code as a plain word, unrun. Braces are what made that possible. Here it is with a real command:

Azvya Education Pvt. Ltd.VLSI Mentor
deferred.tcl
foreach x {1 2 3} {
    puts "x is $x"
}
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
x is 1
x is 2
x is 3

Look at what foreach received: two braced words. The first, {1 2 3}, is the list to walk. The second is the body.

The body contains $x. If it had been substituted when the line was read, $x would have had no value yet and the command would have failed. Instead the body arrived as text, and foreach ran it three times — setting x before each run.

That is why the body is braced. Not to avoid substitution, but to delay it until the command is ready.

Now the same idea with if, which you have been writing since Module 1:

Azvya Education Pvt. Ltd.VLSI Mentor
if-body.tcl
set count 0

if {$count == 0} {
    puts "nothing to do"
}
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
nothing to do

Both braced words are deferred. if evaluates the first when it wants a true or false answer, and runs the second only if the answer is true. Neither could work if Tcl had substituted them on the way in.

6. When Braces Are the Wrong Choice

Braces are not always better. If you want a value in your text, braces stop you getting it:

Azvya Education Pvt. Ltd.VLSI Mentor
wrong-choice.tcl
set name Maya
puts {Hello, $name}
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
Hello, $name

That is a bug if you wanted the name. Nothing errors — you simply get the wrong text, which is harder to notice than a crash.

The choice between the two forms is Chapter 2.9, the next chapter.

7. The One Exception

Braces stop variable substitution and command substitution. They do not stop a backslash at the end of a line from joining it to the next.

Chapter 2.6 §6 proves this and explains the consequence. The accurate rule is:

Braces stop the substitutions that insert values — $ and [ ]. A backslash-newline still joins lines.

Worth stating precisely, because "braces change nothing" is the version most people carry, and it is wrong in exactly one place.

8. Common Mistakes

Using braces when you wanted a value. §6. No error, wrong text.

Thinking braces are "safer". They are not safer or stricter. They are a different instruction. Using them everywhere breaks every message that needs a variable.

Forgetting a closing brace. You get missing close-brace, and because Tcl keeps reading to the end of the file looking for it, the reported line is often the last line rather than the mistake. Count braces from the top.

Putting a lone brace in a comment. It still counts. That is Chapter 1.8 §6, and Tcl has a dedicated error message for it.

Expecting \n to work inside braces. It does not. {a\nb} is four characters. Use quotes when you want escape sequences processed.

9. Exercises

Exercise 1 — predict both forms. With set n 5, write down what each prints:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
puts "n is $n"
puts {n is $n}
puts "total [expr {$n + 1}]"
puts {total [expr {$n + 1}]}

Exercise 2 — produce awkward text. Write one puts that prints exactly this, using braces rather than escaping:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
use $name and [command] carefully

Then write it again with quotes and backslashes. Which is easier to read?

Exercise 3 — break a body. Run this, which is the §5 example with a quoted body instead of a braced one:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
set x PRESET
foreach x {1 2 3} "puts \"x is $x\""

Predict the three lines first. The result is worth seeing: the body was substituted once, when the line was read, so every iteration prints the same thing. Explain why using §5.

Exercise 4 — count the characters. Predict, then check:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
puts [string length "a\tb"]
puts [string length {a\tb}]

Exercise 5 — the nesting question. How many words is show {a {b} c} d? Predict, then run it.

10. Remember

  • Braces group text into one word, exactly as quotes do.
  • Nothing inside is substituted — $name and [command] pass through as characters.
  • Braces nest, so a body full of braces still arrives as one word.
  • Their real purpose is deferring code. A body reaches a command unchanged, and the command decides when to run it.
  • That is why if, foreach, while and proc all take braced bodies.
  • They are not "safer" than quotes. Using them when you wanted a value gives wrong text with no error.
  • One exception: a backslash at the end of a line still joins lines. Chapter 2.6 §6.

Next: how to choose between the two, in one rule.

Where this fits

Part of the Tcl curriculum.