Set a namespace variable inside a namespace function - tcl

I have a package which uses some variables in lots of its functions. I put it in the namespace eval but then this sets the variable as soon as the package is imported, which I don't want, because its value depends on some things that I set later in my main.tcl.
namespace eval test {
set variable val
}
Therefore, I want something as follows:
test::set_test_globals {} {
set test::variable val
}
But tcl doesn't like this one bit. I tried using upvar but it said the namespace doesn't exist.
How can I fix this?

You can do this with something like this:
namespace eval test {}
proc test::set_test_globals {} {
variable variable val
}
If you use
proc test::set_test_globals_a {} {
set test::variable a
}
variable will be created relative to the test namespace, i.e. as ::test::test::variable, provided the ::test::test namespace exists.
proc test::set_test_globals_b {} {
set ::test::variable b
}
Here, using a qualified (absolute) namespace means that regardless of which namespace set_test_globals_b is in, the variable will be created in the ::test namespace.
But there is a dedicated command, variable, which declares or creates variables in the current namespace without any further namespace specification.
proc test::set_test_globals_c {} {
variable variable c
}
Since the command belongs to the ::test namespace (assuming in all cases here that the proc calls are made in the global namespace and not inside any namespace eval scripts), the variable command ensures that the variable variable is created in the same namespace.
Note that you can also invoke the variable command with only the variable name as argument, in which case the variable is declared but does not yet exist.
proc test::set_test_globals_d {} {
variable variable
# variable does not exist here
set variable d
# variable now exists as ::test::variable
}
If variable variable had not been invoked in the last example, set variable d would have created as a local variable instead.
One common idiom is to use one-argument variable invocations inside a namespace eval to document that the namespace contains those variables:
namespace eval test {
variable variable
# variable does not exist yet
}
proc test::set_test_globals_d {} {
variable variable
# variable still does not exist here
set variable d
# ::test::variable now exists with the value d
}
Note that you still need to invoke variable variable again within each command that will use the namespace variable. The first command to assign a namespace variable a value creates it for the whole namespace and all commands that use it.
The name variable is a valid variable name even though there is a variable command, but it's probably less confusing to use another name for the variable.
For completeness, there is a namespace upvar command that creates links between local variables and namespace variables in much the same manner as upvar can do (i.e. namespace upvar $ns a b is almost, but not quite, the same as upvar 0 ${ns}::a b).
Documentation: namespace, proc, set, upvar, variable

Related

How to refer to a PowerShell function defined in a parent scope?

I'm writing a PowerShell script that runs a couple of background jobs. Some of these background jobs will use the same set of constants or utility functions, like so:
$FirstConstant = "Not changing"
$SecondConstant = "Also not changing"
function Do-TheThing($thing)
{
# Stuff
}
$FirstJob = Start-Job -ScriptBlock {
Do-TheThing $using:FirstConstant
}
$SecondJob = Start-Job -ScriptBlock {
Do-TheThing $using:FirstConstant
Do-TheThing $using:SecondConstant
}
If I wanted to share variables (or, in this case, constants) in child scopes, I'd prefix the variable references with $using:. I can't do that with functions, though; running this code as-is returns an error:
The term 'Do-TheThing' is not recognized as the name of a cmdlet, function, script file, or operable program.
My question is this: How can my background jobs use a small utility function that I've defined in a higher scope?
If the function in the higher scope is in the same (non-)module scope in the same session, your code implicitly sees it, due to PowerShell's dynamic scoping.
However, background jobs run in a separate process (child process), so anything from the caller's scope must be passed explicitly to this separate session.
This is trivial for variable values, with the $using: scope, but less obvious for functions, but it can be made to work with a bit of duplication, by passing a function's body via namespace variable notation:
# The function to call from the background job.
Function Do-TheThing { param($thing) "thing is: $thing" }
$firstConstant = 'Not changing'
Start-Job {
# Define function Do-TheThing here in the background job, using
# the caller's function *body*.
${function:Do-TheThing} = ${using:function:Do-TheThing}
# Now call it, with a variable value from the caller's scope
Do-TheThing $using:firstConstant
} | Receive-Job -Wait -AutoRemoveJob
The above outputs 'thing is: Not changing', as expected.

TclOO : What is the difference between my and self?

