Updated 2018-10-03 08:35:20 by hanthe

{*} makes each item in a list an individual argument of the current command.

{*} was new in Tcl 8.5, and resulted in the Endekalogue becoming the Dodekalogue.

See Also  edit

eval
contains a copy of a post to comp.lang.tcl by Jeff Hobbs explaining the multiple layers of the problem with eval, and how something like {*} solves the problem.
{expand}
the original syntax for this feature, before {*} was settled upon.
Expansion with {*} in Tcl 8.4
BraceStarBrace
How should we call this thing?
Unix Shells
compares {*} to Unix shell syntax
Annotating words for better specifying procs in Tcl9
A discussion on generalizing the {...}word syntax.
Every Word is a Constructor
Describes an alternative approach to expanded words.

Availability  edit

Response  edit

RLH: This is much better IMHO.

jcw: HURRAY!

JMN: Well.. {*} looks to me like a nipple.. which is clearly better than a wart.

Documentation  edit

official reference
TIP 157
describes the rationale for and functionality of {*}, but uses the {expand} notation.
TIP 293
proposes {*} instead of {expand}

Description  edit

{*} promotes the items in a list to individual arguments of the current command. For example,
set {*}[list greeting hello]

is equivalent to
set greeting hello

Prior to the introduction of {*}, eval was used for the same purpose, but was difficult to use correctly. In older versions of Tcl, one would write:
#warning: no longer recommended
eval destroy [winfo children .]
eval button .b $stdargs -text \$mytext -bd $border
eval exec \$prog $opts1 [getMoreopts] \$file1 \$file2

When used for this purpose, eval had the nasty habit of breaking apart values that were intended to be single values. When used correctly, it was verbose and inconvenient:
#warning: no longer recommended
eval exec \$prog [lrange $opts1 0 end] [lrange [getMoreopts] 0 end] \$file1 \$file2     

Or:
#warning: no longer recommended
eval [list exec $prog] [lrange $opts1 0 end] [lrange [getMoreopts] 0 end] [list $file1 $file2]

With the new syntax the examples become:
destroy {*}[winfo children .]
button .b {*}$stdargs -text $mytext -bd $border
exec $prog {*}$opts1 {*}[getMoreopts] $file1 $file2

RS: One might say {*} is the inverse function of list:
{*}[list a b c] <==> a b c

RS: In re_syntax, * stands for "zero or more", which is exactly the unique cardinality of an {*}ed list...

DKF: Similarly in string match syntax.

Shell Equivalents  edit

The bash equivalent of {*} is
${varname[@]:+"${varname[@]}"}

More Examples  edit

% set args [list range foobar 2 4]
range foobar 2 4
% string $args
unknown or ambiguous subcommand "range foobar 2 4"
% string {*}$args
oba

The first time, string gets the entire value of $args as one argument, and fails. The second time, $args is expanded, and string, receiving four separate arguments, works correctly.
% proc p args {puts "Number of arguments: [llength $args]"}
% p {a b c}
Number of arguments: 1
% p {*}{a b c}
Number of arguments: 3

{*} in Multiple Dimensions  edit

HaO: To flatten a matrix, one could use {*}[concat {*}...]. Example to find the max of a matrix:
% set m {{1 2} {3 4}}
% tcl::mathfunc::max {*}[concat {*}$m]
4

The expression, {*}[concat {*}$m] results in 1 2 3 4.

Instead of [concat {*}$m] you can also use join:
max {*}[join $m]

Misc  edit

CN slightly regrets that {expand} is now gone - I was looking forward to possible future enhancements along the same line, like for example
lappend mylist {sort|toupper|regmatch [0-9]+}$somelist

to specify a chain of filters for the expansion. But I suppose that people for whom {expand} is a wart will classify this a as a tumor... ;-)

DKF: It's also trivially replacable using {*}[...] and some commands to preprocess the list.

SYStems or you can just
lappend mylist {*}[lsort [string toupper [regmatch {[0-9]+} $somelist]]]

The moral is that {*} is not a shorthand for other commands, but a new feature, although for this particular example you could have just used
set mylist [concat $mylist [lsort [string toupper [regmatch {[0-9]+} $somelist]]] ]

AMG: Often I need to construct a new list by combining a mixture of lists and elements. This is done very well by combining list with the {*} operator. (Is it right to call it an operator?)
set l1 {a b c}
set e1 d
set l2 {e f g}
set e2 {h i}
set result [list {*}$l1 $e1 {*}$l2 $e2]

This sets $result to {a b c d e f g {h i}}. Equivalent non-{*} code:
set result $l1
lappend result $e1
set result [concat $result $l2]
lappend result $e2

kpv: Here's a better way of doing with concat (it's a question of do you special handle the list pieces or element pieces).
set result [concat $l1 [list $e1] $l2 [list $e2]]

AMG: Thanks. This clearly shows the two methods are duals of each other. Side-by-side:
set result [list  {*}$l1       $e1  {*}$l2       $e2 ]
set result [concat   $l1 [list $e1]    $l2 [list $e2]]

In the first method, {*} is used to mark which elements are to be expanded, and the default is to not expand. In the second method, list is used to mark which elements are not to be expanded, and the default is to expand. The latter approach is similar to the behavior of Unix shells and has led to a lot of unsafe code when the programmer isn't careful about quoting--- apparently this default is surprising to many people. However, it does work; just be cautious!

kpv: I think both ways have an equal need for the programmer to be cautious -- forgetting the {*} is just as bad as forgetting list. To me, with years of pre-8.5 programming experience, especially in using eval, the concat method feels more natural. I'm sure that will change as I do more 8.5+ programming.

AMG et al:

a series of literal arguments to a command can be expressed in {*} form:
set data [dict create {*}{
    key1 val1    key2 val2
    key3 val3    key4 val4
    key5 val5    key6 val6
}]

Of course, in this case the dict create could have been left out, and the internal dict representation have been generated the first $data was used as a dict, but the above is slightly faster and clearer... well, except for the {*} nonsense, of course. :^) Given a bit of time, I'm sure I could come up with a more realistic example, but I actually find myself doing the above in real code (if anything I write can be said to be "real").

This usage of {*} provides escaping without turning the arguments into a single argument, since the the resulting list elements (all of which are literal strings) each become an argument to the command, without any extra interpretation. Since the value following {*} is a well-formed list, standard list formatting can be employed, including backslashes, paired braces, and paired double quotes.