Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

TIP #103: Argument Expansion Command

4 views
Skip to first unread message

Peter Spjuth

unread,
Jun 20, 2002, 5:26:57 PM6/20/02
to

TIP #103: ARGUMENT EXPANSION COMMAND
======================================
Version: $Revision: 1.1 $
Author: Peter Spjuth <peter.spjuth_at_space.se>
State: Draft
Type: Project
Tcl-Version: 8.5
Vote: Pending
Created: Saturday, 15 June 2002
URL: http://purl.org/tcl/tip/103.html
WebEdit: http://purl.org/tcl/tip/edit/103
Post-History:

-------------------------------------------------------------------------

ABSTRACT
==========

This TIP proposes to add a command that can perform argument expansion
in a safe and efficient manner.

INTRODUCTION
==============

Many commands take a variable number of arguments and often you find
yourself with those arguments in a list. This list must then be
expanded into individual arguments to the command. This is currently
done with eval:

eval destroy [winfo children .]

This is a bit obscure and also very error prone when the command
becomes more complex. It is also inefficient and not object safe, why a
command specialised in doing this would be better.

RATIONALE
===========

There have been suggestions of introducing some new syntax to Tcl to
handle argument expansion. That is a big and controversial step, and
not anything this TIP wants to meddle in. A command can improve every
point where eval has shortcomings and thus give a good result with less
means.

Such a command can be done in several ways and below the choice in this
TIP's specification is defended.

As examples three statements are used which will be repeated for
different alternatives. This is the eval version:

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

With the proposed command they become:

expand { destroy @[winfo children .] }
expand { button .b @$stdargs -text $mytext -bd $border }
expand { exec $prog @$opts1 @[getMoreopts] $file1 $file2 }

An alternative to having a local syntax is to point at the arguments
that should be expanded, either by index:

expand {end} destroy [winfo children .]
expand {2} button .b $stdargs -text $mytext -bd $border
expand {2 3} exec $prog $opts1 [getMoreopts] $file1 $file2

Or by some flag mechanism:

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

Those lack in writability/readability/maintainability in a disturbing
manner.

For the choice of local syntax the first rule is that it should not
violate Tcl's rules, which simplifies implementation since Tcl's parser
can do the job.

Any char that fulfils that could be used but the choice fell on @ since
that char is odd enough to be visible which helps readability.

An alternative syntax could be:

expand { destroy <[winfo children .]> }
expand { button .b <$stdargs> -text $mytext -bd $border }
expand { exec $prog <$opts1> <[getMoreopts]> $file1 $file2 }

Using enclosing symbols suggests that they may affect grouping, which
they would not if Tcl's parser shall be used. Thus a single char is
less likely to cause confusion.

SPECIFICATION
===============

A new command "expand" is added. It takes one argument, which contains
a Tcl statement. Only one statement is permitted.

The statement is processed in the following manner:

1. Parse into words according to Tcl's standard rules.

2. Any word starting with @ is remembered and the @ is removed.

3. Perform Tcl's normal execution steps on the new line up to the
point where the command should have been called.

4. Expand the arguments that should be expanded.

5. Execute the command.

_Note 1:_ A word should really start with @ to trigger expansion which
means that words like these are not expanded:

cmd "@$temp" {@home} \@[something]

_Note 2:_ Even though it makes most sense to use @ with words like:

cmd @$var @[somecmd $arg]

the rules allow usages like this:

cmd @$var$apa @[foo]xy[apa]

REFERENCE IMPLEMENTATION
==========================

A reference implementation exists and will be uploaded to SF shortly.

COPYRIGHT
===========

This document has been placed in the public domain.

-------------------------------------------------------------------------

TIP AutoGenerator - written by Donal K. Fellows

