Tcl: Evaluate the variable value when passing argument to class - tcl

I have a piece of code like this
oo::class create class_test {
variable title_text
variable result
method title {t} {
set title_text $t
set result [format "%-6s %-6s" {*}$title_text]
}
method print {} {
return $result
}
}
set a "abcde"
set b "fghij"
class_test create foo
foo title {"$a" "$b"}
puts [foo print]
The real output is
$a $b
While the expected output is
abcde efghi
Could someone help to fix it?

Change
foo title {"$a" "$b"}
to
foo title [list $a $b]
so that the variables get substituted by their values.

You want to expand substitutions inside a {brace-quoted} string (logically) after the point that it is written in the script. This isn't usually recommended (not when you can construct arguments with list correctly), but you can do it.
method title {t} {
set title_text [lmap value $t {
uplevel 1 [list subst $value]
}]
set result [format "%-6s %-6s" {*}$title_text]
}
We do the transform on each word in the argument (lmap) and the transform is to apply subst to it, which must be done in the caller's context (uplevel 1). The use of list in there is so that we guarantee that we make a substitution-free script to run in the outer context, a very strongly recommended practice.
A feature of TclOO is that you don't need to take special precautions to use uplevel (or upvar) when using it, unlike some other older object systems for Tcl. That makes doing this sort of thing in a method no more tricky than doing it in a normal procedure. This is true even when inheritance is present.

Could someone help to fix it?
I fail to see why you, first, pack variable references into a single value and, then, uplevel-substitute them. In addition, the number of value arguments to format seem fixed. Why not just use two separate formal parameters to your title method and use them directly?
method title {v1 v2} {
set result [format "%-6s %-6s" $v1 $v2]
}
Then just call it like so:
foo title $a $b
Update
to generate the title in different length
then better use args like so?
method title {args} {
set result [format [join [lrepeat [llength $args] "%-6s"] " "] {*}$args]
}
args is the natural way of having a method (proc) with variable arguments in Tcl.

Related

Tcl - Differentiate between list/dict and anonymous proc

