Version 38 of {*}

Updated 2012-06-05 18:18:44 by escargo

The artist formerly known as {expand}. See TIP 293 [L1 ] for details.


LV This sequence of 3 characters is a bit of new syntax in Tcl 8.5 to allow safe argument expansion. (This resulted in the Endekalogue becoming the Dodekalogue.) This new syntax is intended as an alternative to using eval to change a string into individual elements.


Rather than chase around to all these other pages, let's document here just what the new syntax in Tcl 8.5 does.

In older versions of Tcl, one would write:

 eval destroy [winfo children .]
 eval button .b $stdargs -text \$mytext -bd $border
 eval exec \$prog $opts1 [getMoreopts] \$file1 \$file2

The eval version would be even more complex if the lists that are to be expanded are not known to be pure. To be really safe the last would be:

 eval exec \$prog [lrange $opts1 0 end] [lrange [getMoreopts] 0 end] \$file1 \$file2     

Or:

 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

RLH This is much better IMHO.

jcw - HURRAY!

RS Note that in re_syntax, * stands for "zero or more", which is exactly the unique cardinality of an {expand}ed item...

DKF: Similarly in string match syntax.


SYStems I vote yes. I don't think though that its necessary to deprecate {expand}, but I agree on deprecating {} it does look like syntax error rather than a feature.

RLH I think {expand} should be removed completely. It is not in the language "official" yet and so it should be yanked to make sure no abuse occurs.

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


CN slightly regrets that {expand} is now gone - I was looking forward for 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]]]

moral is {*} is not a shorthand for other commands, its 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 the [list] command and 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 the [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: When I have a long command but don't feel like using \ at the end of every line, I sometimes do the following:

 set data [dict create {*}{
     key1 val1    key2 val2
     key3 val3    key4 val4
     key5 val5    key6 val6
 }]

Of course, in this case I could have just left out the [dict create] and instead relied on the internal representation being generated the first time I tried accessing $data 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 quoting without grouping. Dollar signs and square brackets become ordinary characters, sequences of unquoted whitespace and newlines are delimiters, and the resulting list elements (all of which are pure strings) are placed one-after-the-other into the command's objv list without any extra interpretation. Backslashes, paired braces, and paired double quotes still function as quoting/grouping characters. ("Grouping" means to disable the special meaning of characters that would otherwise be treated as delimiters.)


For the impatient, see Expansion with {*} in Tcl 8.4


Was a name ever settled upon for this operator? [L2 ]

RLH It is the expand operator. Kind of like Prince when he renamed himself to a symbol.


BF - 2011-12-16 10:06:19

Can someone write a really simple example of what this operator does. I don't really get most of what is going on on this page.

MG Sure:

(Mike) 1 % set args [list range foobar 2 4]
range foobar 2 4
(Mike) 2 % string $args
unknown or ambiguous subcommand "range foobar 2 4"
(Mike) 3 % 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 the string command receives 4 separate arguments, and works correctly.

APN Or another illustration:

(wits) 49 % proc p args {puts "Number of arguments: [llength $args]"}
(wits) 50 % p {a b c}
Number of arguments: 1
(wits) 51 % p {*}{a b c}
Number of arguments: 3


  Double {*}

HaO If one wants 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.