Is is possible to set the default values of parameters to be the return of a function? (TCL) - tcl

In TCL is it possible to have the default values of parameters be the return value of a function call?
proc GetParameterValue { } {
# calculation for value...
return value
}
proc TestFunction { {paramVal [GetParameterValue]} } {
puts $paramVal
}
TestFunction
This results in printing "[GetParameterValue]". Rather than calling the procedure GetParameterValue. Is this possible to do in TCL or do I need to redesign this bit of code?

The default values of parameters can only be constants that you compute at the time of declaration of the procedure (most commonly, they're literals which means you don't need to use list to do the construction):
proc TestFunction [list [list paramVal [GetParameterValue]]] {
...
}
To compute a default value at procedure call time, you have to move the calculation into the body of the procedure. There's a few ways to do the detection of whether to do the calculation, but they come down to three options: using a marker value, getting a count of words in the call, and taking full control of parsing.
Using a marker value
The trick to this is to find some value that is really unlikely to be passed in. For example, if this is to be a piece of text shown to the user, a value with nothing but an ASCII NUL in it is not going to occur; put that in the default then you can tell whether you've got the default and can substitute with what the complex code provides.
proc TestFunction {{paramVal "\u0000"}} {
if {$paramVal eq "\u0000"} {
set paramVal [GetParameterValue]
}
...
}
Getting a count of words in the call
This relies on the capabilities of the info level introspection command. In particular, info level 0 reports the full list of actual arguments to the current procedure. A bit of counting, and we can can know whether a real value was passed.
proc TestFunction {{paramVal "dummy"}} {
if {[llength [info level 0]] < 2} {
# Note that the command name itself is always present
set paramVal [GetParameterValue]
}
...
}
It's a totally general approach, so there's no worry about the case where someone provides an unexpected edge case, but it's more complicated when you have multiple arguments as you need to work out how many arguments should be present and so on yourself. That is simple in this case, but gets progressively more difficult as you have more arguments.
Taking full control of parsing
Ultimately, you can also decide to make a procedure that takes full control of the parsing of its arguments. You do that by giving it a single argument, args, and then you can use any approach you want to handle the actual argument list. (I tend to not put the formal argument list in parentheses in this case only but that's just my own style.)
proc TestFunction args {
if {[llength $args] == 0} {
set paramVal [GetParameterValue]
} elseif {[llength $args] == 1} {
set paramVal [lindex $args 0]
} else {
# IMPORTANT! Let users discover how to use the command!
return -code error -errorcode {TCL WRONGARGS} \
"wrong # args: should be \"TestFunction ?paramVal?\""
}
...
}
This is currently the only way to do anything truly advanced, e.g., to have optional arguments before mandatory ones. It's also pretty much what you'd have to do in C if you implemented the command there, though adjusted for a different language. The downside is that it is definitely more work than using the built-in basic argument parsing support code provided by the implementation of the proc command.

This is meant as a complement to Donal's thorough answer. In the past, I sometimes resorted to the assistance of [subst] for computed defaults:
proc GetParameterValue {} { return computedDefault }
proc TestFunction {{paramVal [GetParameterValue]}} {
puts [subst -novariables $paramVal]
}
TestFunction; # returns "computedDefault"
TestFunction "providedValue"
TestFunction {$test}
This avoids the need for (implementing) full control over arguments, and is piggybacking onto the built-in argument handler. It also allows for using anonymous procs rather than explicitly named ones for computing the defaults:
proc TestFunction {{paramVal "[apply {{} { return computedValue }}]"}} {
puts [subst -novariables ${paramVal}]
}
TestFunction; # returns "computedDefault"
TestFunction "providedValue"
TestFunction {$test}
It goes without saying that there are also some assumptions behind, which turn into important restrictions depending on one's application case:
You must keep some discipline in using brackets for the defaults in the argument lists, and placing [subst] at the usage sites of the argument variable.
It assumes that you have some control over the arguments, or that you can guarantee that certain special-purpose characters are not valid members of the arguments' value domain.
Watch:
TestFunction {[xxx]}
throws
invalid command name "xxx"
and must be sanitized to
TestFunction {\[xxx\]}

Related

Can we declare a variable in tcl which behave kind of like instance variable in Java i.e. once declared can be accessed anywhere within a class?

I have tried this:
global svcCallWithOneChar ""
if {[catch {set "svcCallWithOneChar = [mimic.list :prefix \"l\"]"} errmsg]} {
puts $errmsg
} else {
puts "###### Svc Call With prefix set to Single Char \n $svcCallWithOneChar \n ##################"
}
but it produces this error
can't read "svcCallWithOneChar =
The reason is: It goes to else condition and then can't access the result stored in svcCallWithOneChar.
If you are doing object-oriented programming in Tcl, there are several options. In TclOO, you define an instance variable like this:
oo::class create Foo {
variable thevariable
...
}
The variable thevariable is now accessible in all methods in all instances of the Foo class.
As for your example code, I can't really figure out what you are trying to do. A couple of notes, though:
The arguments to the global command are variable names to be linked between the global scope and the current scope. Your invocation, if it is inside a procedure, creates two local variables: svcCallWithOneChar and "" (yes, that is a legal variable name).
The invocation set "svcCallWithOneChar = [mimic.list :prefix \"l\"]" is not an assignment. It's the single-argument form of set, so it tries to access the value of a variable named svcCallWithOneChar = foo (if foo is the return value of the mimic.list :prefix \"l\" invocation). If the latter invocation results in the empty string, the variable name becomes svcCallWithOneChar =, which is exactly what your error message says.
Also, even though mimic.list :prefix \"l\" is invoked within a double-quoted string, the text within brackets is not part of the string (the result of the invocation is embedded verbatim in the string, though). So escaping the double quotes in the second argument to mimic.list means that the command gets the argument "l", not l.

How to prevent Tcl from printing the value being set?

A call to a Tcl procedure that sets a variable seems to print the value being set.
% proc a { } { set b "I don't want that!" }
% a
I don't want that!
How to prevent that?
Just add a bare return:
% proc a { } { set b "I don't want that!" ; return }
Note that the value isn't actually printed by the code in question. The set command returns the value, and the a command returns the value of its last executed command. If you run the a command in an interactive tclsh* session, the return value is printed before the next prompt, but if you execute a non-interactively in a script, it won't be printed.
*) or similar, like tkcon.

