Luc <l...@sep.invalid> wrote:
> Hi. Here is the code:
Thanks, this is helpful, very helpful.
> ------------------------
> namespace eval AllWidgets {
> namespace eval RightClickMenu {
> set ::[namespace tail [namespace current]] [namespace current]
> puts "set ::[namespace tail [namespace current]] [namespace current]"
> }
> }
> namespace eval CFG {
> namespace eval WidgetConfig {}
> }
>
>
> puts $CFG::WidgetConfig::RightClickMenu
> ------------------------
>
> In the first namespace, AllWidgets, I create a child namespace named
> RightClickMenu. In that child namespace, I create a global variable
> named ::RightClickMenu.
>
> I could probably just ditch that because it's a leftover of the old
> design (I am rewriting and redesigning everything), but I really would
> like to understand what is going on here.
You are not paying full attention to the namespace name resolution
rules (set out in the namespace manual page):
NAME RESOLUTION
In general, all Tcl commands that take variable and command names sup‐
port qualified names. This means you can give qualified names to such
commands as set, proc, rename, and interp alias. If you provide a
fully-qualified name that starts with a ::, there is no question about
what command, variable, or namespace you mean. However, if the name
does not start with a :: (i.e., is relative), Tcl follows basic rules
for looking it up:
• Variable names are always resolved by looking first in the cur‐
rent namespace, and then in the global namespace.
Given your code, the "AllWidgets" does indeed set a global variable.
And it is a fully quailfied global, so none of the name resolution
rules come into play.
But, here (for the code you posted):
> namespace eval CFG::WidgetConfig {
> set RightClickMenu {
> -font "Freesans 16"
> -background "#FFFFFF"
> -foreground "#000000"
> }
> }
When this snippet is executed, CFG::WidgetConfig does not exist
(because none of the posted code created it before), so it contains no
variable names.
You then try to 'set' an unqualified variable name within the namespace
creation script. That invokes the name resolution rules. So what Tcl
does is:
1) looks in current namespace (i.e., CFG::WidgetConfig) for a variable
named "RightClickMenu" and finds the current namespace has no variable
in it of that name (remember, it is empty at this point). So it finds
nothing, and invokes the second half of the "variable name" look rule.
2) it now looks in the global namespace, and it finds "RightClickMenu"
as a global variable in the global namespace. So Tcl updates the value
stored in ::RightClickMenu for you.
You can see this effect if you run your code, one statement at a time,
from a REPL:
$ rlwrap tclsh
% namespace eval AllWidgets {
namespace eval RightClickMenu {
set ::[namespace tail [namespace current]] [namespace current]
puts "set ::[namespace tail [namespace current]] [namespace current]"
}
}
set ::RightClickMenu ::AllWidgets::RightClickMenu
% info globals
tcl_rcFileName tcl_version argv0 argv tcl_interactive RightClickMenu auto_path auto_index env tcl_pkgPath tcl_patchLevel argc tcl_library tcl_platform
% set ::RightClickMenu
::AllWidgets::RightClickMenu
% namespace eval CFG {
namespace eval WidgetConfig {}
}
% namespace eval CFG::WidgetConfig {
set RightClickMenu {
-font "Freesans 16"
-background "#FFFFFF"
-foreground "#000000"
}
}
-font "Freesans 16"
-background "#FFFFFF"
-foreground "#000000"
% puts $CFG::WidgetConfig::RightClickMenu
can't read "CFG::WidgetConfig::RightClickMenu": no such variable
Note I added in an 'info globals' and set ::RightClickMenu just to show
that the variable is now in the global namespace, and to show it's
contents, before running the last two statements.
Now, look what the contents of the global variable are, after the last
two statements:
% set ::RightClickMenu
-font "Freesans 16"
-background "#FFFFFF"
-foreground "#000000"
%
Presumably you want the namespace'ed RightClickMenu to be in that
namespace (implied by your puts of the fully qualified version). In
which case you need to first create the variable in the namespace,
which is the purpose of the "variable" command.
Watch:
$ rlwrap tclsh
% namespace eval AllWidgets {
namespace eval RightClickMenu {
set ::[namespace tail [namespace current]] [namespace current]
puts "set ::[namespace tail [namespace current]] [namespace current]"
}
}
set ::RightClickMenu ::AllWidgets::RightClickMenu
% set ::RightClickMenu
::AllWidgets::RightClickMenu
% namespace eval CFG {
namespace eval WidgetConfig {}
}
% namespace eval CFG::WidgetConfig {
variable RightClickMenu
set RightClickMenu {
-font "Freesans 16"
-background "#FFFFFF"
-foreground "#000000"
}
}
-font "Freesans 16"
-background "#FFFFFF"
-foreground "#000000"
% puts $CFG::WidgetConfig::RightClickMenu
-font "Freesans 16"
-background "#FFFFFF"
-foreground "#000000"
% set ::RightClickMenu
::AllWidgets::RightClickMenu
%
You first have to make the variable 'exist' in the namespace, which is
what the 'variable' command will do for you. Note that the global is
no longer modified in this changed bit of code.