The documentation probably explains it very well but I do not see the difference between this 2 commands in my case :
method dir {} {
puts "method dir..."
}
method pseudomethod {} {
set vardir [my dir]
set vardir [[self] dir]
}
The only difference I can see is that with [self] I can pass it as an argument in a procedure and not with my.
What is the best solution in my case ?
Both solutions have equal performance ?
The self command (with no extra arguments) is equivalent to self object which returns the current public name of the object that is executing the method (you can rename the object). The self command overall provides access to bits of “runtime” state.
The my command is actually the object's internal name; it's created in each object's instance namespace. You can invoke all exported and non-exported methods via my, unlike with the public name. This makes it useful for both calling your internal methods directly, and also for setting up things like callbacks to internal methods (you'll need something like namespace which or namespace code when setting up the callback).
Unlike with the public name, you can delete the internal name command without automatically destroying the object. It'll likely break code (your methods most probably) if you do that, but the base system allows you to do it.
Aside: Tcl 8.7 includes this helper procedure (which also works in 8.6) for creating callback scripts within methods (the funny name means it gets mapped into your methods automatically as callback):
proc ::oo::Helpers::callback {method args} {
list [uplevel 1 {::namespace which my}] $method {*}$args
}
In this case, if the callback was exported, you'd be able to do this instead:
proc ::oo::Helpers::callback {method args} {
list [uplevel 1 self] $method {*}$args
}
but that would be more vulnerable to rename problems. (In all cases, the uplevel 1 is because we want to run a little bit of name-resolving code in the calling context, not inside the scope of the procedure itself.)
I'm not sure how they are implemented, but one reason you'd want to use my is to access non-exported (private) methods. A demo:
oo::class create Foo {
method PrivateMethod {} {puts "this is PrivateMethod"}
method publicMethod {} {puts "this is publicMethod"}
method test {} {
my publicMethod
my PrivateMethod
[self] publicMethod
[self] PrivateMethod
}
}
then:
% Foo create foo
::foo
% foo test
this is publicMethod
this is PrivateMethod
this is publicMethod
unknown method "PrivateMethod": must be destroy, publicMethod or test
my is the mechanism for an object to invoke its methods.
self is the mechanism for introspection on how the current method was called.
Spend some time with the my and self man pages.

How can a Tcl procedure access dozens of constants outside it?

A tcl script has dozens of procedures and is used to aid in debug of a hardware system. There are several procedures inside this script that need to access constants outside the procedures. At the moment every procedure uses upvar keyword to basically enable the procedure to access the constants declared outside it. However, this seems to clutter the code. Is there any other way that the procedures can access the constants outside it that do not use upvar and do not clutter the code as well? Is declaring them as global the only other way?
Different procedures share same constants, so I cannot just put them inside the procedures themself.
Any variable declared as global is in the global namespace and can be
accessed using the :: prefix.
proc a { } {
global va
set va aa
}
a
puts $::va
Variables declared within a namespace can be access by prefixing the variable
with the namespace.
namespace eval nn {
variable nnb
proc init {} {
variable nnb
set nnb bb
}
init
}
puts $::nn::nnb
Essentially, any variable that is not a local variable is part of a namespace,
the global namespace (::) or some other namespace and can be accessed with
the fully qualified variable name.
To avoid clutter, you can also use arrays to hold constants and variables.
I find a single variable vars much easier to work with than a very long list
of global declarations.
set const(pi) 3.14151927
set const(unary) 1
set const(nil) 0
proc mydebug { } {
puts $::const(pi)
}
or
proc mydebug { } {
global const
puts $const(pi)
}
See Brad Lanam's answer for most of what I would have otherwise said.
If you have a lot of constants, put them in the global array with the empty name.
# Initialise constants
array set {} {
pi 3.141
e 2.718
}
Then you can access them all from a procedure like this:
proc calculate {x} {
global {}; # Bring the constants into visibility
return [expr {$(pi) * $(e) ** $x}]
}
This keeps using a constant relatively low on syntax.

How to resource the itcl classes to without starting a tcl shell

