Neatly listing values in multiple lines

tcl
Dor picture Dor · Mar 25, 2014 · Viewed 11.6k times · Source

How does one list values in multiple lines without a backslash at the end of each line?

One can't create a list in multiple lines without having a backslash at the end.
For example, the following (wrong) code:

set pets [list 
    cat
    dog
    elephant
]

gives an error:

invalid command name "cat"
    while executing
"cat"
    invoked from within
"set pets [list
        cat
        dog
        elephant
]"

It can be fixed by appending a backslash at the end of the line:

set pets [list \
    cat \
    dog \
    elephant \
]

Which is ugly and prone to errors.

Please note that:

  • I'm aware of using the curly braces ({ & }), but it doesn't allows executing commands and also keeps redundant whitespace characters.
  • Any other command may be used (e.g. dict create), not only list as in my example.

Using Tcl 8.5

Answer

Donal Fellows picture Donal Fellows · Mar 25, 2014

Tcl uses newline (and semicolon) as a command separator. This is a core part of the basic syntax; you can't work around it so you must use either double quotes or braces to avoid backslash-itis. Let's look at the possibilities (remember, list separators can be any non-empty whitespace sequence).

Ugly, error prone list with backslashes

set pets [list \
    cat \
    dog \
    $elephant \
]

With braces, no substitutions

set pets {
    cat
    dog
    $elephant
}

(Note that in above, $elephant is just a sequence of characters, not a variable read.)

With double quotes, substitutions but be careful!

set pets "
    cat
    dog
    $elephant
"

By “be careful!” I mean that where you have a multi-word member of the list, you need an inner [list …] or other quoting:

set pets "
    cat
    dog
    [list $elephant]
    [list "elephant's child"]
"

But this would be true with the list+backslashes at the top.

Using subst

set pets [subst {
    cat
    dog
    $elephant
    "elephant's child"
}]

I might “clean that up” (and avoid other potential problems) with:

set pets [list {*}[subst {
    cat
    dog
    [list $elephant]
    [list "elephant's child"]
}]]

But frankly, if things are getting really complex then I actually do this:

Construct with several commands

set pets {
    cat
    dog
}
lappend pets $elephant "elephant's child"

No point in bashing yourself over the head to use one command when two or more will do everything with fewer problems.