[[Send Tcl/Tk announcements to tcl-an...@mitchell.org
Announcements archived at http://groups.yahoo.com/group/tcl_announce/
Send administrivia to tcl-announ...@mitchell.org
Tcl/Tk at http://tcl.tk/ ]]

Donald Arseneau

unread,
Jun 21, 2002, 11:06:02 PM6/21/02
to
Peter Spjuth <peter....@space.se> writes:

> TIP #103: ARGUMENT EXPANSION COMMAND

> expand { exec $prog @$opts1 @[getMoreopts] $file1 $file2 }

> expand {2 3} exec $prog $opts1 [getMoreopts] $file1 $file2

> expand exec - $prog + $opts1 + [getMoreopts] - $file1 - $file2

I hate syntax that doesn't fit the rest of the language!

Are there problems with the following format:

expand exec {$prog} $opts1 [getMoreopts] {$file1 $file2}

where braces prevent expansion (but are stripped off)?

Donald Arseneau as...@triumf.ca

Joe English

unread,
Jun 22, 2002, 1:29:45 PM6/22/02
to
Donald Arseneau wrote:

>Peter Spjuth writes:
>
>> TIP #103: ARGUMENT EXPANSION COMMAND
>> expand { exec $prog @$opts1 @[getMoreopts] $file1 $file2 }
>> expand {2 3} exec $prog $opts1 [getMoreopts] $file1 $file2
>> expand exec - $prog + $opts1 + [getMoreopts] - $file1 - $file2
>
>I hate syntax that doesn't fit the rest of the language!
>
>Are there problems with the following format:
>
>expand exec {$prog} $opts1 [getMoreopts] {$file1 $file2}
>
>where braces prevent expansion (but are stripped off)?

It's not clear to me what you intend here. (Braces already
prevent expansion and are stripped off while processing the
arguments to [expand]; it's not clear what [expand] should
do with its arguments).

Let's assume for the sake of argument that $opts1 contains
"-opt1 -opt2 -opt3", and [getMoreopts] returns "". Then in the above,
[expand] will be called with 5 arguments:

1: <exec>
2: <$prog>
3: <-opt1 -opt2 -opt3>
4: <>
5: <$file1 $file2>

Should [expand] (a) split each argument into words, substitute
each word individually, and concatenate the results,
(b) substitute each argument, split the result into elements,
and concatenate the results, or (c) something else?

What kind of substitution should be performed? [subst]-style,
[eval]-style, or something else?

For example, suppose further that $prog contains "ls -l -A",
$file1 contains "File One", and $file2 contains "File Two".
Should [exec] be called with 6 arguments:

# option (a):
1. <ls -l -A>
2. <-opt1>
3. <-opt2>
4. <-opt3>
5. <File One>
6. <File Two>

with 10:

# option (b):
1. <ls>
2. <-l>
3. <-A>
4. <-opt1>
5. <-opt2>
6. <-opt3>
7. <File>
8. <One>
9. <File>
10. <Two>

or something else?

Choices (a) and (b) both have problems. Consider the
case where $opts is a list you want to interpolate, whose
elements may contain $s; and suppose $file is an argument
that you don't want expanded whose name contains spaces.

With option (a) (split first, then substitute), you need to say:

expand $command $opts {$file}

but the second round of substitution will process the $s in $opts.
With option (b) (substitute first, then split), you would say:

expand $command {$opts} {$file}

but then you need extra quoting around {$file} (what kind
of quoting depends on whether [subst]-style or [eval]-style
substitution is used) because of the embedded spaces.

My current preferred solution for this is [invoke]:

SYNOPSIS
invoke args ...

DESCRIPTION
_invoke_ interprets each of its arguments as a list,
concatenates the arguments to form a single list in
the style of [lconcat] [*], and evaluates the result
as a command.

This is basically the same as [eval], except that it concatenates
lists to form a command insted of concatenating strings to form
a script.

This doesn't do a second round of substitution, so it avoids
most quoting hell issues. It does require more calls to [list]
than the proposed [expand] command would (which I guess is a
kind of quoting hell, but IMO not as confusing as the kind caused
by double-substitution...)


[*] from TclX, or as follows:

proc lconcat {args} {
set result [list]
foreach arg $args {
foreach element $arg {
lappend result $element
}
}
return $result
}


--Joe English

jeng...@flightlab.com

ulis

unread,
Jun 23, 2002, 5:55:23 AM6/23/02
to
> >> expand { exec $prog @$opts1 @[getMoreopts] $file1 $file2 }

1/
Today when I need:


expand { exec $prog @$opts1 @[getMoreopts] $file1 $file2 }

I do
eval [format \
{ exec $prog %s %s $file1 $file2 } $opts1 [getMoreopts]]
that is (for me) obscure.

2/
I don't like the 'expand' keyword because it means: expand then exec.

3/
I would like a way to use the parser to check syntax without the
expansion of $var or [eval].
(yes, I know this restricts the useable syntax but would help for
generated code)

ulis

Joe English

unread,
Jun 23, 2002, 11:36:08 AM6/23/02
to
ulis wrote:
>
>Today when I need:
> expand { exec $prog @$opts1 @[getMoreopts] $file1 $file2 }
>I do
> eval [format \
> { exec $prog %s %s $file1 $file2 } $opts1 [getMoreopts]]
>that is (for me) obscure.

Also beware that it's not safe! If $file1 or $file2 contain
whitespace, $, [, {, or other Tcl syntax characters, this
will break.


--Joe English

jeng...@flightlab.com

Harald Kirsch

unread,
Jun 23, 2002, 2:14:17 PM6/23/02
to
Peter Spjuth <peter....@space.se> wrote in message news:<pgpmoose.2002...@despot.non.net>...
[snip]
> As examples three statements are used which will be repeated for
> different alternatives. This is the eval version:
>
> eval destroy [winfo children .]
> eval button .b $stdargs -text \$mytext -bd $border
> eval exec \$prog $opts1 [getMoreopts] \$file1 \$file2
>
> With the proposed command they become:
>
> expand { destroy @[winfo children .] }
> expand { button .b @$stdargs -text $mytext -bd $border }
> expand { exec $prog @$opts1 @[getMoreopts] $file1 $file2 }
[snip]
> Any char that fulfils that could be used but the choice fell on @ since
> that char is odd enough to be visible which helps readability.

You are a bit late in assigning a special meaning to words starting with '@'.
Bras (bras.berlios.de) does this already. While the two uses not necessarily
clash, you might want to consider that.

Apart from that, I don't really see the point why your three expand lines
are so much better than the three eval lines. With eval you mark (escape)
things which should not be expanded and with expand you mark things which
should be expanded. This looks pretty symmetrical to me. Consequently,
judging from the examples given, expand is redundant and introduction of it
would make Tcl more Perl-like where everthing can be done in a million
different ways.

Harald Kirsch

ulis

unread,
Jun 24, 2002, 5:40:43 AM6/24/02
to
> Also beware that it's not safe! If $file1 or $file2 contain
> whitespace, $, [, {, or other Tcl syntax characters, this
> will break.

You are true! So I have to use other tricks to avoid Tcl interpreting
its syntax chars before needed.

I found Tcl not so easy to use for generating scripts than I thought
in the first beginning.

ulis

Donal K. Fellows

unread,
Jun 24, 2002, 9:11:41 AM6/24/02
to

However, the following will work:
eval [format {exec $prog %s %s $file1 $file2} \
[list $opts1] [list [getMoreopts]]]

It won't win prizes in most beauty contests though.

Donal (a supporter of {expand}$substitution...)
--
Donal K. Fellows http://www.cs.man.ac.uk/~fellowsd/ fell...@cs.man.ac.uk
"If RedHat are so purblind that they think computing is about ever fancier
'desktop themes', they are in the interior design business, and as everyone
knows, if you can piss you can paint." -- Steve Blinkhorn

Joe English

unread,
Jun 24, 2002, 10:07:13 AM6/24/02
to
>ulis wrote:
>>
>>Today when I need:
>> expand { exec $prog @$opts1 @[getMoreopts] $file1 $file2 }
>>I do
>> eval [format \
>> { exec $prog %s %s $file1 $file2 } $opts1 [getMoreopts]]
>>that is (for me) obscure.

Joe English responded, incorrectly:


>
>Also beware that it's not safe! If $file1 or $file2 contain
>whitespace, $, [, {, or other Tcl syntax characters, this
>will break.

This is of course wrong. Funny characters in $file1 and
$file2 won't cause any problems, it's funny characters
in $opts1 and the result of [getMoreopts] that can mess
things up. If $opts1 and [getMoreopts] are guaranteed
to be canonical-form lists, then the above is safe.

I think.


--Joe English

jeng...@flightlab.com

Donald Arseneau

unread,
Jun 24, 2002, 6:55:53 PM6/24/02
to
jeng...@flightlab.com (Joe English) writes:

> Donald Arseneau wrote:
> >Peter Spjuth writes:
> >
> >> TIP #103: ARGUMENT EXPANSION COMMAND
> >> expand { exec $prog @$opts1 @[getMoreopts] $file1 $file2 }
> >> expand {2 3} exec $prog $opts1 [getMoreopts] $file1 $file2
> >> expand exec - $prog + $opts1 + [getMoreopts] - $file1 - $file2
> >
> >I hate syntax that doesn't fit the rest of the language!
> >
> >Are there problems with the following format:
> >
> >expand exec {$prog} $opts1 [getMoreopts] {$file1 $file2}
> >
> >where braces prevent expansion (but are stripped off)?
>
> It's not clear to me what you intend here. (Braces already
> prevent expansion and are stripped off while processing the
> arguments to [expand]; it's not clear what [expand] should
> do with its arguments).

That was really what I was getting at. What is the advantage of
specially-tagging arguments to be expanded as opposed to tagging
(with braces) arguments to not be expanded? I couldn't see the
advantage of the proposed "expand" over existing Tcl. And the
penalty of introducing special syntax is severe IMO.

Donald Arseneau as...@triumf.ca

Joe English

unread,
Jun 24, 2002, 7:49:41 PM6/24/02
to
Donald Arseneau wrote:

>Joe English writes:
>> Donald Arseneau wrote:
>> >Peter Spjuth writes:
>> >> TIP #103: ARGUMENT EXPANSION COMMAND
>> >I hate syntax that doesn't fit the rest of the language!
>> >Are there problems with the following format:
>> >expand exec {$prog} $opts1 [getMoreopts] {$file1 $file2}
>> >where braces prevent expansion (but are stripped off)?
>>
>> It's not clear to me what you intend here. (Braces already
>> prevent expansion and are stripped off while processing the
>> arguments to [expand]; it's not clear what [expand] should
>> do with its arguments).
>
>That was really what I was getting at. What is the advantage of
>specially-tagging arguments to be expanded as opposed to tagging
>(with braces) arguments to not be expanded?

I see. So you basically meant:

eval exec {$prog} $opts1 [getMoreopts] {$file1 $file2}

(instead of "expand exec ..."), which is already in Tcl, right?

> I couldn't see the
>advantage of the proposed "expand" over existing Tcl. And the
>penalty of introducing special syntax is severe IMO.

TIP 103 does address a real problem though. In the
all-too-frequent situation where you have a command and
a list of arguments to pass to the command, Tcl still
does not provide a way to invoke the command that
is safe, efficient, and perspicuous.

"eval $cmd $args" is perspicuous, but it's only safe if
$args is known to be in canonical form, and it's inefficient
due to shimmering and a round-trip through the parser.

"eval [linsert $args 0 $cmd]" is safe, and thanks to
the pure list optimization fast-path in [eval] is
now also efficient, but it's hardly perspicuous. Also, this
idiom only works for single-word commands; multi-word
command prefixes require a different (and longer) idiom.

For what it's worth, I'm not hugely enamored of a new
syntax either, but the notion is certainly popular
and TIP 103's proposed realization of the syntax is
the one with the least impact on the core. I prefer
[invoke], but that idea isn't nearly as popular.

--Joe English

jeng...@flightlab.com

Lowclouds

unread,
Jun 25, 2002, 2:22:14 AM6/25/02
to
While hesitant to stick my nose in on TIP discussions, in general, Harald's
comment :

..."judging from the examples given, expand is redundant and introduction of


it
would make Tcl more Perl-like where everthing can be done in a million
different ways."

goes right to the core of the matter for me. I'm currently being forced to
relearn enough Perl to get through a project and the syntax is making my
head explode. When your core language is C or C++ and you need a scripting
language just to get some hack done, the last thing you want is unnecessary
syntax. I'd rather see this discussion well documented and indexed and
stuffed into the eval examples in the help docs and/or in the wiki. I say
this noting that the worst quoting hell i've ever gone through has been with
eval. subst can help out in some cases.

ok, i'll shut up now.

Craig Denson

Peter Spjuth

unread,
Jun 25, 2002, 6:36:59 AM6/25/02
to
ulis wrote:
> 2/
> I don't like the 'expand' keyword because it means: expand then exec.

I assume you mean eval, not exec. (So we don't confuse it with Tcl's
exec)
I guess a more expressive name could be used but somehow users
can grasp that commands like catch/uplevel/if evaluates things
without calling them eval_and_catch/eval_in_another_scope/eval_if_true.
I prefer it simple.


> 3/
> I would like a way to use the parser to check syntax without the
> expansion of $var or [eval].
> (yes, I know this restricts the useable syntax but would help for
> generated code)

You want Tcl's parser exposed to the script level? I want that too
but that is not within the scope of this TIP.

/Peter

Peter Spjuth

unread,
Jun 25, 2002, 6:38:30 AM6/25/02
to
"Donal K. Fellows" wrote:
>
> However, the following will work:
> eval [format {exec $prog %s %s $file1 $file2} \
> [list $opts1] [list [getMoreopts]]]
>
> It won't win prizes in most beauty contests though.

I think you have just prevented the argument expansion from
taking place, thus rendering the statement rather useless :-)

If the things to expand are lists (and they should be, otherwise
the semantics of expansion gets a bit fuzzy), but not trusted, you need:
eval [format {exec $prog %s %s $file1 $file2} \
[lrange $opts1 0 end] [lrange [getMoreopts] 0 end]]

I think your beats mine in a beauty contest ;-)

> Donal (a supporter of {expand}$substitution...)

/Peter, who wouldn't mind {expand}$substitution either

Peter Spjuth

unread,
Jun 25, 2002, 6:40:06 AM6/25/02
to
Joe English wrote:
>
> >ulis wrote:
> >>
> >>Today when I need:
> >> expand { exec $prog @$opts1 @[getMoreopts] $file1 $file2 }
> >>I do
> >> eval [format \
> >> { exec $prog %s %s $file1 $file2 } $opts1 [getMoreopts]]
> >>that is (for me) obscure.
>
> Joe English responded, incorrectly:
> >
> >Also beware that it's not safe! If $file1 or $file2 contain
> >whitespace, $, [, {, or other Tcl syntax characters, this
> >will break.
>
> This is of course wrong. Funny characters in $file1 and
> $file2 won't cause any problems, it's funny characters
> in $opts1 and the result of [getMoreopts] that can mess
> things up.

True.

> If $opts1 and [getMoreopts] are guaranteed
> to be canonical-form lists, then the above is safe.
>
> I think.

A very good example of the wonders of argument expansion with eval.
You have to know about and deal with things like canonical-form
lists to be safe, and in the end you still go, "It's safe. I think."

/Peter

Peter Spjuth

unread,
Jun 25, 2002, 6:43:09 AM6/25/02
to
Harald Kirsch wrote:
>
> You are a bit late in assigning a special meaning to words starting with '@'.
> Bras (bras.berlios.de) does this already. While the two uses not necessarily
> clash, you might want to consider that.

The choise of character was rather arbitrary and thus not an
important point, so it might change.

> Apart from that, I don't really see the point why your three expand lines
> are so much better than the three eval lines. With eval you mark (escape)
> things which should not be expanded and with expand you mark things which
> should be expanded. This looks pretty symmetrical to me.

Generally I prefer to express what I want to do, not what I don't want
to do. IMO a big part of quoting hell is the dealing with things you
don't want to do since it's so easy to forget something.
A reasonably quoted eval like in the examples is very symmetrical to
expand, but even those are still not completely protected and may bite
you if unlucky.
Using eval is tricky (very tricky if you want to be safe), inefficient
and not object safe (unless using even more obscure tricks), so expand
has some advantages.


> Consequently,
> judging from the examples given, expand is redundant and introduction of it
> would make Tcl more Perl-like where everthing can be done in a million
> different ways.

Most new commands added are redundant since almost everything can
already
be done in Tcl. If they can be justified by being simpler and/or more
efficent than existing solutions (like the wonderful new 'lset' in 8.4),
I think they have a place in the language.

/Peter

Donal K. Fellows

unread,
Jun 25, 2002, 9:29:18 AM6/25/02
to
Peter Spjuth wrote:
> I think you have just prevented the argument expansion from
> taking place, thus rendering the statement rather useless :-)

Shhh!... I was hoping people wouldn't notice that mistake.

Donal.

"[E]ven now, wars are fought and lost, people are killed and unkilled, toilet
rolls are used and unused, pants are derwear and underwear, all because
of the delicious velvety substance that is Marmite." -- Nathan Weston

lvi...@yahoo.com

unread,
Jun 26, 2002, 11:34:38 AM6/26/02
to

According to Peter Spjuth <peter....@space.se>:
: If they can be justified by being simpler and/or more

:efficent than existing solutions (like the wonderful new 'lset' in 8.4),
:I think they have a place in the language.

If the result were that things were simpler in this case, I would agree.
I don't, however, agree that the examples shown are any simpler to write
or read.

Is this merely a convenience - that more complex examples would clearly
show the superiority of the new construct, or is it that the new
syntax provides only minimal improvements?

--
Support Internet Radio <URL: http://saveinternetradio.org/ >
Join Tcl'2002 in Vancouver http://www.tcl.tk/community/tcl2002/
Even if explicitly stated to the contrary, nothing in this posting
should be construed as representing my employer's opinions.

Peter Spjuth

unread,
Jun 26, 2002, 2:10:48 PM6/26/02
to
lvi...@yahoo.com wrote:
>
> If the result were that things were simpler in this case, I would agree.
> I don't, however, agree that the examples shown are any simpler to write
> or read.

I think efficiency and object safety are rather important
parts of expand's features too, but let's focus on simplicity.

Yes, an eval is not that hard to write or read, when you know how.
To compare it to another, simpler, idiom, I find these two about equally
simple to read and write since I know the trick:
foreach {a b} $list break
lassign $list a b
But I wouldn't expect a beginner trying to read man pages to agree.

> Is this merely a convenience - that more complex examples would clearly
> show the superiority of the new construct, or is it that the new
> syntax provides only minimal improvements?

The examples are rather typical in complexity but can be complicated
a bit more.

Here they are again, excluding the simplest, with a slight modification
(using a command for a non-expanded argument).

The inexperienced user maybe starts with the simplest solution:

eval button .b $stdargs -text $mytext -bd $border

eval exec $prog $opts1 [getMoreopts] [getFile1] $file2

This may work well, but will probably bite you soon due
to some bad char in an argument you don't want expanded.
Typically a space in a file name, or a \ in a Windows path.
Also, the author's intentions are obscured since it is
not clear what is indended for expansion.
So, you quote them:

eval button .b $stdargs -text \$mytext -bd \$border

eval exec \$prog $opts1 [getMoreopts] {[getFile1]} \$file2

This usually is good enough, but is still not completely safe
unless you know that the things to expand are canonical lists.
To be safe it becomes:

eval button .b [lrange $stdargs 0 end] -text \$mytext -bd \$border
eval exec \$prog [lrange $opts1 0 end] [lrange [getMoreopts] 0 end] \
{[getFile1]} \$file2

Still not that hard to write, but I wouldn't expect a beginner to
do it. Even experienced Tcl'ers tend to get it wrong (including me
if I don't concentrate).

IMO, the expand way is simpler, and I wouldn't call it a minimal
improvement:

expand { button .b @$stdargs -text $mytext -bd $border }

expand { exec $prog @$opts1 @[getMoreopts] [getFile1] $file2 }

It also has the advantage of clarity by saying what I want to do.
If I want to evaluate a script, I use eval. If I want to get
argument expansion, I use expand instead of trying to build and
evaluate a script that emulates argument expansion while trying
not to shoot myself in the foot.

/Peter

Ramon Ribó

unread,
Jun 27, 2002, 4:25:42 AM6/27/02
to

Hello,

Maybe, I am missing the point, but as I understand, the natural solution
to
this would be, instead of using expand and mark the lists, just use eval and
mark
the strings:

eval exec [list $prog] $opts1 [getMoreopts] [list $file1] [list $file2]

assuming opts1 and [getMoreopts] are valid lists.

This solution is not less natural than expand and avoids the creation of
a new
command.

As I see it, the problem is not in the TCL commands but in the
documentation. People
tend to make confusion between string and lists and this could be very
easily solved
with a few clever examples in the documentation. In this case, in the eval
manual page
there should be a section with 2 or 3 common examples. One of them could be
this one,
clearly stating that all arguments to 'eval' or to 'concat' must be valid
lists.

Another common place where people make the same mistake is in:

button .b -command "mycommand $var"

It is better to use:

button .b -command [list mycommand $var]

To avoid problems for arbitrary values of var. This could also be easily
solved with a
simple set of examples in the manual page.

It is important to remember that in TCL everything is a string but there
are many places
where a list is necessary, and they are not exactly the same thing.

I can provide help to the TCL development team by defining some examples
if someone
else is willing to include them in the official documentation.

Best regards,

Ramon Ribó


Donal K. Fellows

unread,
Jun 27, 2002, 5:29:34 AM6/27/02
to
Peter Spjuth wrote:
> IMO, the expand way is simpler, and I wouldn't call it a minimal
> improvement:
>
> expand { button .b @$stdargs -text $mytext -bd $border }
> expand { exec $prog @$opts1 @[getMoreopts] [getFile1] $file2 }

Some relative newcomers might not realize this, but the whole expand business
was discussed on this newsgroup (at great length) a few years back, and much
experimentation indicates that it is easier in practise to mark those arguments
that want expanding (typically only one or two) than it is to mark those
arguments that want to be not expanded (which can be quite a few!) I'm sure
groups.google.com will be able to provide the details...

Peter, how would I pass a constant argument that starts with an '@' symbol
through expand? I'm sure it'd be possible to think of an application where this
would be useful. :^)

-- I have to warn you up front that I'm pretty sure you're full of crap, but
it might still be interesting to see your argument.
-- Bill Newman <wne...@netcom.com>

Donal K. Fellows

unread,
Jun 27, 2002, 5:22:36 AM6/27/02
to
"Ramon Ribó" wrote:
> Maybe, I am missing the point, but as I understand, the natural
> solution to this would be, instead of using expand and mark the lists,
> just use eval and mark the strings:
>
> eval exec [list $prog] $opts1 [getMoreopts] [list $file1] [list $file2]
>
> assuming opts1 and [getMoreopts] are valid lists.

Did you know that you can use newlines as separators and still have a valid
list? And yet [eval] will do something quite different? The key is to realize
that [eval] is defined to work with strings and scripts, and there is plenty of
code out there that depends on this behaviour.

> This solution is not less natural than expand and avoids the creation
> of a new command.

But it is unsafe unless you jump through the hoops detailed by Peter elsewhere
in this thread, or know for sure the cleanliness of the lists (you can get away
with this fairly often in practise, since mostly it seems to be things like
$args that are being expanded).

> As I see it, the problem is not in the TCL commands but in the
> documentation. People tend to make confusion between string and lists
> and this could be very easily solved with a few clever examples in the
> documentation. In this case, in the eval manual page there should be a
> section with 2 or 3 common examples. One of them could be this one,
> clearly stating that all arguments to 'eval' or to 'concat' must be
> valid lists.

This is at total variance with the current definition and behaviour of those
commands. The behaviour of [concat] is defined to be (effectively) apply
[string trim] to all arguments and pass them through [join], and
[eval $arg1 ... $argN] is defined to act as [eval [concat $arg1 ... $argN]]
(where the single argument form of [eval] takes a script as an argument, not
a command.)

By contrast, [expand] knows it is working with a single command, and this allows
for completely different whitespace handling.

Peter Spjuth

unread,
Jun 27, 2002, 8:20:11 AM6/27/02
to
"Donal K. Fellows" wrote:
>
> Peter, how would I pass a constant argument that starts with an '@' symbol
> through expand? I'm sure it'd be possible to think of an application where this
> would be useful. :^)

To quote from the TIP:

_Note 1:_ A word should really start with @ to trigger expansion which
means that words like these are not expanded:

cmd "@$temp" {@home} \@[something]

There you have three methods of quoting a @.

/Peter

Ramon Ribó

unread,
Jun 27, 2002, 10:41:50 AM6/27/02
to

Donald,

Correct me if I am wrong, but as I understand, if the arguments to be
considered as
list are real lists, there cannot be any problems.

I understand as real list as the one that comes from a command that
returns a list:
list, lrange, linsert, split ... or the argument 'args' of a proc.

I know that this definition is subjective, but taking care of it avoids
many problems.

Regards,


"Donal K. Fellows" <fell...@cs.man.ac.uk> escribió en el mensaje
news:3D1AD95C...@cs.man.ac.uk...

Kristoffer Lawson

unread,
Jun 27, 2002, 12:18:55 PM6/27/02
to
"Ramon Rib?" <ram...@compassis.com> wrote:
> Another common place where people make the same mistake is in:
>
> button .b -command "mycommand $var"
>
> It is better to use:
>
> button .b -command [list mycommand $var]

To be honest, I always found the link between strict list format and
[eval]able commands to be tenuous. It is documented, but not immediately
obvious. In the beginning I didn't realize that [list] adds extra
braces for words beginning with $, for example. So I didn't know it could
be used as above. I still think it is confusing to read the code above
for a beginner and I would much prefer:

button .b -command [compound mycommand $var]

Or whatever. While effectively that does exactly the same as [list]
a specific command for grouping words together for safe evaluation would
make everything much clearer. Obviously then [list] itself could be
simplified so it wouldn't have to bother with extra dollars and quotation.

--
/ http://www.fishpool.com/~setok/

ulis

unread,
Jun 27, 2002, 2:45:29 PM6/27/02
to
> Correct me if I am wrong, but as I understand, if the arguments to be
> considered as
> list are real lists, there cannot be any problems.

It is really an annoyance that Tcl considers strings as lists, and
times to times, adds from itself antislashes inside strings (for me,
this is a bug).

I would like that there is a way to said that a string is a string and
should not be considered as a list.

ulis

Roy Terry

unread,
Jun 27, 2002, 3:41:03 PM6/27/02
to
Sure. Just don't pass the string to
commands that operate on lists:
l*
concat
foreach
join
array set
...

Don Porter

unread,
Jun 27, 2002, 3:52:12 PM6/27/02
to
In article <3D1B6A47...@earthlink.net>, Roy Terry wrote:
> Sure. Just don't pass the string to
> commands that operate on lists:
> l*
> concat
> foreach
> join
> array set
> ...

[concat] operates on strings, not lists. That's essential to proper
working of [eval] and brethren.

--
| Don Porter Mathematical and Computational Sciences Division |
| donald...@nist.gov Information Technology Laboratory |
| http://math.nist.gov/~DPorter/ NIST |
|______________________________________________________________________|

Joe English

unread,
Jun 27, 2002, 6:30:21 PM6/27/02
to
Roy Terry wrote:

>Sure. Just don't pass the string to commands that operate on lists:
>l*
>concat

By the way, contrary to appearances [concat] really operates
on strings, not on lists. The man page lies.

If the arguments to [concat] happen to be valid lists (not
necessarily canonical-form), then the result will also be
a valid list (in canonical form if the original arguments are).

Internally, [concat] has a pure-list optimization similar to
the one in [eval], but by default it only uses the string rep.


--Joe English

jeng...@flightlab.com

ulis

unread,
Jun 28, 2002, 5:28:30 AM6/28/02
to
> > I would like that there is a way to said that a string is a string and
> > should not be considered as a list.
> Sure. Just don't pass the string to
> commands that operate on lists:

Very difficult to do when generating code: parameters can be strings
or lists.
After all, invokations of commands are lists containing strings. And
manipulating these invokations needs lists commands.

For illustrate that, here is a simplistic OO extension.
Proc 'class' defines a class (a namespace containing objects).
Proc 'new' defines an object (a numbered sub-namespace where is
defined the member 'str').
Proc 'new' returns a reference to an object (a proc that can eval
method calls).
Proc 'method' defines a proc evaled inside the object sub-namespace.
----
proc class {aname} { namespace eval $aname { set ID 0 } }
proc new {aclass args} \
{
namespace eval $aclass \
{
namespace eval obj[incr ID] { set str "" }
proc obj$ID {amethod args} \
{
set theref [lindex [info level 0] 0]
eval $amethod $theref $args
}
}
return ${aclass}::obj[set ${aclass}::ID]
}
proc method {name parms body} \
{
eval [format \
{proc %s {ref %s} { namespace eval $ref %s }} \
$name $parms $body]
}

class myclass
method myclass::setstr {value} { set str $value }
method myclass::getstr {} { set str }
set obj [new myclass]
$obj setstr "a string"
puts [$obj getstr]
----

All this is straigthforward and shows how easy is Tcl coding.
But this fails when the method 'setstr' is called with a string
containing spaces: mixing strings and lists is not allowed.

For me, Tcl needs a way to say: 'this string is not a list'.

ulis

Donal K. Fellows

unread,
Jun 28, 2002, 6:41:30 AM6/28/02
to
Joe English wrote:
> Internally, [concat] has a pure-list optimization similar to
> the one in [eval], but by default it only uses the string rep.

Both [concat] and [eval] use Tcl_ConcatObj() in their implementation, and that
function works normally on strings. However, it has been told that in one
special case (all arguments to be concatenated have no current string
interpretation, just list interpretations) it can do something a bit more
efficient that avoids generating the strings (which can always be generated
later if so desired; it's just postponing work, hopefully forever.) This is
purely an optimization though; *semantically* [concat] works on strings and
always (or at least for the last seven years or so, and there is plenty of
evidence to suggest that its behaviour had been precisely defined for a long
time even then) has.

Rule #1 of Tcl Semantics:
The Semantics of Tcl are Based on Strings.

-- Well, I'm not exactly a high-brow cineaste either. The number of Iranian
movies I've seen can be counted on one hand by a guy who lost all his
fingers in a tragic fax machine accident. -- Mike Kozlowski

Donal K. Fellows

unread,
Jun 28, 2002, 8:32:48 AM6/28/02
to
ulis wrote:
> proc class {aname} { namespace eval $aname { set ID 0 } }
> proc new {aclass args} {
> namespace eval $aclass {
> namespace eval obj[incr ID] { set str "" }
> proc obj$ID {amethod args} {
> set theref [lindex [info level 0] 0]
> eval $amethod $theref $args
> }
> }
> return ${aclass}::obj[set ${aclass}::ID]
> }
> proc method {name parms body} {
> eval [format \
> {proc %s {ref %s} { namespace eval $ref %s }} $name $parms $body]
> }

Hmm. The problem is that you've mixed up substitution contexts quite
thoroughly; you have to do it by keeping a list of procedures to create in each
object in the class (alas, that makes the system quite a lot more expensive) and
you could really do with a few more instances of [uplevel] in there. Getting
things like this right is why using a real object system is better than rolling
your own on the cheap.

If you want objects, use Itcl or XOtcl or ...

> All this is straigthforward and shows how easy is Tcl coding.
> But this fails when the method 'setstr' is called with a string
> containing spaces: mixing strings and lists is not allowed.

It's not that simple. The deep problem is the multiplicity of different
variable contexts needed, given that Tcl does not provide closures in the same
way as, say, Perl (where someone's done a lot of the hard bit already.)

Donal K. Fellows

unread,
Jun 28, 2002, 11:07:25 AM6/28/02
to
"Donal K. Fellows" wrote:
> If you want objects, use Itcl or XOtcl or ...

[Following up to myself - post in haste, repent at leisure!]

... or Stooop or ...

lvi...@yahoo.com

unread,
Jun 29, 2002, 6:32:11 PM6/29/02
to

According to Joe English <jeng...@flightlab.com>:
:By the way, contrary to appearances [concat] really operates

:on strings, not on lists. The man page lies.

So will the man page be updated?

Donal K. Fellows

unread,
Jul 1, 2002, 6:16:15 AM7/1/02
to
lvi...@yahoo.com wrote:
> According to Joe English <jeng...@flightlab.com>:
>: By the way, contrary to appearances [concat] really operates
>: on strings, not on lists. The man page lies.
> So will the man page be updated?

If someone can suggest a better substitute. Note that that page is *really*
old; when it talks about lists, it is talking about lists in a string pre-8.0
sense...

make: *** No rule to make target `war'. Stop.

Andreas Leitgeb

unread,
Jul 1, 2002, 2:53:44 PM7/1/02
to
The advantage of expand&co versus eval from the readability-point of view
lies primarily in
a) whether you have to mark the to-be-expanded arguments,
b) or the NOT-to-be-expanded arguments.

I'm leaving aside all the performance-arguments, and only collect (some of)
the arguments, why "a)" is sufficiently better than "b)" to
justify a new command (or even enhanced syntax :-)
(Each of these might already have appeared in some article in this thread)

1) There are usually more not-expanded arguments than others. (counting
only lines WITH expansion, of course; otherwise it would be trivial)
2) Some "arguments" (e.g. usually the invoked command itself) are immune
to expansion (a single word is also a list containing only that word),
but only at first glance: if a single word is given to eval, then
eval sees it as string, not list, thus it also stringifies the rest,
even if that would have been pure lists. To work around that, one
would even have to substitute each plain word to [list word].
3) Users more likely tend to "protect" too little than "marking" too much.
(laziness)
4) "eval [list command ... ] ..." pushes the actual "command" completely
out of first sight. It requires full concentration to visually pick
the real arguments from the meta-commands needed for protection.
A command is easiest understood, if the actual command is the first word
of the line, rather than some meta-commands.

a more personal one:
5) Those, who preach minimality of tcl, are better served with scheme/lisp,
which (imho) is principially nothing else than a minimalistic tcl,
with ()'s instead of []'s and even without the 'alien' expr-syntax :-)


>> Donal (a supporter of {expand}$substitution...)
>
> /Peter, who wouldn't mind {expand}$substitution either

/AvL, who primarily came up with {}$substitution, and although still
likes that best, has been convinced, that {expand}$substitution looks
less cryptic/perl'ish... and who would be happy with each of these.

--
Newsflash: Sproingy made it to the ground !
read more ... <http://avl.enemy.org/sproingy/>

Joe English

unread,
Jul 1, 2002, 5:00:43 PM7/1/02
to
Andreas Leitgeb wrote:
>The advantage of expand&co versus eval from the readability-point of view
>lies primarily in
>a) whether you have to mark the to-be-expanded arguments,
>b) or the NOT-to-be-expanded arguments.

A third way of looking at it, which I prefer, is that
you _construct a command to be invoked_. Since a command
is just a list of words, you can use whichever of Tcl's
many list processing commands are most appropriate for
the circumstances at hand. IOW: you don't mark arguments
to be expanded, or mark arguments not to be expanded,
instead you build a list.

The only thing that's missing is a way to invoke the constructed
command. ([eval] is unsuitable for this because of the many subtle
differences between strings-interpeted-as-scripts and strings-
interpreted-as-lists.)


> [...]


>2) Some "arguments" (e.g. usually the invoked command itself) are immune

> to expansion [...]

One important exception to this rule is the common
idiom of command prefixes used as callbacks. Here
you usually have a fixed list of (extra) arguments,
but the command prefix can contain any number of words.


--Joe English

jeng...@flightlab.com

Donal K. Fellows

unread,
Jul 2, 2002, 6:24:59 AM7/2/02
to
Joe English wrote:

> Andreas Leitgeb wrote:
>> 2) Some "arguments" (e.g. usually the invoked command itself) are immune
>> to expansion [...]
>
> One important exception to this rule is the common
> idiom of command prefixes used as callbacks. Here
> you usually have a fixed list of (extra) arguments,
> but the command prefix can contain any number of words.

Callbacks of this kind is actually where the way [eval] currently works can be
used to great effect (the ';#' trick is great when you really don't care about
the extra args...)

-- What's the reason of long discussions, if at the end someone says, "we have
not yet thought about it..." -- Andreas Leitgeb <a...@pc7499.gud.siemens.at>

Stephen Trier

unread,
Jul 2, 2002, 11:04:10 AM7/2/02
to
Could [expand] be implemented in Tcllib to gain experience before it, or
a revised version of it, goes into the core? That would allow the community
to test its utility and possibly reach consensus about the syntax.

Testing the concept in Tcllib would also allow [expand] to get into
circulation well before Tcl 8.5 is realized.

Stephen

--
Stephen Trier
Technical Development Lab
Cleveland FES Center
s...@po.cwru.edu

lvi...@yahoo.com

unread,
Jul 2, 2002, 1:52:02 PM7/2/02
to

According to Andreas Leitgeb <a...@logic.at>:
:1) There are usually more not-expanded arguments than others. (counting
: only lines WITH expansion, of course; otherwise it would be trivial)

Here's where I really fall out of the norm, I guess. Most of the
Tcl apps that I create are relatively simplistic I guess - because nearly
all of my apps have only arguments that get expanded.

0 new messages