Tcl upvar issue

Im trying to modify a variable using upvar (in an upward stack), but the value of the variable is passed to the procedure and not the variable name.
I cannot change what is passed, since it is already implemented widely on the program.
Is there a way to modify the file name in some way ?
proc check_file_exists {name} {
upvar $name newName
check_exists $name #Do all checks that file is there
set newName $name_1
}
check_file_exists $name
puts $name
this code will print the old name of the file and not the new one.
What I think you should do is bite the bullet and change the calls. It's a fairly simple search-and-replace, after all. The code will be more sane than if you use any of the other solutions.
check_file_exists name
Or, you could add another parameter to the parameter list and use that to pass the name, making the first argument a dummy argument.
check_file_exists $name name
Or, if you're not using the return value, you could return the new value and assign it back:
set name [check_file_exists $name]
Or, you could assign the new value to a global variable (e.g. theValue) inside the procedure, and assign that back:
check_file_exists $name
# don't need this if you're in global scope
global theValue
set name $theValue
Or, you could assign the name to a global variable (e.g. theName) and access that inside the procedure: the procedure will be able to update name directly.
# don't need this if you're in global scope
global theName
set theName name
check_file_exists $name
(There are some variations on this f.i. using upvar.)
None of the alternatives are pretty, and all of them still require you to make a change at the call (except the last, if you only ever use one variable for this value). If you're adamant about not doing that, there's always Donal's info frame solution, which only requires the procedure itself to be changed.
Let me know if you want help with the procedure code for any of these alternatives.
This is quite difficult; it's really not the way you're supposed to work. But you can do it using info frame -1 (a facility usually used for debugging) to find out exactly how the current procedure was called. However, you need to be careful as the caller might be using the result of a command: this is an unsafe hack.
proc check_file_exists {name} {
set caller [dict get [info frame -1] cmd]
if {[regexp {^check_file_exists +\$(\w+)} $caller -> varName]} {
# OK, we were called with a simple variable name
puts "Called with variable $varName"
} else {
# Complicated case! Help...
return -code error "must be called with a simple variable's contents"
}
upvar 1 $varName newName
check_exists $name
set newName $name_1
}

Why is Tcl variable command always return an empty string?

