Tcl Part I · Module 1 · Introduction to Tcl
Comments, and the One Place Tcl Allows Them
Every language has comments and you already know how to write them, so this chapter looks like it should be two paragraphs. It is not, because Tcl handles comments differently from every other language you are likely to have used, and the difference produces a failure that is genuinely baffling the first time: adding a comment to the end of a working line stops that line from working. The explanation is a single sentence, and it is a direct consequence of the design in Chapter 1.1. Once you have it, you also understand three other things that otherwise have to be memorised separately — including a trap that can break an entire block of code from inside a comment.
Foundation12 min readTclPart IFundamentalsCommentsSyntax
Part I · Module 1 · Chapter 1.8 · Comments, and the One Place Tcl Allows Them
1. The Easy Part
A line whose first word is # is a comment. Tcl ignores it.
# This script checks the input file before processing it.
puts "starting"startingLeading whitespace is fine — Tcl looks for the first non-blank character:
# indented comments work exactly as you would expect
puts "starting"So far this matches every language you know. Now the part that does not.
2. The Failure Nobody Predicts
Take a line that works:
puts "hi"Add a comment to the end of it, the way you would in C, Python, Bash, or SystemVerilog:
puts "hi" # explain what this doesPredict the output. Then look:
wrong # args: should be "puts ?-nonewline? ?channelId? string"
while executing
"puts "hi" # explain what this does"
(file "trailing.tcl" line 1)The comment broke the command. And notice which error it is — the usage message from Chapter 1.7 §4, complaining about the number of arguments.
That is the clue. Tcl did not ignore your comment. It passed it to puts as four more arguments: #, explain, what, this, does. The command received six arguments where it accepts at most three.
It is not a different language's comment syntax. It is not a comment at all.
3. The Rule, in One Sentence
#begins a comment only where Tcl is expecting a command. Anywhere else it is an ordinary character.
That is the whole of it. Every other fact in this chapter follows from it.
Why it works that way is Chapter 1.1. Tcl's grammar is "a command, then its words", and # is handled at the point where Tcl is about to read a command name. Once Tcl has started collecting words for a command, it is collecting words — and # is just a character that can appear in one.
In other words, Tcl does not have a comment character. It has a position where a # means "ignore the rest of this line".
4. The Fix: End the Command First
If # needs a position where a command could start, then give it one. A semicolon ends the current command, so the position right after it is exactly that:
puts "hi" ;# this IS a comment, because the ; ended the commandhiWorking again. The ; finished the puts command, which put Tcl back where a command may begin, which made the # a comment.
The ;# sequence is therefore idiomatic Tcl, and you will see it constantly in real code. It is not a special token — it is a semicolon followed by a comment, and knowing that is better than memorising the pair.
5. The Other Two Consequences
Both fall straight out of §3, which is the reason the rule is worth having rather than three separate facts.
A # inside quoted text is just a character. Tcl is collecting a word, not looking for a command, so nothing is special:
puts "the # character is fine here"the # character is fine hereNo escaping needed, and no comment started.
Any command, not just puts, breaks the same way. This is not a puts quirk:
set x 5 # a notewrong # args: should be "set varName ?newValue?"
while executing
"set x 5 # a note"
(file "set-note.tcl" line 1)Same cause, same shape of error, different command and different usage line. Once you recognise wrong # args next to a line with a trailing #, you will diagnose this instantly for the rest of your career.
6. The Trap That Reaches Out of the Comment
This is the one worth the chapter's length, because a comment can break code that is not on the comment's line.
Here is a block with an ordinary-looking note in it. The if command is Module 8's subject and you are not expected to understand it yet — read it as "a command taking a condition and a body in braces", and keep your attention on the comment:
if {1} {
# this comment has an unmatched { brace
puts "did we get here?"
}Nothing about that comment looks dangerous. It is an English sentence that happens to mention a brace. The result:
missing close-brace: possible unbalanced brace in comment
while executing
"if {1} {
# this comment has an unmatched { brace
puts "did we get here?"
}
"
(file "brace-in-comment.tcl" line 1)The script never ran. A brace inside a comment broke the block that contained it.
The reason follows from §3 once more. To pass a braced body to if, Tcl must first find where that body ends, and it does that by matching braces. That matching happens while Tcl is still working out the extent of the command — before anything inside the body has been looked at as commands or comments. At that moment the # has no meaning yet, so the { inside it counts.
Balance the braces and the problem disappears:
if {1} {
# this comment has a balanced {} pair
puts "reached"
}reachedThe practical habits that avoid it entirely:
- Do not write a lone
{or}in a comment. Write "brace" or "curly brace" in prose instead. - If you must show one, show a balanced pair —
{}— which satisfies the matcher. - Commenting out code is where this bites hardest. Putting
#in front of a line that contains an opening brace leaves that brace counted and its partner commented out. Comment out whole balanced blocks, not fragments of one.
7. What Comments Should Say
A separate matter from the mechanics, and the one that affects whether anyone thanks you.
A comment that restates the code is noise:
# print starting
puts "starting"A comment that records something the code cannot say is worth keeping:
# The tool writes this file only after a successful run, so its absence
# means failure rather than an empty result.
puts "checking for the output file"The code already says what. Spend comments on why — the constraint you were working around, the assumption you are relying on, the reason the obvious approach was not used. Those are the things the next reader cannot recover by looking harder.
This matters more in Tcl than in most languages for a practical reason you will appreciate by Module 2: Tcl's quoting rules mean a line's behaviour can depend on a subtlety that is invisible on inspection. A comment explaining why a particular form was chosen is often the only record of a decision somebody will otherwise undo.
8. Failure Modes, Collected
| What you wrote | What happens | Why |
|---|---|---|
puts "hi" # note | wrong # args for puts | # is not where a command starts, so it became an argument. |
set x 5 # note | wrong # args for set | Same cause; every command behaves this way. |
puts "hi" ;# note | works | The ; ended the command, creating a position where # is a comment. |
puts "a # b" | prints a # b | Inside a word, # is an ordinary character. |
{ # unmatched { } | possible unbalanced brace in comment | Braces are matched before comments are recognised. |
9. Exercises
Exercise 1 — break it and read it. Write puts "hi" # some note in a file and run it. Before reading the error, predict which error it will be. Then fix it with ;# and confirm.
Exercise 2 — generalise it. Do the same with set count 0 # the counter. Confirm you get wrong # args for set rather than for puts, and explain in one sentence why the error names a different usage line.
Exercise 3 — the literal #. Write a script that displays the text item #3 failed exactly. Then write one that displays a single # character and nothing else. Neither needs escaping — if you reach for a backslash, re-read §5.
Exercise 4 — reproduce the brace trap. Copy brace-in-comment.tcl from §6 and run it. Confirm you get the dedicated message. Then fix it two ways: once by balancing the braces, once by rewording the comment to avoid braces entirely. Which fix would you prefer in code a colleague maintains, and why?
Exercise 5 — comment out a block, carefully. Take the working if block from §6 and comment out only the puts line inside it. Does it still run? Now comment out only the if {1} { line and run it. Explain what happened using §6.
Exercise 6 — rewrite a bad comment. Here is a real-shaped comment worth improving:
# set the limit to 3
set retry_limit 3Rewrite the comment so it earns its place. You will have to invent a plausible reason, which is the point — a comment with nothing to say should be deleted rather than written.
10. Summary
- A line whose first non-blank word is
#is a comment. Indentation is fine. #begins a comment only where Tcl expects a command — start of a line, after a;, or at the start of a braced body. Everywhere else it is an ordinary character.- A trailing
#therefore breaks the command, and the error iswrong # argswith that command's usage line. ;#is the fix, and it is not a special token — it is a semicolon ending the command followed by a comment.- Inside quoted text,
#needs no escaping. - Every command behaves this way, not just
puts. - Braces are matched before comments are recognised, so an unmatched
{or}in a comment can break the whole block. Tcl'spossible unbalanced brace in commentmessage exists because this is common. - Never write a lone brace in a comment; write a balanced pair or use words. Comment out whole balanced blocks, not fragments.
- Spend comments on why, not what. The code already says what.
One chapter remains in this module, and it is the one that turns everything so far into a working method: Tcl's errors are not obstacles but instructions, and reading them properly is the difference between fixing a script and guessing at it.
Where this fits
Part of the Tcl curriculum.
