Skip to content
VLSI Mentor

Tcl Part I · Module 6 · Lists

{*} — Expanding a List Into Arguments

You have seen this a dozen times in the last few chapters, usually in a format call, and it is time it got its own treatment. It solves a problem that comes up constantly once you work with lists: you have the arguments for a command sitting in a list, and passing that list gives the command one argument rather than several. This is also the one piece of Tcl syntax that breaks the rule Module 2 spent a chapter establishing, which is worth understanding rather than memorising.

Intermediate11 min readTclPart IListsExecution ModelArguments

Part I · Module 6 · Chapter 6.13 · {*} — Expanding a List Into Arguments

1. The Problem

Azvya Education Pvt. Ltd.VLSI Mentor
problem.tcl
proc show {args} {
    puts "  received [llength $args] argument(s): $args"
}

set pair {alpha beta}

puts "without:"
show $pair
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
without:
  received 1 argument(s): {alpha beta}

One argument, which happens to be a two-element list.

That is correct and it is Chapter 2.2 §4: a substituted value never changes how many words a command has. The source said show $pair — two words — so the command gets one argument.

But sometimes you have the arguments in a list and want them to be the arguments.

2. The Operator

Azvya Education Pvt. Ltd.VLSI Mentor
solution.tcl
proc show {args} {
    puts "  received [llength $args] argument(s): $args"
}

set pair {alpha beta}

puts "with:"
show {*}$pair
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
with:
  received 2 argument(s): alpha beta

Two arguments. {*} tells Tcl to take the value and use its elements as separate words.

Note there is no space: {*}$pair, not {*} $pair.

3. It Is Syntax, Not a Command

{*} is not a list command. It is a piece of parser syntax, handled when Tcl is working out the words of a command — Chapter 2.2.

That matters because it means it works anywhere a command takes arguments, not only with list-related commands:

Azvya Education Pvt. Ltd.VLSI Mentor
anywhere.tcl
set spec {%-10s %4d}
set row {clk 1}

puts [format "[join $spec " "]" {*}$row]

set opts {-nocase "error*"}
puts [string match {*}$opts "ERROR: failed"]
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
clk           1
1

In the second, {*}$opts supplied both the -nocase flag and the pattern from a list — so which options a command receives can be decided at run time.

4. The Rule It Breaks

Chapter 2.2 §4 stated the rule:

What a substitution returns becomes part of a word. It never changes how many words there are.

And §8 of that chapter named the exception. This is it:

Azvya Education Pvt. Ltd.VLSI Mentor
the-exception.tcl
proc show {args} { puts "[llength $args] argument(s)" }

set three "a b c"

show $three
show {*}$three
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
1 argument(s)
3 argument(s)

The rule holds everywhere except here, and here you have to ask for it explicitly. That is the design: word boundaries are fixed by the source text, and the one way to change them is to write something that visibly says so.

5. It Replaces eval

Chapter 2.11 §4 made this argument; here is the evidence.

Suppose a command is built as a list:

Azvya Education Pvt. Ltd.VLSI Mentor
as-a-list.tcl
set cmd [list puts "hello world"]

puts "the list: $cmd"
puts "elements: [llength $cmd]"
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
the list: puts {hello world}
elements: 2

Running it directly does not work, because the whole value is one word and therefore one command name:

Azvya Education Pvt. Ltd.VLSI Mentor
directly.tcl
set cmd [list puts "hello world"]

puts [catch {$cmd} message]
puts $message
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
1
invalid command name "puts {hello world}"

With {*}, the elements become the command and its argument:

Azvya Education Pvt. Ltd.VLSI Mentor
with-expansion.tcl
set cmd [list puts "hello world"]

{*}$cmd
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
hello world

6. Where You Will Use It

Spreading a row into a formatted line — seen throughout Module 6:

Azvya Education Pvt. Ltd.VLSI Mentor
format-row.tcl
set table {{clk 1 input} {payload 64 output}}

foreach row $table {
    puts [format "  %-10s %4d  %s" {*}$row]
}
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  clk           1  input
  payload      64  output

Merging a list into another — Chapter 6.12 §4:

Azvya Education Pvt. Ltd.VLSI Mentor
merge.tcl
set a {1 2}
set b {3 4}

lappend a {*}$b
puts "$a -> [llength $a] elements"
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
1 2 3 4 -> 4 elements

Flattening a table — Chapter 6.11 §8:

Azvya Education Pvt. Ltd.VLSI Mentor
flatten.tcl
set table {{clk 1} {reset 1}}
puts [concat {*}$table]
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
clk 1 reset 1

Passing options decided at run time:

Azvya Education Pvt. Ltd.VLSI Mentor
runtime-options.tcl
set sort_opts {-integer -decreasing}
set data {10 9 100}

puts [lsort {*}$sort_opts $data]
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
100 10 9

That last one is genuinely useful — a script can decide how to sort based on a configuration value, without an if for every combination.

7. Version Note

{*} requires Tcl 8.5 or later. Since the baseline for this track is 8.6 — Chapter 1.4 §4 — it is always available to you.

If you meet very old code using eval where {*} would do, that is usually why.

8. Common Mistakes

Writing {*} $value with a space. It must be attached: {*}$value.

Using it where you wanted one argument. lappend l {*}$other merges; lappend l $other adds one element. Both are correct for different intents.

Expecting it to parse text. It expands a list into words. It does not evaluate anything.

Reaching for eval instead. §5.

Expanding something that is not a well-formed list. The same error as Chapter 6.1 §5 — unbalanced braces cannot be expanded.

9. Exercises

Exercise 1 — one versus several. Using the show procedure from §1, pass a three-element list with and without {*}. Print both counts.

Exercise 2 — format a row. Build a two-row table and print each row with one format call using {*}. Then try it without and read the error.

Exercise 3 — merge. Merge two lists into one four-element list using lappend and {*}. Then show the three-element version without it.

Exercise 4 — the command list. Build [list puts "a message"], show that running it directly fails, and then run it with {*}.

Exercise 5 — runtime options. Write a script with a variable holding sort options, and sort the same data twice by changing only that variable.

Exercise 6 — eval versus expansion. Put the value {has $dollar} into a list and pass it to puts with {*}. Confirm the dollar sign is printed literally. Then say what eval would have done differently.

10. Remember

  • {*}$list passes the list's elements as separate arguments. No space after {*}.
  • It is parser syntax, not a command, so it works with any command.
  • It is the one exception to the rule that substitution never changes word boundaries — and you have to ask for it.
  • It replaces eval for building commands. eval re-parses the text; {*} does not evaluate anything.
  • Build a command as a list and expand it.
  • Common uses: spreading a row into format, merging with lappend, flattening with concat, and run-time options.
  • It needs Tcl 8.5 or later, so it is always available on this track's baseline.

One chapter remains, and it makes the format examples above even cleaner.

Where this fits

Part of the Tcl curriculum.