See code below:
namespace eval foo {
variable bar 5
proc getBar {} {
variable bar
}
proc getRetBar {} {
variable bar
return $bar
}
}
puts "\"[ foo::getBar ]\""
puts "\"[ foo::getRetBar ]\""
exit
It outputs:
""
"5"
Why does it not return the variable value, just like the set command would?
Why does it always return an empty string? If I want to access namespace variables through procedure, and not by accessing them directly, it makes the code slightly longer. Not a major issue, but a little annoying
That's the way the command is defined. It makes sense, because if given an odd number of arguments, the last one is a name that will be declared as a namespace variable but need not exist. If it doesn't exist, what value should variable return?
Still, it's no hassle to write a single-command getter procedure (assuming the variable exists):
proc getBar {} {
set [namespace current]::bar
}
Or you could use a TclOO object (note that you need a setter to initialize it):
oo::object create foo
# -> ::foo
oo::objdefine foo variable bar
oo::objdefine foo method setBar v {set bar $v}
oo::objdefine foo method getBar {} {set bar}
foo setBar 5
# -> 5
puts "\"[foo getBar]\""
# => "5"
You can define the object in one call, if you prefer:
oo::objdefine foo {
variable bar
method setBar v {set bar $v}
method getBar {} {set bar}
}
Documentation: namespace, oo::objdefine, oo::object, variable
The proc command returns an empty string by default. When a procedure is invoked, the procedure's return value is the value specified in a return command. If the procedure does not execute an explicit return, then its return value is the value of the last command executed in the procedure's body.
The fun part here is that variable command never returns any value. It just creates/initializes/modifies a namespace variable.
% namespace eval foo {
variable bar 5
proc getBar {} {
variable bar
}
}
If the variable command is executed inside a Tcl procedure, it creates local variables linked to the corresponding namespace variables and therefore these variables are listed by info vars.
% info vars foo::*
::foo::bar
% set foo::bar; # Getting the 'bar' value
5
% variable foo::bar 10; # Note that it does not return any value as such.
%
% set foo::bar
10
%
Since there is no implicit/explicit return value in the procedure getBar, it is returning the empty string which is the default.
Reference : variable, proc
It works that way because that's how it was implemented and documented, which is to say that it is a relatively arbitrary decision. However it is a decision that occurred nearly 20 years ago, and we see no particular reason to revisit it. Who knows what (OK, probably quite ill-advised) code would be broken?
That said, the main use of variable is with a single argument. What would be the result in this case? It's even used when the variable concerned does not exist: there is no sensible result possible at all, and unlike set it is not an error to do that (it allocates some structures in the namespace if necessary, and binds a local variable if in a procedure). The result you ask for would also make little sense when we have four arguments: why would the last thing to be set be privileged this way? The set command rejects this, and so does not need to deal with the philosophical consequences.
It is better to let variable continue to do what it currently does. It might be longer to write an explicit read as well, but it is also considerably clearer as to what the intention of the code is, and that is a thoroughly good thing over the somewhat longer term.

how to pass string to function in tcl?

I have a function like this:
proc test { {isSet "false"} } {
if {isSet == "true"} {
# do something
}
}
but when I call this proc, how to pass false or true:
test "false" or test false?
You're doing it wrong (ignoring the fact you're using bare word isSet in your if conditional expression which won't work). Instead do
proc test {{isSet "false"}} {
if {$isSet} {
# do something
}
}
This will allow you to call your test proc however you wish:
test 0
test 1
test [expr {1 == 0}]
test yes
test false
test "no"
test {true}
test "off"
etc…
The underlying idea is that the Tcl interpreter will try to interpret the value contained in the isSet variable as a value of a boolean type; there's a set of rules for it to do that:
A value 0 of integer type is false.
A value different from 0 of integer type is true.
Everything else is first converted to a string and then
A string literally equal to one of yes, true or on is true;
A string literally equal to one of no, false or off is false.
The rules applied for interpreting a string as a boolean are described in the Tcl_GetBoolean() manual.
And you're making another mistake in your assumptions: Tcl does not have explicit string and boolean types (as it's typeless) so "false" and false are exactly the same things to the Tcl interpreter at the time it parses your script as double quotes and curly braces are used in Tcl for grouping, not for specifying types of literals (and Tcl does not have literals). So I would recommend to start from the tutorial to get better grip on these ideas.
The types I mentioned when I described interpreting of values as boolean are internal to Tcl values and are not user-visible — yes, internally Tcl caches "native" representation of values so when you do set x [expr {1 + 2}], internally the value assigned to x will have an integer type but this is really an implementation detail.