I have a puzzle figuring out what's happening in some test code.
I have this code:
#------------------
proc test {args} {
namespace eval foobar {
set foo 123
if [catch {
puts "1 foo= |$foo|"
puts "2 ::foobar::foo= |$::foobar::foo|"
} err_code] {
puts "3 $err_code"
}
}
}
set ::foo 999 ;# what is this doing to foo in the proc
test
if [catch {
puts "4 foo= |$foo| "
} err_code] {
puts "5 $err_code"
}
#------------------
output:
-------------
1 foo= |123|
3 can't read "::foobar::foo": no such variable
4 foo= |123|
-------------
If however, I comment out setting foo to 999 outside i.e.
...
#set ::foo 999
test
...
I then get this:
--------------
1 foo= |123|
2 ::foobar::foo= |123|
5 can't read "foo": no such variable
--------------
So,... why is having a global foo variable affecting foo inside a
namespace inside a proc, and why is that affecting the global version of
foo, from within the proc inside the namespace?
set ::foo 999 ;# what is this doing to foo in the procwhen executing the [namespace eval foobar] argument the variable is not
test
when executing the [namespace eval foobar] argument the variable is not
yet created in the current (::foobar) namespace and is then looked up
in the global namespace. Since is found there, the variable which is set
with the value of "123" is the global one.
On Mon, 11 Oct 2021 15:26:41 -0700
ted brown <tedbrown888@gmail.com> wrote:
I have a puzzle figuring out what's happening in some test code.
I have this code:
#------------------
proc test {args} {
namespace eval foobar {
set foo 123
if [catch {
puts "1 foo= |$foo|"
puts "2 ::foobar::foo= |$::foobar::foo|"
} err_code] {
puts "3 $err_code"
}
}
}
set ::foo 999 ;# what is this doing to foo in the proc
test
if [catch {
puts "4 foo= |$foo| "
} err_code] {
puts "5 $err_code"
}
#------------------
output:
-------------
1 foo= |123|
3 can't read "::foobar::foo": no such variable
4 foo= |123|
-------------
If however, I comment out setting foo to 999 outside i.e.
...
#set ::foo 999
test
...
I then get this:
--------------
1 foo= |123|
2 ::foobar::foo= |123|
5 can't read "foo": no such variable
--------------
So,... why is having a global foo variable affecting foo inside a
namespace inside a proc, and why is that affecting the global version of
foo, from within the proc inside the namespace?
The answer is in namespace(n) man page, section "NAME RESOLUTION".
In short, variables are resolved first in the current namespace;
if not found there, they are looked up in the global namespace.
So when you create the variable first in the global namespace with
set ::foo 999 ;# what is this doing to foo in the procwhen executing the [namespace eval foobar] argument the variable is not
test
yet created in the current (::foobar) namespace and is then looked up
in the global namespace. Since is found there, the variable which is set
with the value of "123" is the global one.
The opposite happens when you comment out the global variable, so when
the [namespace eval foobar] argument is evaluated there's no global
::foo variable, so one is created inside the ::foobar namespace.
This is a well known variable resolution *design* bug, addressed by
tip#278
https://core.tcl-lang.org/tips/doc/trunk/tip/278.md
Also, note that having the [namespace eval] code inside a proc body
is of no consequence here. The same will behave whether is put at
top level or inside a proc, since namespaces are resolved by looking
only at the current namespace, which for a "::test" proc is "::".
Regards
Sysop: | Keyop |
---|---|
Location: | Huddersfield, West Yorkshire, UK |
Users: | 285 |
Nodes: | 16 (3 / 13) |
Uptime: | 32:04:29 |
Calls: | 6,449 |
Calls today: | 1 |
Files: | 12,052 |
Messages: | 5,254,774 |