Tcl Part I · Module 6 · Lists
lassign: Unpacking a List Into Variables
This closes the module with a small command that improves a lot of code. Pulling fields out of a record with numbered index calls works, and it leaves a reader counting positions to understand what each value is — and it breaks silently everywhere at once if a field is ever inserted. One line replaces the lot, names every field where a reader can see it, and hands back anything left over. It also has one behaviour worth knowing before you rely on it, which differs from the similar-looking command you met in the strings module.
Foundation10 min readTclPart IListsUnpackingReadability
Part I · Module 6 · Chapter 6.14 · lassign
1. The Command
lassign {alpha beta gamma} first second third
puts "first=$first second=$second third=$third"first=alpha second=beta third=gammaA list, then variable names — no $, as with set, lappend and lset. Each element goes into the corresponding variable.
2. What It Replaces
set row {clk 1 input}
set name [lindex $row 0]
set width [lindex $row 1]
set direction [lindex $row 2]
puts "$name $width $direction"clk 1 inputset row {clk 1 input}
lassign $row name width direction
puts "$name $width $direction"clk 1 inputThree lines become one, and that is the smaller benefit.
The larger one is that the field names now appear once, in order, where a reader can see the record's shape. The lindex version makes you read three lines and match numbers to names.
3. Missing Elements Become Empty
lassign {one two} a b c
puts "a=<$a> b=<$b> c=<$c>"
puts "c exists: [info exists c]"a=<one> b=<two> c=<>
c exists: 1There were two elements and three variables. The third was set to the empty string — and it does exist afterwards.
That is different from scan, which leaves unmatched variables unset:
| Command | A field that was not supplied |
|---|---|
lassign | set to the empty string |
scan | left unset — reading it is an error |
So the defensive check is different too. With scan you check the returned count; with lassign you check the list length before unpacking:
set row {clk 1}
if {[llength $row] != 3} {
puts "expected 3 fields, got [llength $row]"
} else {
lassign $row name width direction
puts "$name $width $direction"
}expected 3 fields, got 2Without that check you get an empty direction and no indication anything was wrong — the same silent-empty-value problem as Chapter 6.4 §3.
4. Extra Elements Are Returned
set rest [lassign {1 2 3 4 5} first second]
puts "first=$first second=$second"
puts "rest=<$rest> ([llength $rest] elements)"first=1 second=2
rest=<3 4 5> (3 elements)lassign returns whatever it did not use.
That gives a clean way to take the head of a list and keep the tail:
set queue {alpha beta gamma delta}
set queue [lassign $queue next]
puts "took: $next"
puts "remaining: $queue ([llength $queue] left)"took: alpha
remaining: beta gamma delta (3 left)One line removes the first element and gives it to you — the list equivalent of popping from a queue. Compare with Chapter 6.7 §3's lreplace approach, which needs two statements.
5. Discarding Fields
There is no placeholder syntax, so the convention is a variable you ignore:
set row {clk 1 input "the main clock"}
lassign $row name - - description
puts "name=$name description=$description"name=clk description=the main clock- is an ordinary variable name here — it genuinely gets set, and nothing stops you reading it. It simply reads as "not interested", and _ is equally common.
6. Where You Will Use It
Walking a table — the shape from Chapter 6.11 §4:
set table {{clk 1 input} {reset 1 input} {payload 64 output}}
foreach row $table {
lassign $row name width direction
puts [format " %-10s %4d %s" $name $width $direction]
} clk 1 input
reset 1 input
payload 64 outputAfter a split — the registry's own example, joining this module to Chapter 5.13:
set line "widget,42,3.5"
lassign [split $line ","] name count value
puts "name=$name count=$count value=$value"name=widget count=42 value=3.5That one line is the whole point of the chapter: text came in, became a list, and its fields now have names.
7. Common Mistakes
Writing lassign $row $name. The targets are names, not values.
Assuming a missing field is unset. It is an empty string. §3.
Forgetting to check the length first. §3.
Confusing it with scan. Different missing-field behaviour and different checks. §3.
Ignoring the return value when you wanted the tail. §4.
Using it on a record with many fields. Eight names on one line is as unreadable as eight lindex calls. That is the signal for a dictionary — Chapter 6.11 §7.
8. Exercises
Exercise 1 — unpack three. Unpack {red green blue} into three named variables and print them.
Exercise 2 — before and after. Write a record unpack with three lindex calls, then with lassign. Then insert a new field at the front of the record and say how many lines each version needs changing.
Exercise 3 — too few. Unpack a two-element list into three variables. Print all three in angle brackets and check whether the third exists. Then add the guard that catches it.
Exercise 4 — the leftovers. Take the first two elements of a six-element list and print how many remain.
Exercise 5 — pop. Write the one-line "take the first element and keep the rest" idiom, and run it twice on the same list.
Exercise 6 — after a split. Parse "clk,1,input" into three named variables in two lines total.
9. What You Can Now Do
That closes Module 6. Across fourteen chapters you have Tcl's central data structure:
- what a list actually is, and why not every string is one
- building one correctly, and the corruption that follows from not doing so
- counting, indexing, slicing
- growing, inserting, replacing, deleting, and updating in place
- searching by value, pattern or column — and the two traps in doing so
- sorting by any key, and the default that is wrong for numbers
- nesting lists into tables, and when to stop
- combining versus nesting
- expanding a list into arguments
- and unpacking one into named variables
The thread running through it is the one from Chapter 6.1: a list is a string read by list rules. Everything else follows — the automatic quoting, the free composition with text commands, and the ways a careless text operation can change an element count.
Module 7 is Dictionaries and Arrays, which Chapter 6.11 §7 has already made the case for: once records have several fields, or fields that may change, naming them beats numbering them.
10. Remember
lassign list name ...unpacks elements into variables. Names, not values.- It replaces a chain of numbered
lindexcalls and names the record's fields in one visible place. - Numbered access encodes the layout everywhere you use it — that is the real argument, not line count.
- Missing elements become the empty string, and the variable does exist.
- That differs from
scan, which leaves them unset — so checkllengthbefore unpacking, notinfo existsafter. - Extra elements are returned, which gives the one-line "pop the head" idiom.
- Use
-or_for fields you do not want. There is no real placeholder. - Many fields means you want a dictionary — Module 7.
Where this fits
Part of the Tcl curriculum.