With this Tcl script: A.tcl
itcl::class ::A {
variable list1 {}
public method getList {} {
return $list1
}
}
I do this:
Start the tcl shell and interactively do source A.tcl
then make changes to getList method in A.tcl
To make the changes effective, I do re-source the file A.tcl
When I re-source, I get the following error
% source /home/manid/scripts/test.tcl
class "A" already exists
How can i overcome this error? Is there a way to get the latest changes in the class definition without exiting the shell?
You need to write your code somewhat differently. In particular, you have to put the definitions of the body of the methods (which can be repeated) outside the declaration of the class (which can't be repeated). Then, you do a conditional class creation (with itcl::is class as the tester) and use itcl::body to actually provide the method definitions.
According to these principles, rewriting your A.tcl to be:
if {![itcl::is class ::A]} {
itcl::class ::A {
variable list1 {}
# *Dummy* method body; method must exist, but may be empty
public method getList {} {}
}
}
itcl::body ::A::getList {} {
return $list1
}
would allow you to source that multiple times to change the method bodies however you wanted. This doesn't give you freedom to change everything (e.g., the variable declarations and scope rules are fixed); you need to switch to something like TclOO to get that sort of flexibility.

TclOO class inside namespace: calling namespace procs give errors

I'm experimenting a little with TclOO from Tcl8.6 and Rivet, but I'm in trouble, because I'm not able to do what I want.
The problem is simply reproduceable with the following code inside a .rvt file:
<?
proc dumbproc {} {
puts "this is dumbproc ([namespace current])"
}
oo::class create foo {
method bar {} {
puts "this is bar ([namespace current])"
dumbproc
}
}
set obj [foo new]
dumbproc
$obj bar
If I simply look at the code, it seems it should work as expected, but it really doesn't because a subtle behavior of the Rivet package and the specific configuration choosen.
In this example, I'm using a .rvt file whose code is executed inside the ::request namespace, so the fully qualified name of the dumbproc procedure is ::request::dumbproc. When the name resolution algorithm is called inside the bar method, it searches for dumbproc inside ::oo::Obj12, then in ::oo, and finally in ::, without finding it and giving the following error.
this is dumbproc (::request) this is bar (::oo::Obj16)
invalid command name "dumbproc"
while executing
"dumbproc"
(class "::request::foo" method "bar" line 3)
invoked from within
"$obj bar"
(in namespace eval "::request" script line 21)
invoked from within
"namespace eval request {
puts -nonewline ""
proc dumbproc {} {
puts "this is dumbproc ([namespace current])"
}
oo::class create..."
So, Tcl is right in doing what it does, a feature, then. But the behavior is unpredictable because when you write some class code, you must know the context it will be used in.
Indeed, I get the same error if I drop the starting <? Rivet magic, put the code inside a test.tcl file and use it in an interactive session:
$ tclsh
% namespace eval ::myns {source test.tcl}
this is dumbproc (::myns)
this is bar (::oo::Obj12)
invalid command name "dumbproc"
I tried to solve the issue by prepending the current namespace to the class creation code
::oo::class create [namespace current]::foo { ... }
then, I also tried to create the obj object inside the namespace
::oo::class create [namespace current]::foo { ... }
namespace eval [namespace current] {set obj [[namespace current]::foo new]}
then, I switched to the create method of the class for giving the object a qualified name which includes the namespace
foo create [namespace current]::obj
obj bar
but everything was unsuccessful. Every trial shows that, no matter how I do it, a method inside a TclOO class is always executed inside its object unique namespace. Am I wrong?
Is there a way to get what I want? Is TclOO not intended to work that way, and in this case why? What really surprise me is this context-dependend behavior, which I'm not sure it's the right thing, but maybe I'm completely wrong and there are sound cases for it, which I'm missing.
The interior of each TclOO object is in reality its own namespace. You can use self namespace or namespace current inside your methods to get the name of the namespace, or info object namespace $theobj to get the namespace from anywhere. The only command placed by default in the namespace is my (for calling private methods), and some commands in other namespaces are made available through the standard Tcl namespace path mechanism (this is how you get self and next available).
The simplest way to fix this would probably be to add this to the foo class's constructor:
namespace path [list {*}[namespace path] ::request]
In your specific case, you'd have to actually add a constructor...
constructor {} {
namespace path [list {*}[namespace path] ::request]
# If you had a non-trivial constructor in a superclass, you'd need to call
# [next] too.
}
Longer term, it may be reasonable to ask for a mechanism for adding to the list of namespaces that are used to make the default for objects of a class. If you want that, do submit a feature request…
[EDIT]: If you're just after adding the parent namespace to the current object's command resolution path, you can do that by adding a bit more magic:
oo::class create foo {
self {
method create args {
set ns [uplevel 1 {namespace current}]
next {*}[linsert $args 1 $ns]
}
method new args {
set ns [uplevel 1 {namespace current}]
next {*}[linsert $args 0 $ns]
}
}
constructor {creatorNS args} {
namespace path [list {*}[namespace path] $creatorNS]
}
method bar {} {
puts "this is bar ([namespace current])"
dumbproc
}
}
That will then automatically put the current namespace at creation on the path of the instance. If you're doing this in many classes, you probably want to create a metaclass with the majority of machinery in it, but the above technique (a self declaration of some methods on the foo class object itself) works fine for simple cases.