my_question
my_question

Reputation: 3235

Can proc argument be variable

I just tried the following in tclsh:

proc pp {$ag} { puts hi}
pp  whatever

To my surprise, this works fine! I had expected a syntax error because the proc argument is a variable. But it seems tcl takes it fine.

Then I experimented:

proc pp {$ag} { puts "hi $ag"}
pp  whatever

and

proc pp {$ag} { puts "hi ${$ag}"}
pp  whatever

I got error, can't read "ag": no such variable.

This makes me wondering, can proc argument be variable? If not, why not error out in the first script?

Upvotes: 1

Views: 147

Answers (1)

Peter Lewerin
Peter Lewerin

Reputation: 13252

The first case works because you never use the parameter to pp.

When you invoke the proc command, the text of the invocation is evaluated just like in any other command invocation. In all the above cases, the third argument (which will become the argument list of the pp command) is is wrapped in braces, which means it won't be evaluated as a variable but as a string of three characters: "$ag" (i.e. the dollar sign is just a regular character here). This is not an error, and does work, just not the way you seem to expect it to.

It's a bit tricky to get the value of the parameter $ag, though. This works:

proc pp {$ag} {puts "hi [set {$ag}]"}

The dollar notation is actually just syntactic sugar for the unary set command. Sometimes the dollar notation won't work, and you need to fall back to an explicit set.

This does work too, though:

proc pp {$ag} {puts "hi ${$ag}"}

So, in your invocations, the third argument to proc isn't really a variable evaluation, it just looks like one. You can of course use an actual variable evaluation in the invocation of proc:

set foo {bar baz}
proc qux $foo {puts $bar ; puts $baz}
qux abc def
# => abc
# => def

What the Tcl interpreter really sees here is:

proc qux {bar baz} {puts $bar ; puts $baz}

Or you can go really crazy:

set foa {proc qux}
set fob {bar}
lappend fob baz
set foc {puts $bar}
set fod "puts \$baz"
{*}$foa $fob [join [list $foc $fod] { ; }]

Which amounts to the same thing as the previous invocation of proc. (If you don't believe me, try list {*}$foa $fob [join [list $foc $fod] { ; }])

This example just looks (and is) weird, but many times it's actually useful to construct new commands within your program, and in those cases it's really nice that the text used in the invocation of proc, like with any other command, is simply text that the evaluation rules of Tcl can be applied to. You can use any kinds of string or list operations on it and join up pieces of text from various sources, even user input (if you can trust it).

Documentation: Tcl evaluation rules including $ and {*}, join, lappend, list, proc, puts, set

Upvotes: 3

Related Questions