• Does bind operate in a separate scope?

    From Luc@21:1/5 to All on Wed Sep 21 21:06:06 2022
    I guess I never learned about this, and I need to:

    set foo "bar"
    puts "test 1 $foo"
    bind $::w <Control_L><s> {puts "test 2 $foo"}

    Test 1 prints "bar", but test 2 won't. An error says there is no "foo" variable.

    Does bind operate in a separate scope?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Ralf Fassel@21:1/5 to All on Thu Sep 22 10:15:54 2022
    * Luc <[email protected]>
    | I guess I never learned about this, and I need to:

    | set foo "bar"
    | puts "test 1 $foo"
    | bind $::w <Control_L><s> {puts "test 2 $foo"}

    | Test 1 prints "bar", but test 2 won't. An error says there is no "foo" variable.

    | Does bind operate in a separate scope?

    The code you show does not run because $::w is not defined.

    You need to post a *complete* example¹ that shows the error, otherwise
    anyone wanting to help needs to guess what might be wrong, and many of
    the regulars skip the post right at that point.

    And, my experience is that while preparing the complete example, many
    times I stumble upon the solution.

    In your case, most probably the code you show is inside a proc, so foo
    is defined only inside that proc. As the bind manpage

    https://www.tcl-lang.org/man/tcl/TkCmd/bind.htm

    explains:

    BINDING SCRIPTS AND SUBSTITUTIONS
    [...]
    Command will be executed in the same interpreter that the bind
    command was executed in, and it will run at global level (only global
    variables will be accessible).

    HTH
    R'
    ---
    ¹ http://www.catb.org/~esr/faqs/smart-questions.html#beprecise

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Luc@21:1/5 to Ralf Fassel on Thu Sep 22 08:24:44 2022
    On Thursday, September 22, 2022 at 5:16:00 AM UTC-3, Ralf Fassel wrote:
    * Luc
    | I guess I never learned about this, and I need to:

    | set foo "bar"
    | puts "test 1 $foo"
    | bind $::w <Control_L><s> {puts "test 2 $foo"}

    | Test 1 prints "bar", but test 2 won't. An error says there is no "foo" variable.

    | Does bind operate in a separate scope?
    The code you show does not run because $::w is not defined.

    You need to post a *complete* example¹ that shows the error, otherwise anyone wanting to help needs to guess what might be wrong, and many of
    the regulars skip the post right at that point.

    And, my experience is that while preparing the complete example, many
    times I stumble upon the solution.

    In your case, most probably the code you show is inside a proc, so foo
    is defined only inside that proc. As the bind manpage

    https://www.tcl-lang.org/man/tcl/TkCmd/bind.htm

    explains:

    BINDING SCRIPTS AND SUBSTITUTIONS
    [...]
    Command will be executed in the same interpreter that the bind
    command was executed in, and it will run at global level (only global variables will be accessible).

    HTH
    R'
    ---
    ¹ http://www.catb.org/~esr/faqs/smart-questions.html#beprecise

    I am sorry if I wasn't sufficiently clear. I believe the entire code is too large to be posted here.

    But I am prone to believing that the information I submitted is clear enough.

    Let me change it just a little:

    set file "/home/luc/somefile.txt"
    puts "test $file"
    bind $::w <Control_L><o> {proc.openfile $file}

    The proc fails to open the file. It complains that the "file" variable doesn't exist.

    And this works:

    bind $::w <Control_L><o> {proc.openfile /home/me/somefile.txt}

    Why?

    What confuses me is that yes, the variable exists. The puts command right before it proves that. So in my head, {proc.openfile $file} should translate directly into {proc.openfile /home/me/somefile.txt} and that message should be received by bind and
    proc.openfile accordingly. That is my problem. Some shift in scope is happening here that I don't understand. I replaced the intended command with [info vars] and realized that only global variables are available, $foo is not available although it can be
    found and printed only one line before, and I could solve the whole problem right now by just using a global variable. But I want more than that. I want to learn. I want to know why Tcl is not doing what I thought it would.

    So the documentation says, as you pointed out, that "the bind command was executed in, and it will run at global level (only global variables will be accessible)"

    OK, now I know that, but I still find it confusing. Programming languages usually like to provide for safety mechanisms, the opposite of this "only globals allowed" decision. Globals tend to be seen as unsafe so I wouldn't expect them to be compulsory
    for passing arguments.

    But OK, I guess the case is closed. Thank you for your attention.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Gerald Lester@21:1/5 to Luc on Thu Sep 22 10:59:24 2022
    On 9/22/22 10:24, Luc wrote:
    On Thursday, September 22, 2022 at 5:16:00 AM UTC-3, Ralf Fassel wrote:
    * Luc
    | I guess I never learned about this, and I need to:

    | set foo "bar"
    | puts "test 1 $foo"
    | bind $::w <Control_L><s> {puts "test 2 $foo"}

    | Test 1 prints "bar", but test 2 won't. An error says there is no "foo" variable.

    | Does bind operate in a separate scope?
    The code you show does not run because $::w is not defined.

    You need to post a *complete* example¹ that shows the error, otherwise
    anyone wanting to help needs to guess what might be wrong, and many of
    the regulars skip the post right at that point.

    And, my experience is that while preparing the complete example, many
    times I stumble upon the solution.

    In your case, most probably the code you show is inside a proc, so foo
    is defined only inside that proc. As the bind manpage

    https://www.tcl-lang.org/man/tcl/TkCmd/bind.htm

    explains:

    BINDING SCRIPTS AND SUBSTITUTIONS
    [...]
    Command will be executed in the same interpreter that the bind
    command was executed in, and it will run at global level (only global
    variables will be accessible).

    HTH
    R'
    ---
    ¹ http://www.catb.org/~esr/faqs/smart-questions.html#beprecise

    I am sorry if I wasn't sufficiently clear. I believe the entire code is too large to be posted here.

    But I am prone to believing that the information I submitted is clear enough.

    Let me change it just a little:

    set file "/home/luc/somefile.txt"
    puts "test $file"
    bind $::w <Control_L><o> {proc.openfile $file}

    The proc fails to open the file. It complains that the "file" variable doesn't exist.

    Sorry, but again you are hiding stuff! The following works:


    gerald@gerald-laptop:/tmp/test$ cat scope.tcl
    proc proc.openfile {f} {
    puts "In proc with f == {$f}"
    }

    set w [entry .e]
    grid configure $w
    set file "/home/luc/somefile.txt"
    bind $::w <Control_L> {proc.openfile $file}

    gerald@gerald-laptop:/tmp/test$ wish scope.tcl
    In proc with f == {/home/luc/somefile.txt}

    As stated in the man page -- binds are executed at the global level.
    That means that only global variables are visible.


    And this works:

    bind $::w <Control_L><o> {proc.openfile /home/me/somefile.txt}

    Why?

    What confuses me is that yes, the variable exists. The puts command right before it proves that. So in my head, {proc.openfile $file} should translate directly into {proc.openfile /home/me/somefile.txt} and that message should be received by bind and
    proc.openfile accordingly. That is my problem. Some shift in scope is happening here that I don't understand. I replaced the intended command with [info vars] and realized that only global variables are available, $foo is not available although it can be
    found and printed only one line before, and I could solve the whole problem right now by just using a global variable. But I want more than that. I want to learn. I want to know why Tcl is not doing what I thought it would.

    So the documentation says, as you pointed out, that "the bind command was executed in, and it will run at global level (only global variables will be accessible)"

    OK, now I know that, but I still find it confusing. Programming languages usually like to provide for safety mechanisms, the opposite of this "only globals allowed" decision. Globals tend to be seen as unsafe so I wouldn't expect them to be compulsory
    for passing arguments.

    But OK, I guess the case is closed. Thank you for your attention.




    --
    +----------------------------------------------------------------------+
    | Gerald W. Lester, President, KNG Consulting LLC |
    | Email: [email protected] | +----------------------------------------------------------------------+

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Ralf Fassel@21:1/5 to All on Thu Sep 22 18:21:59 2022
    * Luc <[email protected]>
    | puts "test $file"
    | bind $::w <Control_L><o> {proc.openfile $file}
    --<snip-snip>--
    | What confuses me is that yes, the variable exists. The puts command
    | right before it proves that. So in my head, {proc.openfile $file}
    | should translate directly into {proc.openfile /home/me/somefile.txt}

    Read about the effect of {} on variable substitution in

    https://www.tcl.tk/man/tcl/TclCmd/Tcl.html#M10

    [6] Braces.
    ...
    No substitutions are performed on the characters between the
    braces
    ...

    So in your code the $file is not substituted when the bind runs, because
    of the {} surrounding it.

    Rather when the bind *triggers*, the script is evaluated in global
    scope, and it is _there_ that the variable does not exist.

    Note: if you want to substitute $file right at that point (rather than
    when the binding triggers), use list:
    bind $::w <Control_L> [list proc.openfile $file]

    Another note:
    If the whole code is too large to show, strip it down to a minimal
    example which shows the error - and in that process of stripping down
    you will in many cases find the problem yourself.

    HTH
    R'

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Robert Heller@21:1/5 to Luc on Thu Sep 22 16:29:17 2022
    At Thu, 22 Sep 2022 08:24:44 -0700 (PDT) Luc <[email protected]> wrote:


    On Thursday, September 22, 2022 at 5:16:00 AM UTC-3, Ralf Fassel wrote:
    * Luc
    | I guess I never learned about this, and I need to:

    | set foo "bar"
    | puts "test 1 $foo"
    | bind $::w <Control_L><s> {puts "test 2 $foo"}

    | Test 1 prints "bar", but test 2 won't. An error says there is no "foo" variable.

    | Does bind operate in a separate scope?
    The code you show does not run because $::w is not defined.

    You need to post a *complete* example¹ that shows the error, otherwise anyone wanting to help needs to guess what might be wrong, and many of
    the regulars skip the post right at that point.

    And, my experience is that while preparing the complete example, many
    times I stumble upon the solution.

    In your case, most probably the code you show is inside a proc, so foo
    is defined only inside that proc. As the bind manpage

    https://www.tcl-lang.org/man/tcl/TkCmd/bind.htm

    explains:

    BINDING SCRIPTS AND SUBSTITUTIONS
    [...]
    Command will be executed in the same interpreter that the bind
    command was executed in, and it will run at global level (only global variables will be accessible).

    HTH
    R'
    ---
    ¹ http://www.catb.org/~esr/faqs/smart-questions.html#beprecise

    I am sorry if I wasn't sufficiently clear. I believe the entire code is too large to be posted here.

    But I am prone to believing that the information I submitted is clear enough.

    It really wasn't.


    Let me change it just a little:

    set file "/home/luc/somefile.txt"
    puts "test $file"
    bind $::w <Control_L><o> {proc.openfile $file}

    The proc fails to open the file. It complains that the "file" variable doesn't exist.

    That is probablly because it does not exist *in the global scope*. Question: is the code snippet inside of a proc? If it is, it won't work.


    And this works:

    bind $::w <Control_L><o> {proc.openfile /home/me/somefile.txt}

    Why?

    What confuses me is that yes, the variable exists. The puts command right before it proves that. So in my head, {proc.openfile $file} should translate directly into {proc.openfile /home/me/somefile.txt} and that message should be received by bind and proc.openfile accordingly. That is my problem. Some shift in scope is happening here that I don't understand. I replaced the intended command with [info vars] and realized that only global variables
    are available, $foo is not available although it can be found and printed only one line before, and I could solve the whole problem right now by just using a global variable. But I want more than that. I want to learn. I want to know why Tcl is not doing what I thought it would.

    So the documentation says, as you pointed out, that "the bind command was executed in, and it will run at global level (only global variables will be accessible)"

    OK, now I know that, but I still find it confusing. Programming languages usually like to provide for safety mechanisms, the opposite of this "only globals allowed" decision. Globals tend to be seen as unsafe so I wouldn't expect them to be compulsory for passing arguments.

    What you want to do is create some sort of "closure" type of thing (something that "remembers" the current scope or contex) if you want to avoid using globals -- there are a number of "hacks" to do this, including several of the OOP frameworks that are available. Another option would be to use variables in namespaces. Or both.

    What is going on here is that the event loop itself runs in the global level. *bind* itself never actually calls anything at any level. It just creates a event object and adds it to the event list. The event object has a mask for the event(s) and possibly the object the event relates to. The event loop loops through the event list and when an element has a matching event, the
    code is then passed to eval (Tcl_Eval), at the current (global) level.


    But OK, I guess the case is closed. Thank you for your attention.



    --
    Robert Heller -- Cell: 413-658-7953 GV: 978-633-5364
    Deepwoods Software -- Custom Software Services
    http://www.deepsoft.com/ -- Linux Administration Services
    [email protected] -- Webhosting Services

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From LES@21:1/5 to Ralf Fassel on Thu Sep 22 15:33:40 2022
    **************************
    On Thu, 22 Sep 2022 18:21:59 +0200, Ralf Fassel wrote:

    Read about the effect of {} on variable substitution in

    https://www.tcl.tk/man/tcl/TclCmd/Tcl.html#M10

    [6] Braces.
    ...
    No substitutions are performed on the characters between the
    braces
    ...

    So in your code the $file is not substituted when the bind runs,
    because of the {} surrounding it.

    Rather when the bind *triggers*, the script is evaluated in global
    scope, and it is _there_ that the variable does not exist.

    Note: if you want to substitute $file right at that point (rather than
    when the binding triggers), use list:
    bind $::w <Control_L> [list proc.openfile $file]


    Ah, yes! That really makes sense now. Total forehead slapping moment.
    Of course I know about variable substitution, but I wasn't making the connection with the hard brackets. I tried using list and now it works
    just as expected. Double quotes works too.

    Now it makes sense.

    Thank you!

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Robert Heller@21:1/5 to [email protected] on Thu Sep 22 19:29:25 2022
    At Thu, 22 Sep 2022 15:33:40 -0300 LES <[email protected]> wrote:


    **************************
    On Thu, 22 Sep 2022 18:21:59 +0200, Ralf Fassel wrote:

    Read about the effect of {} on variable substitution in

    https://www.tcl.tk/man/tcl/TclCmd/Tcl.html#M10

    [6] Braces.
    ...
    No substitutions are performed on the characters between the
    braces
    ...

    So in your code the $file is not substituted when the bind runs,
    because of the {} surrounding it.

    Rather when the bind *triggers*, the script is evaluated in global
    scope, and it is _there_ that the variable does not exist.

    Note: if you want to substitute $file right at that point (rather than
    when the binding triggers), use list:
    bind $::w <Control_L> [list proc.openfile $file]


    Ah, yes! That really makes sense now. Total forehead slapping moment.
    Of course I know about variable substitution, but I wasn't making the connection with the hard brackets. I tried using list and now it works
    just as expected. Double quotes works too.

    Warning: Double quotes are meant for strings and might not handle "quoting" correctly. When building a command/script to be passed off to bind, etc.
    using list is always prefered, since proper quoting will occur.


    Now it makes sense.

    Thank you!




    --
    Robert Heller -- Cell: 413-658-7953 GV: 978-633-5364
    Deepwoods Software -- Custom Software Services
    http://www.deepsoft.com/ -- Linux Administration Services
    [email protected] -- Webhosting Services

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From [email protected]@21:1/5 to All on Thu Sep 22 19:26:58 2022
    connection with the hard brackets. I tried using list and now it works
    just as expected. Double quotes works too.

    Be careful about using double quotes.
    They work until someone has a file name with a space (C:\My Data),
    when it will fail. Using [list ...] is much safer.


    Dave B

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Ralf Fassel@21:1/5 to All on Thu Sep 22 23:21:18 2022
    * LES <[email protected]>
    | > Note: if you want to substitute $file right at that point (rather than
    | > when the binding triggers), use list:
    | > bind $::w <Control_L> [list proc.openfile $file]

    | Ah, yes! That really makes sense now. Total forehead slapping moment.

    :-)

    | I tried using list and now it works just as expected. Double quotes
    | works too.

    Note that double quotes will fail if your file name contains spaces.
    Always use [list] to build callbacks or -command scripts.

    R'

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Gerald Lester@21:1/5 to LES on Fri Sep 23 07:46:13 2022
    On 9/22/22 13:33, LES wrote:
    **************************
    On Thu, 22 Sep 2022 18:21:59 +0200, Ralf Fassel wrote:

    Read about the effect of {} on variable substitution in

    https://www.tcl.tk/man/tcl/TclCmd/Tcl.html#M10

    [6] Braces.
    ...
    No substitutions are performed on the characters between the
    braces
    ...

    So in your code the $file is not substituted when the bind runs,
    because of the {} surrounding it.

    Rather when the bind *triggers*, the script is evaluated in global
    scope, and it is _there_ that the variable does not exist.

    Note: if you want to substitute $file right at that point (rather than
    when the binding triggers), use list:
    bind $::w <Control_L> [list proc.openfile $file]


    Ah, yes! That really makes sense now. Total forehead slapping moment.
    Of course I know about variable substitution, but I wasn't making the connection with the hard brackets. I tried using list and now it works
    just as expected. Double quotes works too.

    If the file name contains a space, double quotes will not work as you
    appear to expect.


    --
    +----------------------------------------------------------------------+
    | Gerald W. Lester, President, KNG Consulting LLC |
    | Email: [email protected] | +----------------------------------------------------------------------+

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