Home > Archive > Tcl > November 2007 > "namespace upvar" question
You are viewing an archived Text-only version of the thread.
To view this thread in it's original format and/or if you want to reply to
this thread please [click here]
| Author |
"namespace upvar" question
|
|
| MartinLemburg@Siemens-PLM 2007-11-23, 4:31 am |
| Hello,
I just tried to use "namespace upvar" in tcl 8.5b1 (ActiveState
distribution) the first time and ... I think I didn't understood its
possibilities.
Following source code fails:
namespace eval ::a {
puts "namespace ::a:"
puts "\tinfo vars ::a::* = [join [info vars ::a::*] {, }]";
puts "\tinfo vars = [join [info vars] {, }]";
puts "\tinfo locals = [join [info locals] {, }]";
puts ""
namespace upvar :: tcl_library tclLib1;
proc a1 {} {
variable tclLib1;
variable tclLib2;
namespace upvar :: tcl_library tclLib2;
puts "proc a1:"
puts "\tinfo vars ::a::* = [join [info vars ::a::*]
{, }]";
puts "\tinfo vars = [join [info vars] {, }]";
puts "\tinfo locals = [join [info locals] {, }]";
puts "\ttclLib1 = '$tclLib1'";
puts "\ttclLib2 = '$tclLib2'";
puts "";
return;
}
proc a2 {} {
variable tclLib1;
variable tclLib2;
upvar #0 ::tcl_library tclLib2;
puts "proc a2:"
puts "\tinfo vars ::a::* = [join [info vars ::a::*]
{, }]";
puts "\tinfo vars = [join [info vars] {, }]";
puts "\tinfo locals = [join [info locals] {, }]";
puts "\ttclLib1 = '$tclLib1'";
puts "\ttclLib2 = '$tclLib2'";
puts "";
return;
}
proc b {} {
variable tclLib1;
variable tclLib2;
puts "proc b:"
puts "\tinfo vars ::a::* = [join [info vars ::a::*]
{, }]";
puts "\tinfo vars = [join [info vars] {, }]";
puts "\tinfo locals = [join [info locals] {, }]";
puts "\ttclLib1 = '$tclLib1'";
puts "\ttclLib2 = '$tclLib2'";
puts "";
return;
}
a1;
puts "namespace ::a:"
puts "\tinfo vars ::a::* = [join [info vars ::a::*] {, }]";
puts "\tinfo vars = [join [info vars] {, }]";
puts "\tinfo locals = [join [info locals] {, }]";
puts "\ttclLib1 = '$tclLib1'";
puts "\ttclLib2 = '$tclLib2'";
puts "";
b;
}
::a::a2;
::a::b;
The output of the source above is:
namespace ::a:
info vars ::a::* =
info vars = tcl_rcFileName, tcl_version, argv0, argv,
tcl_interactive, logFileName, tk_library, application, tk_version,
auto_path, errorCode, backupSchemes, tk_strictMotif, errorInfo,
auto_execs, reason, auto_index, fileName, env, fd, tcl_patchLevel,
backupScheme, showDirExcludes, showInExcludes, cb, argc, version,
schemeFileName, tk_patchLevel, tcl_library, tcl_platform
info locals =
proc a1:
info vars ::a::* = ::a::tclLib2, ::a::tclLib1
info vars = tclLib1, tclLib2
info locals =
tclLib1 = 'c:/Programme/tcl/lib/tcl8.5'
tclLib2 = 'c:/Programme/tcl/lib/tcl8.5'
namespace ::a:
info vars ::a::* = ::a::tclLib2, ::a::tclLib1
info vars = tclLib2, tclLib1, tcl_rcFileName,
tcl_version, argv0, argv, tcl_interactive, logFileName, tk_library,
application, tk_version, auto_path, errorCode, backupSchemes,
tk_strictMotif, errorInfo, auto_execs, reason, auto_index, fileName,
env, fd, tcl_patchLevel, backupScheme, showDirExcludes,
showInExcludes, cb, argc, version, schemeFileName, tk_patchLevel,
tcl_library, tcl_platform
info locals =
tclLib1 = 'c:/Programme/tcl/lib/tcl8.5'
can't read "tclLib2": no such variable
(bin) 726 %
(bin) 726 % ::a::a2;
proc a2:
info vars ::a::* = ::a::tclLib2, ::a::tclLib1
info vars = tclLib1, tclLib2
info locals =
tclLib1 = 'c:/Programme/tcl/lib/tcl8.5'
tclLib2 = 'c:/Programme/tcl/lib/tcl8.5'
(bin) 727 % ::a::b;
proc b:
info vars ::a::* = ::a::tclLib2, ::a::tclLib1
info vars = tclLib1, tclLib2
info locals =
tclLib1 = 'c:/Programme/tcl/lib/tcl8.5'
can't read "tclLib2": no such variable
I expected the variable ::a::tclLib2 to exist, after calling the
procedure ::a::a1.
It seems that in the procedure ::a::a1 the call "variable tclLib2"
declares the typical namespace variable (like usual), but the
"namespace upvar" command doesn't use this "predeclaration" to define
it, right?
But even the procedure ::a::a2 fails using the normal upvar command,
so my question is about some "global" behaviour.
Looking at the results of "info vars" and "info locals", I must
assume, that there never was a local variable tclLib2 inside the
procedures ::a::a1 and ::a::a2.
If there was no local variable tclLib2, I assume, that there must be
the namespace variable tclLib2, right?
Is somebody able to explain this behaviour, or may I overlook some
normal reasons for this?
Best regards,
Martin Lemburg
P.S.: besides of this all ... why does "info vars" return all global
variables, while it is invoked inside the namespace ::a. The man page
of "info vars" tells:
If pattern isn't specified, returns a list of all the names of
currently-visible variables. This includes locals and currently-
visible globals.
If code of a "namespace eval" is evaluated in e.g. "::a", why all the
global variables are "currently-visible"?
| |
| miguel 2007-11-23, 8:11 am |
| Comments interspersed. I believe you misunderstood the purpose of
[namespace upvar] - it is just a code-clarity and performance
optimization: the code
namespace upvar $ns a b c d
does precisely the same thing (except on errors) as
upvar 0 $ns::a b $ns::c d
The variables it creates are within the current scope. If used within a
proc, it will not create variables in the current namespace. Same as
with [global] and [upvar]. It may create variables in the $ns namespace.
MartinLemburg@Siemens-PLM wrote:
> Hello,
>
> I just tried to use "namespace upvar" in tcl 8.5b1 (ActiveState
> distribution) the first time and ... I think I didn't understood its
> possibilities.
>
> Following source code fails:
>
> namespace eval ::a {
> puts "namespace ::a:"
> puts "\tinfo vars ::a::* = [join [info vars ::a::*] {, }]";
> puts "\tinfo vars = [join [info vars] {, }]";
> puts "\tinfo locals = [join [info locals] {, }]";
> puts ""
>
> namespace upvar :: tcl_library tclLib1;
>
> proc a1 {} {
> variable tclLib1;
> variable tclLib2;
There are now two new "locally accessible variables" tclLib1 (linked to
::a::tclLib1) and tclLib2 (linked to ::a::tclLib2)
> namespace upvar :: tcl_library tclLib2;
The local var tclLib2 is now linked to ::tclLib2, the previous link to
::a::tclLib2 is no more.
Note that "destroying any previous link" is the behaviour fo all var
linking commands: global, variable, upvar, nsupvar
> puts "proc a1:"
> puts "\tinfo vars ::a::* = [join [info vars ::a::*]
> {, }]";
> puts "\tinfo vars = [join [info vars] {, }]";
> puts "\tinfo locals = [join [info locals] {, }]";
> puts "\ttclLib1 = '$tclLib1'";
> puts "\ttclLib2 = '$tclLib2'";
> puts "";
>
> return;
> }
>
> proc a2 {} {
> variable tclLib1;
> variable tclLib2;
>
> upvar #0 ::tcl_library tclLib2;
>
> puts "proc a2:"
> puts "\tinfo vars ::a::* = [join [info vars ::a::*]
> {, }]";
> puts "\tinfo vars = [join [info vars] {, }]";
> puts "\tinfo locals = [join [info locals] {, }]";
> puts "\ttclLib1 = '$tclLib1'";
> puts "\ttclLib2 = '$tclLib2'";
> puts "";
>
> return;
> }
>
> proc b {} {
> variable tclLib1;
> variable tclLib2;
>
> puts "proc b:"
> puts "\tinfo vars ::a::* = [join [info vars ::a::*]
> {, }]";
> puts "\tinfo vars = [join [info vars] {, }]";
> puts "\tinfo locals = [join [info locals] {, }]";
> puts "\ttclLib1 = '$tclLib1'";
> puts "\ttclLib2 = '$tclLib2'";
> puts "";
>
> return;
> }
>
> a1;
>
> puts "namespace ::a:"
> puts "\tinfo vars ::a::* = [join [info vars ::a::*] {, }]";
> puts "\tinfo vars = [join [info vars] {, }]";
> puts "\tinfo locals = [join [info locals] {, }]";
> puts "\ttclLib1 = '$tclLib1'";
> puts "\ttclLib2 = '$tclLib2'";
> puts "";
>
> b;
> }
>
> ::a::a2;
> ::a::b;
>
> The output of the source above is:
>
> namespace ::a:
> info vars ::a::* =
> info vars = tcl_rcFileName, tcl_version, argv0, argv,
> tcl_interactive, logFileName, tk_library, application, tk_version,
> auto_path, errorCode, backupSchemes, tk_strictMotif, errorInfo,
> auto_execs, reason, auto_index, fileName, env, fd, tcl_patchLevel,
> backupScheme, showDirExcludes, showInExcludes, cb, argc, version,
> schemeFileName, tk_patchLevel, tcl_library, tcl_platform
> info locals =
>
> proc a1:
> info vars ::a::* = ::a::tclLib2, ::a::tclLib1
> info vars = tclLib1, tclLib2
> info locals =
> tclLib1 = 'c:/Programme/tcl/lib/tcl8.5'
> tclLib2 = 'c:/Programme/tcl/lib/tcl8.5'
>
> namespace ::a:
> info vars ::a::* = ::a::tclLib2, ::a::tclLib1
> info vars = tclLib2, tclLib1, tcl_rcFileName,
> tcl_version, argv0, argv, tcl_interactive, logFileName, tk_library,
> application, tk_version, auto_path, errorCode, backupSchemes,
> tk_strictMotif, errorInfo, auto_execs, reason, auto_index, fileName,
> env, fd, tcl_patchLevel, backupScheme, showDirExcludes,
> showInExcludes, cb, argc, version, schemeFileName, tk_patchLevel,
> tcl_library, tcl_platform
> info locals =
> tclLib1 = 'c:/Programme/tcl/lib/tcl8.5'
> can't read "tclLib2": no such variable
> (bin) 726 %
> (bin) 726 % ::a::a2;
> proc a2:
> info vars ::a::* = ::a::tclLib2, ::a::tclLib1
> info vars = tclLib1, tclLib2
> info locals =
> tclLib1 = 'c:/Programme/tcl/lib/tcl8.5'
> tclLib2 = 'c:/Programme/tcl/lib/tcl8.5'
>
> (bin) 727 % ::a::b;
> proc b:
> info vars ::a::* = ::a::tclLib2, ::a::tclLib1
> info vars = tclLib1, tclLib2
> info locals =
> tclLib1 = 'c:/Programme/tcl/lib/tcl8.5'
> can't read "tclLib2": no such variable
>
> I expected the variable ::a::tclLib2 to exist, after calling the
> procedure ::a::a1.
Wrong expectation
> It seems that in the procedure ::a::a1 the call "variable tclLib2"
> declares the typical namespace variable (like usual), but the
> "namespace upvar" command doesn't use this "predeclaration" to define
> it, right?
> But even the procedure ::a::a2 fails using the normal upvar command,
> so my question is about some "global" behaviour.
Yes, that's the way things work. What are you trying to achieve?
> Looking at the results of "info vars" and "info locals", I must
> assume, that there never was a local variable tclLib2 inside the
> procedures ::a::a1 and ::a::a2.
> If there was no local variable tclLib2, I assume, that there must be
> the namespace variable tclLib2, right?
>
> Is somebody able to explain this behaviour, or may I overlook some
> normal reasons for this?
>
> Best regards,
>
> Martin Lemburg
>
> P.S.: besides of this all ... why does "info vars" return all global
> variables, while it is invoked inside the namespace ::a. The man page
> of "info vars" tells:
>
> If pattern isn't specified, returns a list of all the names of
> currently-visible variables. This includes locals and currently-
> visible globals.
>
> If code of a "namespace eval" is evaluated in e.g. "::a", why all the
> global variables are "currently-visible"?
That is our crazy varname resolving rule - I hate more than most, but it
is what it is: if a variable by some name is not found in the current
namespace but there is a variable by that name in ::, then the global
one is meant. Ie, globals ARE visible.
% set x 0
0
% namespace eval a {set x}
0
% namespace eval a {set x 1}
1
% set x
1
| |
| MartinLemburg@Siemens-PLM 2007-11-23, 8:11 am |
| Hi Miguel,
thanks for your details answer.
First to the "namespace upvar" thing.
Here again a modified source:
namespace eval ::a {
namespace upvar :: tcl_libary tclLib1;
proc a {} {
variable tclLib1;
variable tclLib2;
namespace upvar :: tcl_library ::a::tclLib2;
puts "info locals =[info locals]";
puts "info vars =[info vars]";
puts "info vars ::a::*=[info vars ::a::*]";
puts "tclLib2 =$tclLib2";
puts "::a::tclLib2=$::a::tclLib2";
return;
}
proc b {} {
variable tclLib1;
variable tclLib2;
variable tclLib3;
namespace upvar :: tcl_library tclLib3;
puts "info locals =[info locals]";
puts "info vars =[info vars]";
puts "info vars ::a::*=[info vars ::a::*]";
puts "tclLib3 =$tclLib3";
puts "::a::tclLib3=$::a::tclLib3";
return;
}
}
The purpose of the procedures ::a::a and ::a::b are the same - setting
a namespace variable named tclLib2 and tclLib3.
Both namespace variables are declared at the beginning of their
procedures using the "variable" command.
Afterwards I use "namespace upvar :: tcl_library ..." to "link" the
namespace variable with the global variable "::tcl_libary".
If in the procedure ::a::b exists a local variable tclLib3 instead of
a namespace variable, than why "info locals" does not return it, while
"info vars" returns it?
In my eyes it would make sense to interpete the function of "namespace
upvar" this way:
1. "namespace upvar" takes the first argument as the namespace it
refers to, when linking variables.
2. every first argument of the following argument pairs is a variable
name inside the given namespace
3. every second argument of the following argument pairs is a the
variable name to represent the linked variable in the current scope.
4. if there is already a variable in the current scope, than release
its contents and link the existing variable to the first mentioned
one.
5. don't add a local variable if there is already a namespace variable
accessable in the current scope
=> namespace upvar sourceNamespace ?sourceNamespaceVarName
currentScopeVarName ...?
if a "currentScopeVarName" does already exists in the current scope,
than it's !!contents!! are released and linked to the
"sourceNamespaceVarName".
That's what I expect even from the "namespace upvar" documentation:
"This command arranges for one or more local variables in the current
procedure to refer to variables in namespace. The namespace name is
resolved as described in section NAME RESOLUTION. The command
namespace upvar $ns a b has the same behaviour as upvar 0 $ns::a b,
with the sole exception of the resolution rules used for qualified
namespace or variable names. namespace upvar returns an empty string."
The used variables tclLib2 and tclLib3 are local variables inside the
current procedure, that should refer to the variable tcl_library in
the global namespace!
If I change to use upvar than I expect the next both procedures to
work the same way, except the second, that should link the namespace
variable with the "upvar"ed one:
proc a {} {
upvar #0 ::tcl_library tclLib;
puts "info locals =[info locals]";
puts "info vars =[info vars]";
puts "info vars ::a::*=[info vars ::a::*]";
puts "tclLib=$tclLib";
return;
}
namespace eval ::a {
variable tclLib;
proc a {} {
variable tclLib;
upvar #0 ::tcl_library tclLib;
puts "info locals =[info locals]";
puts "info vars =[info vars]";
puts "info vars ::a::*=[info vars ::a::*]";
puts "tclLib =$tclLib";
puts "::a::tclLib=$::a::tclLib";
return;
}
}
I'm surprised, that this does not work - perhabs because I never did
this before and had now the intelligent idea that this should work.
Is it really only about "shadowing" a namespace variable with a local
variable of the same name? Instead of reusing the already known
(namespace) variable name with "namespace upvar" or "upvar"? Is this
ment by the man page "This command arranges for one or more local
variables in the current procedure ..." with only working with local
variables, but not with namespace variables - relying strictly on the
words of the man page?
And than there is still the question, why "info locals" does not show
this shadowing local variable, but "info vars" shows the shadowed
namespace variable?!
----
Now much shorter to the namespace variable resolution scheme ... I
normally have nothing to be evaluated in namespace bodies, but only in
namespace procedures, so I never stumbled upon the namespace variable
resolution scheme.
And ... I never followed the discussions in the TCLCORE list about
this theme.
But ... its dangerous, really dangerous. It makes it very difficult to
detect/find bugs, if automatically variables of the owning namespace
are visible in the owned namespace, until they are "shadowed" by
variables defined in one of the "sub" namespaces.
The namespace variable resolution scheme make it very easy to access
variables of parent namespaces, but ... I think I don't like it - even
emotional ;).
Best regards,
Martin Lemburg
| |
| miguel 2007-11-23, 7:13 pm |
| MartinLemburg@Siemens-PLM wrote:
> Hi Miguel,
>
> thanks for your details answer.
>
> First to the "namespace upvar" thing.
>
> Here again a modified source:
>
> namespace eval ::a {
> namespace upvar :: tcl_libary tclLib1;
>
> proc a {} {
> variable tclLib1;
> variable tclLib2;
>
> namespace upvar :: tcl_library ::a::tclLib2;
>
> puts "info locals =[info locals]";
> puts "info vars =[info vars]";
> puts "info vars ::a::*=[info vars ::a::*]";
> puts "tclLib2 =$tclLib2";
> puts "::a::tclLib2=$::a::tclLib2";
>
> return;
> }
>
> proc b {} {
> variable tclLib1;
> variable tclLib2;
> variable tclLib3;
>
> namespace upvar :: tcl_library tclLib3;
>
> puts "info locals =[info locals]";
> puts "info vars =[info vars]";
> puts "info vars ::a::*=[info vars ::a::*]";
> puts "tclLib3 =$tclLib3";
> puts "::a::tclLib3=$::a::tclLib3";
>
> return;
> }
> }
>
> The purpose of the procedures ::a::a and ::a::b are the same - setting
> a namespace variable named tclLib2 and tclLib3.
If you want to SET it:
variable tclLib3 ::tcl_library
If you want to LINK IT to the global
namespace eval [namespace current] {upvar #0 tcl_library tclLib3}
or
upvar #0 tcl_library [namespace current]::tclLib3
After having ::a::tclLib3 linked to ::tcl_library, you can bring it into
the proc's scope using
variable tclLib3
> Both namespace variables are declared at the beginning of their
> procedures using the "variable" command.
> Afterwards I use "namespace upvar :: tcl_library ..." to "link" the
> namespace variable with the global variable "::tcl_libary".
That is not what namespace upvar does.
Also "variable" does not declare a variable, it creates a local var
linked to the namespace's. It does not show in [info locals] - ie, it is
a local to all effects BUT info locals, and everything you do to it
EXCEPT linking actually happens to the namespace var.
> If in the procedure ::a::b exists a local variable tclLib3 instead of
> a namespace variable, than why "info locals" does not return it, while
> "info vars" returns it?
By design [info locals] will not return local vars that are linked to
other vars. When the other vars are external this is as it should: to
all effects (but relinking), the variables are external and not local.
OTOH, there may be a bug in there:
% proc x {} {set x 1; upvar 0 x y; info locals}
% x ;# expect "x y"
x
> In my eyes it would make sense to interpete the function of "namespace
> upvar" this way:
>
> 1. "namespace upvar" takes the first argument as the namespace it
> refers to, when linking variables.
> 2. every first argument of the following argument pairs is a variable
> name inside the given namespace
> 3. every second argument of the following argument pairs is a the
> variable name to represent the linked variable in the current scope.
> 4. if there is already a variable in the current scope, than release
> its contents and link the existing variable to the first mentioned
> one.
No. Look:
% proc x {} {set x 1; global x}
% x
variable "x" already exists
All linking commands do the same:
* if the variable already exists as a true-to-$deity variable, refuse to
create a link (user can unset first).
* if the variable is already a link to another variable, delete the
previous link prior to creating the new one
Do not let [namespace upvar] confuse you: it does the same thing (up to
details) as the other commands. The one truly general command in the
family is [upvar]. But look at this:
% set ns ::
::
% namespace eval a {
variable ns [namespace current]
proc x {} {
global ns
puts $ns
variable ns
puts $ns
}
}
% a::x
::
::a
% set ns
::
% set a::ns
::a
> 5. don't add a local variable if there is already a namespace variable
> accessable in the current scope
>
> => namespace upvar sourceNamespace ?sourceNamespaceVarName
> currentScopeVarName ...?
>
> if a "currentScopeVarName" does already exists in the current scope,
> than it's !!contents!! are released and linked to the
> "sourceNamespaceVarName".
>
> That's what I expect even from the "namespace upvar" documentation:
>
> "This command arranges for one or more local variables in the current
> procedure to refer to variables in namespace. The namespace name is
> resolved as described in section NAME RESOLUTION. The command
> namespace upvar $ns a b has the same behaviour as upvar 0 $ns::a b,
> with the sole exception of the resolution rules used for qualified
> namespace or variable names. namespace upvar returns an empty string."
>
> The used variables tclLib2 and tclLib3 are local variables inside the
> current procedure, that should refer to the variable tcl_library in
> the global namespace!
>
> If I change to use upvar than I expect the next both procedures to
> work the same way, except the second, that should link the namespace
> variable with the "upvar"ed one:
>
> proc a {} {
> upvar #0 ::tcl_library tclLib;
>
> puts "info locals =[info locals]";
> puts "info vars =[info vars]";
> puts "info vars ::a::*=[info vars ::a::*]";
> puts "tclLib=$tclLib";
>
> return;
> }
>
> namespace eval ::a {
> variable tclLib;
>
> proc a {} {
> variable tclLib;
>
> upvar #0 ::tcl_library tclLib;
>
> puts "info locals =[info locals]";
> puts "info vars =[info vars]";
> puts "info vars ::a::*=[info vars ::a::*]";
> puts "tclLib =$tclLib";
> puts "::a::tclLib=$::a::tclLib";
>
> return;
> }
> }
>
> I'm surprised, that this does not work - perhabs because I never did
> this before and had now the intelligent idea that this should work.
It never did - IIUC what you are trying to do.
> Is it really only about "shadowing" a namespace variable with a local
> variable of the same name? Instead of reusing the already known
> (namespace) variable name with "namespace upvar" or "upvar"? Is this
> ment by the man page "This command arranges for one or more local
> variables in the current procedure ..." with only working with local
> variables, but not with namespace variables - relying strictly on the
> words of the man page?
>
> And than there is still the question, why "info locals" does not show
> this shadowing local variable, but "info vars" shows the shadowed
> namespace variable?!
[info vars] shows it because it is accessible
[info locals] does not show it because it is isn't really a local var
from the scripting point of view: it is an unqualified name for a
variable that is NOT a local. The fact that it is implemented as a
"local var linked to the other one" should not matter to scripters.
You should interpret upvar and friends (global, variable, ns-upvar) as
saying that
upvar $l foo bar
means "bar is to be interpreted in the current context as another name
(an alias?) for the variable foo in level $l".
If you "relink" later on:
upvar $m moo bar
this means: "from now on bar refers to variable moo in level $m". The
previous meaning is forgotten ...
>
> ----
>
> Now much shorter to the namespace variable resolution scheme ... I
> normally have nothing to be evaluated in namespace bodies, but only in
> namespace procedures, so I never stumbled upon the namespace variable
> resolution scheme.
> And ... I never followed the discussions in the TCLCORE list about
> this theme.
>
> But ... its dangerous, really dangerous. It makes it very difficult to
> detect/find bugs, if automatically variables of the owning namespace
> are visible in the owned namespace, until they are "shadowed" by
> variables defined in one of the "sub" namespaces.
>
> The namespace variable resolution scheme make it very easy to access
> variables of parent namespaces, but ... I think I don't like it - even
> emotional ;).
Dangerous? It is absolutely crazy IMO. It trips even the most careful
coders! Hence:
If you want to set a variable in a namespace, do not do
set x 1
but rather
variable x 1
The reason being that you DO NOT KNOW which x will be set by [set].
Except it you already established that you mean a variable in the
current namespace - having "declared" (rather, created) it with
variable x
| |
| MartinLemburg@Siemens-PLM 2007-11-23, 7:13 pm |
| Thanks for the evening enlightment!
I hopefully understood now a bit more.
So ...
1. namespace eval ::a {proc a {} {variable a}}
creates a link to a namespace variable
2. "namespace upvar" would redefine the link of that variable a,
unlinking the namespace variable, linking the new one
Ok. Hope that's right now.
What makes me wonder is the fact, that linked variables are "external"
variables and that they do not appear in any introspection command!
In the past I tried to determine the type of a variable (global,
namespace, or local) the way the following source does:
proc printVarType {varNames locals globals} {
foreach varName $varNames {
upvar #1 $varName var
if {$varName in $locals} {
set type local;
} elseif {$varName in $globals} {
set type global;
} else {
set type namespace
}
puts " $varName = \"$var\" ($type)";
}
return;
}
set globalVar "global variable";
namespace eval ::a {
variable namespaceVar "namespace variable";
proc a {} {
global globalVar;
variable namespaceVar;
set localVar "local variable (proc [lindex [info level
0]])";
puts "[info level 0]";
puts " vars = [info vars]";
printVarType [info vars] [info globals] [info locals];
return;
}
proc b {} {
global globalVar;
variable namespaceVar;
set localVar "local variable (proc [lindex [info level
0]])";
puts "[info level 0]";
puts " vars = [info vars]";
puts "!! normal !!";
printVarType [info vars] [info globals] [info locals];
puts "!! relinked namespaceVar !!";
namespace upvar :: globalVar namespaceVar;
printVarType [info vars] [info globals] [info locals];
puts " ::a::namespaceVar = \"$::a::namespaceVar\"";
return;
}
}
The output of "::a::a" is the way I expected it in the past:
::a::a
vars = globalVar namespaceVar localVar
globalVar = "global variable" (global)
namespaceVar = "namespace variable" (namespace)
localVar = "local variable (proc ::a::a)" (local)
The output of "::a::b" is now not surprising anymore, but normal,
because I can not distinguish between the known namespace variable and
its now new "link" target:
::a::b
vars = globalVar namespaceVar localVar
!! normal !!
globalVar = "global variable" (global)
namespaceVar = "namespace variable" (namespace)
localVar = "local variable (proc ::a::b)" (local)
!! relinked namespaceVar !!
globalVar = "global variable" (global)
namespaceVar = "global variable" (namespace)
localVar = "local variable (proc ::a::b)" (local)
::a::namespaceVar = "namespace variable"
Reading the man pages, I would say ... to realize that this behaviour
is normal and ok, you need to have deep knowledge about the
backgrounds of upvar, local, global, and namespace variables, the
character of a variable declared via "variable" inside a proc, the
consequence of "relinking", etc.!
The man pages should be more detailed about this!
Just to prevent bugs, that are very nasty and hard to identify!
The code of "::a::b" is in my eyes (not a tcl newbie, even if
sometimes feeling this way) very clear!
It declares a namespace variable to be known inside a proc and links
this namespace variable to another variable.
But in reality the name of the declared namespace variable is unlinked
from the namespace variable and relinked to the other variable.
The mechanisms deep inside are different and so the code must fail,
even if reading the man pages, it should/could work.
IMHO this is not satisfying, but ... ok ... something I know now and
have to live with.
Another question that rises now is ...
....in which way can I introspect "linked" variables or distinguish
correctly them from being not a current-visible namespace variable
inside a procedure?
Best regards and a happy w end!
Martin Lemburg
On Nov 23, 5:42 pm, miguel <mso...@users.sf.net> wrote:
> MartinLemburg@Siemens-PLM wrote:
>
>
>
>
>
>
>
>
>
>
>
>
>
>
> If you want to SET it:
> variable tclLib3 ::tcl_library
>
> If you want to LINK IT to the global
> namespace eval [namespace current] {upvar #0 tcl_library tclLib3}
> or
> upvar #0 tcl_library [namespace current]::tclLib3
> After having ::a::tclLib3 linked to ::tcl_library, you can bring it into
> the proc's scope using
> variable tclLib3
>
>
> That is not what namespace upvar does.
>
> Also "variable" does not declare a variable, it creates a local var
> linked to the namespace's. It does not show in [info locals] - ie, it is
> a local to all effects BUT info locals, and everything you do to it
> EXCEPT linking actually happens to the namespace var.
>
>
> By design [info locals] will not return local vars that are linked to
> other vars. When the other vars are external this is as it should: to
> all effects (but relinking), the variables are external and not local.
>
> OTOH, there may be a bug in there:
>
> % proc x {} {set x 1; upvar 0 x y; info locals}
> % x ;# expect "x y"
> x
>
>
>
> No. Look:
>
> % proc x {} {set x 1; global x}
> % x
> variable "x" already exists
>
> All linking commands do the same:
> * if the variable already exists as a true-to-$deity variable, refuse to
> create a link (user can unset first).
> * if the variable is already a link to another variable, delete the
> previous link prior to creating the new one
>
> Do not let [namespace upvar] confuse you: it does the same thing (up to
> details) as the other commands. The one truly general command in the
> family is [upvar]. But look at this:
>
> % set ns ::
> ::
> % namespace eval a {
> variable ns [namespace current]
> proc x {} {
> global ns
> puts $ns
> variable ns
> puts $ns
> }
> }
> % a::x
> ::
> ::a
> % set ns
> ::
> % set a::ns
> ::a
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
> It never did - IIUC what you are trying to do.
>
>
>
> [info vars] shows it because it is accessible
>
> [info locals] does not show it because it is isn't really a local var
> from the scripting point of view: it is an unqualified name for a
> variable that is NOT a local. The fact that it is implemented as a
> "local var linked to the other one" should not matter to scripters.
>
> You should interpret upvar and friends (global, variable, ns-upvar) as
> saying that
> upvar $l foo bar
> means "bar is to be interpreted in the current context as another name
> (an alias?) for the variable foo in level $l".
>
> If you "relink" later on:
> upvar $m moo bar
> this means: "from now on bar refers to variable moo in level $m". The
> previous meaning is forgotten ...
>
>
>
>
>
>
>
>
>
>
>
> Dangerous? It is absolutely crazy IMO. It trips even the most careful
> coders! Hence:
>
> If you want to set a variable in a namespace, do not do
> set x 1
> but rather
> variable x 1
> The reason being that you DO NOT KNOW which x will be set by [set].
> Except it you already established that you mean a variable in the
> current namespace - having "declared" (rather, created) it with
> variable x- Hide quoted text -
>
> - Show quoted text -- Hide quoted text -
>
> - Show quoted text -- Hide quoted text -
>
> - Show quoted text -
|
|
|
|
|