• namespace inside a proc puzzle

    From ted brown@21:1/5 to All on Mon Oct 11 15:26:41 2021
    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?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Emiliano Gavilan@21:1/5 to ted brown on Tue Oct 12 08:09:52 2021
    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 proc
    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.
    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

    --
    Emiliano

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Schelte@21:1/5 to Emiliano Gavilan on Tue Oct 12 15:58:15 2021
    On 12/10/2021 13:09, Emiliano Gavilan wrote:
    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.

    And the way to fix it is to use the [variable] command:

    namespace eval foobar {
    variable foo 123
    }


    Schelte

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From ted brown@21:1/5 to Emiliano Gavilan on Tue Oct 12 09:31:00 2021
    On 10/12/2021 4:09 AM, Emiliano Gavilan wrote:
    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 proc
    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.
    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


    Thanks for the detailed explanation. I didn't know if I'd found a code
    bug or what.

    This came up when I was trying to figure out how to "import" arbitrary
    code, especially some TclOO classes and methods into a thread's
    interpreter using introspection. I looked into Ttrace, but that doesn't
    seem to do classes.

    So, I was trying to wrap code in a proc, since I *do* know how to do
    that for a proc using a reconstructer I found in Ashok's tcl book. Then
    inside the thread I would just run the proc. This doesn't appear like it
    will work.

    What I'm now thinking is this:

    proc importer {

    .... any code ...

    }

    use reconstructer and get the proc "importer" into the thread, then

    eval [info body importer]

    I think maybe that could work, but I'll have to do some more testing to
    see what pitfalls that might present.

    Thanks for solving my puzzle!

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)