I wrote the following proc, which simulates the filter function in Lodash (javascript library) (https://lodash.com/docs/4.17.4#filter). You can call it in 3.5 basic formats, seen in the examples section. For the latter three calling options I would like to get rid of the the requirement to send in -s (shorthand). In order to do that I need to differentiate between an anonymous proc and a list/dict/string.
I tried looking at string is, but there isn't a string is proc. In researching here: http://wiki.tcl.tk/10166 I found they recommend info complete, however in most cases the parameters would pass that test regardless of the type of parameter.
Does anyone know of a way to reliable test this? I know I could leave it or change the proc definition, but I'm trying to stay as true as possible to Lodash.
Examples:
set users [list \
[dict create user barney age 36 active true] \
[dict create user fred age 40 active false] \
]
1. set result [_filter [list 1 2 3 4] {x {return true}}]
2. set result [_filter $users -s [dict create age 36 active true]]
3. set result [_filter $users -s [list age 36]]
4. set result [_filter $users -s "active"]
Proc Code:
proc _filter {collection predicate args} {
# They want to use shorthand syntax
if {$predicate=="-s"} {
# They passed a list/dict
if {[_dictIs {*}$args]} {
set predicate {x {
upvar args args
set truthy 1
dict for {k v} {*}$args {
if {[dict get $x $k]!=$v} {
set truthy false
break
}
}
return $truthy
}}
# They passed just an individual string
} else {
set predicate {x {
upvar args args;
if {[dict get $x $args]} {
return true;
}
return false;
}}
}
}
# Start the result list and the index (which may not be used)
set result {}
set i -1
# For each item in collection apply the iteratee.
# Dynamically pass the correct parameters.
set paramLen [llength [lindex $predicate 0]]
foreach item $collection {
set param [list $item]
if {$paramLen>=2} {lappend param [incr i];}
if {$paramLen>=3} {lappend param $collection;}
if {[apply $predicate {*}$param]} {
lappend result $item
}
}
return $result
}
Is x {return true} a string, a list, a dictionary or a lambda term (the correct name for an anonymous proc)?
The truth is that it may be all of them; it would be correct to say it was a value that was a member of any of the mentioned types. You need to describe your intent more precisely and explicitly rather than hiding it inside some sort of type magic. That greater precision may be achieved by using an option like -s or by different main command names, but it is still necessary either way. You cannot correctly and safely do what you seek to do.
In a little more depth…
All Tcl values are valid as strings.
Lists have a defined syntax and are properly subtypes of strings. (They're implemented differently internally, but you are supposed to ignore such details.)
Dictionaries have a syntax that is equivalent to lists with even numbers of elements where the elements at the even indices are all unique from each other.
Lambda terms are lists with two or three elements (the third element is the name of the context namespace, and defaults to the global namespace if it is absent). The first element of the list needs to be a valid list as well.
A two-element list matches the requirements for all the above. In Tcl's actual type logic, it is simultaneously all of the above. A particular instantiation of the value might have a particular implementation representation under the covers, but that is a transient thing that does not reflect the true type of the value.
Tcl's type system is different to that of many other languages.

What purpose does upvar serve?

In the TCL code that I currently work on, the arguments in each procedure is upvar'ed to a local variable so to speak and then used. Something like this:
proc configure_XXXX { params_name_abc params_name_xyz} {
upvar $params_name_abc abc
upvar $params_name_xyz xyz
}
From here on, abc and xyz will be used to do whatever. I read the upvar TCL wiki but could not understand the advantages. I mean why cant we just use the variables that have been received as the arguments in the procedure. Could anybody please elaborate?
I mean why cant we just use the variables that have been received as the arguments in the procedure.
You can. It just gets annoying.
Typically, when you pass the name of a variable to a command, it is so that command can modify that variable. The classic examples of this are the set and incr commands, both of which take the name of a variable as their first argument.
set thisVariable $thisValue
You can do this with procedures too, but then you need to access the variable from the context of the procedure when it is a variable that is defined in the context of the caller of the procedure, which might be a namespace or might be a different local variable frame. To do that, we usually use upvar, which makes an alias from a local variable to a variable in the other context.
For example, here's a reimplementation of incr:
proc myIncr {variable {increment 1}} {
upvar 1 $variable v
set v [expr {$v + $increment}]
}
Why does writing to the local variable v cause the variable in the caller's context to be updated? Because we've aliased it (internally, it set up via a pointer to the other variable's storage structure; it's very fast once the upvar has been done). The same underlying mechanism is used for global and variable; they're all boiled down to fast variable aliases.
You could do it without, provided you use uplevel instead, but that gets rather more annoying:
proc myIncr {variable {increment 1}} {
set v [uplevel 1 [list set $variable]]
set v [expr {$v + $increment}]
uplevel 1 [list set $variable $v]
}
That's pretty nasty!
Alternatively, supposing we didn't do this at all. Then we'd need to pass the variable in by its value and then assign the result afterwards:
proc myIncr {v {increment 1}} {
set v [expr {$v + $increment}]
return $v
}
# Called like this
set foo [myIncr $foo]
Sometimes the right thing, but a totally different way of working!
One of the core principles of Tcl is that pretty much anything you can do with a standard library command (such as if or puts or incr) could also be done with a command that you wrote yourself. There are no keywords. Naturally there might be some efficiency concerns and some of the commands might need to be done in another language such as C to work right, but the semantics don't make any command special. They all just plain commands.
The upvar command will allow you to modify a variable in a block and make this modification visible from parent block.
Try this:
# a function that will modify the variable passed
proc set_upvar { varname } {
upvar 1 $varname var
puts "var was $var\n"
set var 5
puts "var is now $var\n"
}
# a function that will use the variable but that will not change it
proc set_no_upvar { var } {
puts "var was $var\n"
set var 6
puts "var is now $var\n"
}
set foo 10
# note the lack of '$' here
set_upvar foo
puts "foo is $foo\n"
set_no_upvar $foo
puts "foo is $foo\n"
As it was mentioned in comment above, it is often used for passing function arguments by reference (call by reference). A picture costs a thousand words:
proc f1 {x} {
upvar $x value
set value 0
}
proc f2 {x} {
set x 0
}
set x 1
f1 x
puts $x
set x 1
f2 x
puts $x
will result in:
$ ./call-by-ref.tcl
0
1
With upvar we changed variable x outside of function (from 1 to 0), without upvar we didn't.

TCL/Expect - $argv VS $::argv VS {*}$argv

What is difference between following variables:
$argv
$::argv
{*}$argv
First two are possible to print via puts command and they returns following output:
param0 param1 {param 2} param3
param0 param1 {param 2} param3
The arguments that was passed to script were:
param0 param1 "param 2" param3
The last one end up with error:
wrong # args: should be "puts ?-nonewline? ?channelId? string"
while executing
"puts {*}$argv"
I've done some research in this area using following code:
if {[array exists $argv]} {
puts "\$argv IS ARRAY"
} else {
puts "\$argv IS NOT AN ARRAY"
}
if {[string is list $argv]} {
puts "\$argv IS LIST"
} else {
puts "\$argv IS NOT LIST"
}
if {[array exists $::argv]} {
puts "\$::argv IS ARRAY"
} else {
puts "\$::argv IS NOT AN ARRAY"
}
if {[string is list $::argv]} {
puts "\$::argv IS LIST"
} else {
puts "\$::argv IS NOT LIST"
}
if {[array exists {*}$argv]} {
puts "{*}\$::argv IS ARRAY"
} else {
puts "{*}\$::argv IS NOT AN ARRAY"
}
if {[string is list {*}$argv]} {
puts "{*}\$::argv IS LIST"
} else {
puts "{*}\$::argv IS NOT LIST"
}
The last two if-else statements which contain {*}$argv ends with following error:
wrong # args: should be "array exists arrayName"
while executing
"array exists {*}$argv"
invoked from within
"if {[array exists {*}$argv]} {
puts "{*}\$::argv IS ARRAY"
} else {
puts "{*}\$::argv IS NOT AN ARRAY"
}"
Commenting out those two statements shows that $argv and $::argv are lists:
argv IS NOT AN ARRAY
$argv IS NOT AN ARRAY
argv IS LIST
$argv IS LIST
Both those lists can be traversed as standard list e.g.:
foreach item $argv {
puts $item
}
or
foreach item $::argv {
puts $item
}
Attempt to traverse {*}$argv the same way leads to following error again:
wrong # args: should be "foreach varList list ?varList list ...? command"
while executing
"foreach item {*}$argv {
puts $item
}"
I am using TCL version 8.5
What is difference between following variables:
$argv
$::argv
{*}$argv
There are two types of difference here.
Unqualified and Qualified Variables
In Tcl, unqualified and qualified variables can be a bit different, but it depends on the context (in a pretty simple way though). Firstly, a qualified variable name is one that contains at least one :: within it. If the variable name (the thing after the $ — in Tcl, $ just means “read this variable now and use its contents here”) starts with ::, it is an absolute variable name, otherwise a qualified variable name is a relative variable name and is resolved with respect to the current namespace (which you can find out with namespace current if you're uncertain). Absolute variable names always refer to the same thing, in all contexts. Thus, ::argv is an absolute variable name, and indeed it refers to a variable called argv in the top-level, global namespace. That happens to be a variable that tclsh and wish write their arguments into.
But if there is no ::, it is an unqualified variable name. If you are not in a procedure (or procedure-like thing, which includes a lambda term such as you'd use with apply or the methods defined by various OO systems) then the variable is (mostly) treated as if it was a relative variable name and resolved with respect to the current namespace. namespace eval and namespace code are two of the things that can change the current namespace (the others are more obscure). All this is provided you use variable to declare all your namespace variables. Otherwise, you can hit some weird problems with variable resolution which are really nasty. So do use variable. Really.
If you are in a procedure(-like entity) though, that unqualified name refers to a local variable, whose life is coupled to that of the stack frame pushed on the stack when the procedure is entered. That can be linked to variables in other scopes (including the global namespace) through various commands: global, upvar, variable, and namespace upvar. However, the actual resolution of the variable is to something local.
Finally, there might also be a custom variable resolver in place. Since you're using Tcl 8.5, the place where you're most likely to see this in use is if you're using Incr Tcl, an object system for Tcl. Custom variable resolvers can do some complex stuff. (If you were using Tcl 8.6, the most likely place to see a custom variable resolver at work is in TclOO. The variable resolver there is very conservative and cautious, but allows local variables to be bound to object variables without having to explicitly declare this in each method).
Normal and Expanding Substitution
The difference between $argv and {*}$argv is totally different.
$argv is a normal substitution. It says “read this variable here and use the contents of it instead”. It can be used in the middle of a word, so $argv$argv$argv is a thing, consisting of the concatenation of the contents of the argv variable three times.
{*}, when placed at the start of a word (it's not special elsewhere), marks that word for expansion. When a word is expanded, it's parsed as a Tcl list after all other normal substitutions have been done, and the words of that list are used as words in the resulting command being built up. {*}$argv is a degenerate case where the remainder of the word is just the a read from a variable; the words that are used in the command are the elements of the list in the argv variable. Since that's normally a list, this is all hunky-dory.
Here's an example:
set abc {a b c}
set dabcf [list d $abc f]
puts $dabcf; # ===> “d {a b c} f”
set eabcg [list e {*}$abc g]
puts $eabcg; # ===> “e a b c g”
See the difference? One produces three elements in the list, the other produces five. It makes even more sense with something somewhat longer:
set options {
-foreground blue
-background yellow
-text "This is eye-watering stuff!"
}
button .b1 {*}$options -command {puts "Ouch 1"}
button .b2 {*}$options -command {puts "Ouch 2"}
button .b3 {*}$options -command {puts "Ouch 3"}
pack .b1 .b2 .b3
With expansion, that all Just Works™. Without, you'd have to do something horrific with eval:
eval [list button .b1] [lrange $options 0 end] [list -command {puts "Ouch 1"}]
# etc.
This was difficult to get right, and tedious, so it caused lots of people (including Tcl and Tk maintainers!) many problems because they tended to take shortcuts and get it wrong. It was to address this that expansion syntax was created in Tcl 8.5 to make this all less error prone. (The prototypical example in plain Tcl tends to involve things with exec, and meant that quite a few people actually had security holes because of this.)
As a bonus, using {*} is much faster than using eval since expansion can guarantee that it is never doing complicated reparsing of things. In Tcl, faster virtually always correlates with safer.
Note that this is independent of whether the variable is qualified. Yes, that means that you can also have {*}$::argv if you want.
You confuse the effects of substitution with the effects of argument expansion.
Please study the Dodekalogue http://wiki.tcl.tk/10259.
You mix the Rule #5: Argument Expansion (the {*} thing) with Variable Substitution (Rule #8).
The three forms you listed above are equivalent to the following:
$argv -> [set argv]
Get the value of a simple variable in the currently active scope.
$::argv -> [namespace eval :: { set argv }] -> [set ::argv]
Get the value of the variable in the namespace :: (the global namespace)
{*}$argv -> [eval [set argv]]
Expand the variables content to multiple arguments.

"can't read:variable is array" error in ns2

In ns2, I declared a simple array using
array set ktree {}
then I tried to use it as a GOD variable as
create-god $ktree
but this gives the error
can't read "ktree": variable is array
while executing
"create-god $ktree {}"
Any help is greatly appreciated.
In Tcl, $varName means “read from the variable called varName” and is not a general reference to the variable (unlike some other languages, notably Perl and PHP, which do rather different things). Reading from a whole array, instead of an element of that array, is always an error in Tcl.
To pass an array to a command, you pass the name of that array in. It's then up to that command to access it as it sees fit. For procedures and methods written in Tcl, it'll typically involve upvar to link the array into a local view. (Things written directly in C or C++ have far fewer restrictions as they don't automatically push a Tcl stack frame.)
Note however that the command must be expecting the name of an array when you pass that name in. (Good programmers will document this fact, of course.) Whether create-god does, I really have no idea; it's not a general Tcl command but rather something that's more specific. (Part of ns2? Or maybe your own code.)
Example of passing in an array
An example of passing in an array by name is the parray command that should be part of every Tcl distribution. It's a procedure that prints an array out. Here's the source code without a few boiler-plate comments:
proc parray {a {pattern *}} {
upvar 1 $a array
if {![array exists array]} {
error "\"$a\" isn't an array"
}
set maxl 0
set names [lsort [array names array $pattern]]
foreach name $names {
if {[string length $name] > $maxl} {
set maxl [string length $name]
}
}
set maxl [expr {$maxl + [string length $a] + 2}]
foreach name $names {
set nameString [format %s(%s) $a $name]
puts stdout [format "%-*s = %s" $maxl $nameString $array($name)]
}
}
The key thing here is that we first see upvar 1 to bind the named variable in the caller to a local variable, and a test with array exists to see if the user really passed in an array (so as to give a good error message rather than a rubbishy one). Everything else then is just the implementation of how to actually pretty-print an associative array (finding out the max key length and doing some formatted output); it's just plain Tcl code.

In TCL,can we pass parameters in this way?

I have a question about passing parameters in Tcl regarding to the following code:
set name "Ronaldo"
proc GET_PLAYER_INFO {player_id {player_name "$name"}} {
global name
puts $player_name
}
regarding to the code above, we have a global variable "name", and in the parameter list of proc GET_PLAYER_INFO, the default value of parameter player_name is set to "$name"? if the value of name is "ronaldo", it is already double quotation,do we need to put double quotation in the parameters list like this: player_name "$name"? and before we execute the "global name" command, is the default value of player_name is "Ronaldo"? is so, why we need to have "global name" command in our proc?
That won't work as it stands; the $name won't be evaluated at all so the default will be those literal five characters.
If you're binding the default value at the time you create the procedure, you'd do it like this:
proc GET_PLAYER_INFO [list player_id [list player_name $name]] {
...
}
That is, the arguments to proc are just normal things you can construct with Tcl commands and substitutions. This is one of the great things about Tcl.
However, if you're wanting to evaluate that $name at the time the procedure is called, you've got to do it differently. If you've got some kind of value that will never be used for the player name (e.g., the empty string) then it's pretty easy:
proc GET_PLAYER_INFO {player_id {player_name ""}} {
if {$player_name eq ""} {
set player_name $::name
}
...
}
Note that I've used the fully-qualified variable name there. There are other ways to get that name too (e.g., with global, with upvar, with variable, …)
The place where things get tricky is when you've not got a suitable sentinel value at all. At that point, you have to see how many arguments were actually supplied:
proc GET_PLAYER_INFO {player_id {player_name ""}} {
if {[llength [info level 0]] == 2} {
set player_name $::name
}
...
}
The command info level 0 returns the full list of argument words to the current procedure call. This includes the GET_PLAYER_INFO itself and would be a list of length 2 or 3 in a valid call to the definition above. Once the list is available, checking its length is a trivial exercise in llength and numeric comparison. (Using a sentinel value is easier though, and works in 99.99% of cases.)
The final option is to use the special args formal parameter and do the parsing manually:
proc GET_PLAYER_INFO args {
if {[llength $args] < 1 || [llength $args] > 2} {
return -code error "wrong # args: should be \"GET_PLAYER_INFO player_id ?player_name?\""
}
set player_id [lindex $args 0]
if {[llength $args] > 1} {
set player_name [lindex $args 1]
} else {
set player_name $::name
}
...
}
As you can see, this is rather long-winded...