• a tcl proc similar to lisp let*

    From pd@21:1/5 to All on Mon Apr 4 02:30:08 2022
    I want to create a proc that works in a similar way as let* does in lisp, but limited to expr expresions.

    The idea is to set a bunch of vars only local to a supplied script of tcl code:

    % set x
    55
    % let* {x 4 y 5+$x} {puts "$x $y"}
    4 60
    % set x
    55

    This is easy just evaluating the value setted in uplevel inside de let* proc allowing to use variables defined uplevel in the setting:

    proc let* {_d _b} {
    dict for {_v _e} $_d { set $_v [uplevel expr {*}$_e] }
    eval $_b
    }

    the problem is let* allows to use previously defined vars in the value to set in following vars, so this:

    % let* {x 4 y 5+$x} {puts "$x $y"}
    4 9

    rather than returning 4 60

    for achieving this, the value to assign to each value must be evaluated both in uplevel (to access uplevel vars) and in current level (to access previously defined vars), my first attempt was to duplicate uplevel environment in current proc level:

    proc let* {_d _p} {
    foreach _j [uplevel info vars] {
    if {[uplevel array exists $_j]}
    {array set $_j [uplevel array get $_j]}
    {set $_j [uplevel set $_j]}
    }
    dict for {_v _e} $_d { set $_v [expr {*}$_e] }
    eval $_p
    }

    this solution does not satisfy me, do you know a better approach?

    also as a bonus, do you know some way to protect internal values not to be redefined in the call to proc? I mean something like:

    % let* { x 2 _j 4} {puts "$x $_j"} ; and _j is redefined inside proc not being 4 anymore

    regards

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Ian Braithwaite@21:1/5 to All on Mon Apr 4 14:58:47 2022
    On 04/04/2022 11:30, pd wrote:
    I want to create a proc that works in a similar way as let* does in lisp, but limited to expr expresions.

    The idea is to set a bunch of vars only local to a supplied script of tcl code:

    % set x
    55
    % let* {x 4 y 5+$x} {puts "$x $y"}
    4 60
    % set x
    55

    Maybe [apply] could be a help?

    proc let* {args} {
    foreach {v e} [lrange $args 0 end-1] {
    lappend vl $v
    lappend el [uplevel [list expr $e]]
    }
    uplevel [list apply [list $vl [lindex $args end]] {*}$el]
    }

    % set x 55
    55
    % let* x 4 y {5 + $x} {puts "$x $y"}
    4 60


    the problem is let* allows to use previously defined vars in the value to set in following vars, so this:

    % let* {x 4 y 5+$x} {puts "$x $y"}
    4 9

    rather than returning 4 60

    proc let* {args} {
    set vl {}
    set el {}
    foreach {v e} [lrange $args 0 end-1] {
    set e [uplevel [list apply [list $vl [list expr $e]] {*}$el]]
    lappend vl $v
    lappend el $e
    }
    uplevel [list apply [list $vl [lindex $args end]] {*}$el]
    }

    % let* x 4 y {5 + $x} {puts "$x $y"}
    4 9


    I haven't added any error checking...

    -Ian

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From [email protected]@21:1/5 to All on Mon Apr 4 17:08:52 2022
    proc let* {args} {
    set vl {}
    set el {}
    foreach {v e} [lrange $args 0 end-1] {
    set e [uplevel [list apply [list $vl [list expr $e]] {*}$el]]
    lappend vl $v
    lappend el $e
    }
    uplevel [list apply [list $vl [lindex $args end]] {*}$el]
    }

    % let* x 4 y {5 + $x} {puts "$x $y"}
    4 9

    what about:

    set a 2
    set b 3
    let* {a22 c 5+$b $a+$c}

    returns an error because b is not visible within the apply function

    Dave B

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From pd@21:1/5 to All on Mon Apr 4 11:48:13 2022
    El lunes, 4 de abril de 2022 a las 14:58:53 UTC+2, Ian Braithwaite escribió:
    Maybe [apply] could be a help?

    proc let* {args} {
    set vl {}
    set el {}
    foreach {v e} [lrange $args 0 end-1] {
    set e [uplevel [list apply [list $vl [list expr $e]] {*}$el]]
    lappend vl $v
    lappend el $e
    }
    uplevel [list apply [list $vl [lindex $args end]] {*}$el]
    }

    % let* x 4 y {5 + $x} {puts "$x $y"}
    4 9

    thanks, this is a better approach than mine, but it has a problem, apply adds a new frame stack so the uplevel in the instrucction to excute the script (last line) really executes the apply in the same level of proc let* itself, and thus it is not able
    to see uplevel variables:

    % set z
    34
    % yourlet* x 2 y {2*$x} {puts "$x $y $z"; set z 56; info level}
    can't read "z": no such variable

    % mylet* {x 2 y 2*$x} {puts "$x $y $z"; set z 56; info level}
    2 4 34
    1

    In fact I think the final uplevel is not necessary because it doesn't matter to run the script in level 1 or 2 in that context.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Ian Braithwaite@21:1/5 to All on Tue Apr 5 12:26:09 2022
    On 04/04/2022 20:48, pd wrote:

    El lunes, 4 de abril de 2022 a las 14:58:53 UTC+2, Ian Braithwaite
    escribió:
    Maybe [apply] could be a help?

    ..

    thanks, this is a better approach than mine, but it has a problem,
    apply adds a new frame stack so the uplevel in the instrucction to
    excute the script (last line) really executes the apply in the same
    level of proc let* itself, and thus it is not able to see uplevel variables:

    Yes, you're right. Sorry!
    I'd imagined a combination of apply/uplevel did what you wanted, but now
    I can't see what I was thinking..

    -Ian


    % set z
    34
    % yourlet* x 2 y {2*$x} {puts "$x $y $z"; set z 56; info level}
    can't read "z": no such variable

    % mylet* {x 2 y 2*$x} {puts "$x $y $z"; set z 56; info level}
    2 4 34
    1

    In fact I think the final uplevel is not necessary because it doesn't
    matter to run the script in level 1 or 2 in that context.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Ian Braithwaite@21:1/5 to Ian Braithwaite on Tue Apr 5 15:02:38 2022
    On 05/04/2022 12:26, Ian Braithwaite wrote:
    On 04/04/2022 20:48, pd wrote:

    El lunes, 4 de abril de 2022 a las 14:58:53 UTC+2, Ian Braithwaite
    escribió:
    Maybe [apply] could be a help?

    ..

    thanks, this is a better approach than mine,  but it has a problem,
    apply adds a new frame stack so the uplevel in the instrucction to
    excute the script (last line) really executes the apply in the same
    level of proc let* itself, and thus it is not able to see uplevel
    variables:

    Yes, you're right. Sorry!
    I'd imagined a combination of apply/uplevel did what you wanted, but now
    I can't see what I was thinking..

    -Ian

    And with the risk of making a fool of myself again.. another suggestion


    proc let* {args} {
    while {[namespace exists Let[incr i]]} {}
    set n Let$i
    namespace eval $n {}

    foreach {v e} [lrange $args 0 end-1] {
    set e [uplevel [list namespace inscope $n expr $e]]
    namespace inscope $n variable $v $e
    }

    set x [uplevel [list namespace inscope $n [lindex $args end]]]

    namespace delete $n
    return $x
    }

    % set z 34
    34
    % let* x {2} y {2*$x} {puts "$x $y $z"; set z 56; info level}
    2 4 34


    -Ian

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Andreas Leitgeb@21:1/5 to [email protected] on Fri Apr 8 07:51:01 2022
    pd <[email protected]> wrote:
    I want to create a proc that works in a similar way as let* does in lisp,
    but limited to expr expresions.

    proc let* {_d _b} {
    dict for {_v _e} $_d { set $_v [uplevel expr {*}$_e] }
    eval $_b
    }

    Maybe, all you need is to just create a backup of the values
    originally stored in the explicitly *named* variables (rather
    than *all* variables of callers scope), and save back the
    original values afterwards...

    If then another variable (changed in the let*-body) is desired to
    be undone afterwards, just name it in the variable list.

    % set z 42 ;# -> 42
    % let* {x 1 y 2} { set z 54 } ;# leaves $z at 54
    % set z 42 ;# -> 42
    % let* {x 1 y 2 z $z} { set z 54 } ;# $z goes back to 42

    The implementation should store the previous value of named variables,
    or if the named variable was initially not set, then unset it on exit
    of let*

    also as a bonus, do you know some way to protect internal values not
    to be redefined in the call to proc? I mean something like:

    If the body ends up running in the caller-scope of "let*", then
    variables inside let* shouldn't matter.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From pd@21:1/5 to All on Mon Apr 11 12:03:59 2022
    El viernes, 8 de abril de 2022 a las 9:51:05 UTC+2, Andreas Leitgeb escribió:
    pd <[email protected]> wrote:
    I want to create a proc that works in a similar way as let* does in lisp, but limited to expr expresions.

    proc let* {_d _b} {
    dict for {_v _e} $_d { set $_v [uplevel expr {*}$_e] }
    eval $_b
    }
    Maybe, all you need is to just create a backup of the values
    originally stored in the explicitly *named* variables (rather
    than *all* variables of callers scope), and save back the
    original values afterwards...

    If then another variable (changed in the let*-body) is desired to
    be undone afterwards, just name it in the variable list.

    % set z 42 ;# -> 42
    % let* {x 1 y 2} { set z 54 } ;# leaves $z at 54
    % set z 42 ;# -> 42
    % let* {x 1 y 2 z $z} { set z 54 } ;# $z goes back to 42

    The implementation should store the previous value of named variables,
    or if the named variable was initially not set, then unset it on exit
    of let*

    Yes it should be enough to back up the named list of variables supplied rather than all existing variables but the problem is I think it's not enough because you don't know when the let* proc will be using not binded variables (in assigning named list
    or in script body) outside the considered stack frame.

    The value of a named variable must be set to a variable value previously in the stack frame, i.e. not necessary in uplevel but maybe in uplevel 2 etc The same applies for unbinded variables in script body.

    proc a {} {
    set x 5
    proc b {} {
    set y 10
    proc c {} {
    let* { v1 4 v2 $y v3 $x } { incr x; puts "$v1 $v2 $v3 $x $y" }
    set y 3
    proc d {} {
    let* { v1 4 v2 $y v3 $x } { puts "$v1 $v2 $v3 $x $y" }
    }
    }
    }
    }

    should print "4 10 5 6 10" and "4 3 5 5 3", and for this ouput is not enough to resolve backup variable values in uplevel but in all uplevels until toplevel

    The same may apply for unsetting not named variables, what exactly mean "initially not set" in this context? in the previous example x and y are set on unset in let* context? maybe you can consider x and y unset in d context, but what about c context?
    x is unset but what about y?

    I think the more tcl way is to consider all not named variables as local variables and thus unset unless explicity set.

    I think one problem is the way tcl procs treat variables, from a tcl proc point of view there's only current level variables (local) or toplevel variables (global) but nothing in between, for all the rest you should explicitly use uplevel to explore the
    stack frame, because there's no closure in tcl procs.

    Taking this into account I only see two paths, one is considering only current level and top level and thus resolving values in named variables section of let* as refered to a explict value or a previous named var or to resolve in top level only, the
    other is simulate a closure appending all variables and values to a "environmet list" all across the stack frame up to topvel. The latter has the problem of name clashing among others.

    also as a bonus, do you know some way to protect internal values not
    to be redefined in the call to proc? I mean something like:
    If the body ends up running in the caller-scope of "let*", then
    variables inside let* shouldn't matter.

    In my first example both var assignment and body execution is done in callee (let* proc) stack frame, i.e. current level, thus there's could be a clash of names. Since let* populate its own local environmet with named var list, any var declared in named
    list will clash with local variables defined in the let* proc

    In my opinion the correct way to proceed is using lambda and transforming named list environment into lamda arguments, as Ian Braithwaite did using apply. The problems are the way apply works and also how to access all stack frame chain and also
    handling special type of vars like arrays.

    regards

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Andreas Leitgeb@21:1/5 to [email protected] on Tue Apr 12 11:18:28 2022
    pd <[email protected]> wrote:
    El viernes, 8 de abril de 2022 a las 9:51:05 UTC+2, Andreas Leitgeb escribió:
    pd <[email protected]> wrote:
    I want to create a proc that works in a similar way as let* does in lisp, >> > but limited to expr expresions.
    Maybe, all you need is to just create a backup of the values
    originally stored in the explicitly *named* variables ...
    ... but the problem is I think it's not enough because you don't know
    when the let* proc will be using not binded variables (in assigning
    named list or in script body) outside the considered stack frame.

    Tcl just doesn't offer that specific mechanics:
    "for reading variables, fall back to outer scope(s) until such a var is found."

    You could mimic such mechanic, if you knew the variables in advance, and
    set read-traces for the variables, and within the traces look up the var
    in the outer scopes, but that may also not help here

    Whatever solution can be offered, they'd all have their downsides.

    Choose what downsides are least unacceptible for you and see if you can
    live with them:

    -) manually copying all variables from all outer scopes into current scope.
    downside: performance, maybe also clash between variables and arrays

    -) creating read-traces for a list of variables that you might want to
    read from within "let*".

    -) let the let*-bodies run in caller scope (read access to all vars in
    that scope) but deal with variables that may be oberwritten to restore
    them afterwards.

    pick your poison ;-)

    I think one problem is the way tcl procs treat variables, from a tcl proc point of view there's only current level variables (local) or toplevel variables (global) but nothing in between, for all the rest you should explicitly use uplevel to explore the stack frame, because there's no closure in tcl procs.

    Actually: from a tcl proc point of view there's only current level
    variables - full stop.

    Additional to that, variables can be linked in from outside, where "outside" may be global, any namespace, or even upper caller-frames. Each of
    these variables can be linked into current scope to a local name,
    using "global", "variable", or "upvar" commands to establish the links.

    But once, the link is created, any write to such a variable will also
    go to the original variable from other scope - thus it won't help
    you with your apparent goal of separating read and write access.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From pd@21:1/5 to All on Tue Apr 12 13:23:18 2022
    El martes, 12 de abril de 2022 a las 13:18:32 UTC+2, Andreas Leitgeb escribió:

    Tcl just doesn't offer that specific mechanics:
    "for reading variables, fall back to outer scope(s) until such a var is found."

    You could mimic such mechanic, if you knew the variables in advance, and
    set read-traces for the variables, and within the traces look up the var
    in the outer scopes, but that may also not help here

    Whatever solution can be offered, they'd all have their downsides.

    Choose what downsides are least unacceptible for you and see if you can
    live with them:

    -) manually copying all variables from all outer scopes into current scope. downside: performance, maybe also clash between variables and arrays

    yes, and maybe clash with dicts also or any other variable type could appear in future tcl versions. Performance is the main con here, that's why I said my own solution doesn't satisfy me ;-) Another con is scalability

    -) creating read-traces for a list of variables that you might want to
    read from within "let*".

    may you further expound this?



    -) let the let*-bodies run in caller scope (read access to all vars in
    that scope) but deal with variables that may be oberwritten to restore
    them afterwards.

    I think this will be hard to achieve and also offer poor performance, I should parse all body to see what commands modify variables in any way, even as a colateral effect.


    pick your poison ;-)

    yeah, every choice is a poison ;-)


    I think one problem is the way tcl procs treat variables, from a tcl proc point of view there's only current level variables (local) or toplevel variables (global) but nothing in between, for all the rest you should explicitly use uplevel to explore the stack frame, because there's no closure in tcl procs.

    Actually: from a tcl proc point of view there's only current level
    variables - full stop.

    that's true.

    Additional to that, variables can be linked in from outside, where "outside" may be global, any namespace, or even upper caller-frames. Each of
    these variables can be linked into current scope to a local name,
    using "global", "variable", or "upvar" commands to establish the links.

    yes, ok. What I tried to mean is you have two "declarative" ways to refer a variable: current level as local or toplevel as global. While refering any other variable in the frame stack is "procedimental" in a sense it depends on you current position in
    the stack frame.

    But once, the link is created, any write to such a variable will also
    go to the original variable from other scope - thus it won't help
    you with your apparent goal of separating read and write access.

    Yes, I know but maybe I don't fully understand the implications

    In my understanding (possibly erroneous) namespaces is all about scope not stack frames, from a scope point of view there're only namespaces: global namespace, "local" namespace, whatever namespace you create (eval)...

    From the execution point of view there're only stack frames, and you can have variables in current stack frame (level) binded to variables defined in several scopes using the appropiate commad (local, global, upvar, variable...)

    It happens toplevel define all variables in global namespace but from the point of view of level 0 you could say all variables in global namespace are local in the sense they are defined in the current level (toplevel), with "define" I mean set command
    always creates the variable in the current level, in the current stack frame.

    So it doesn't matter which scope the variable is defined, once you create it in a stack frame (by global, local, variable...) it is directly accesible in that stack frame becouse exists in that stack frame (binded to the right variable)

    So the question is when a new stack frame is created? my answer is everytime you execute a proc
    ok but what is executing a proc? obviously is executing a defined proc, but also executing a lambda (apply) and maybe other executions...

    for example, namespace eval creates a new stack frame? The doc is not clear and I'm not convinced based on code testing:

    % namespace eval XXX { proc l {} {info level} }
    % XXX::l
    1
    % namespace eval XXX { proc nl {} {namespace eval Y {info level}} }
    % XXX::nl
    2
    % namespace eval XXX { namespace eval Y {info level} }
    2

    From first command I understand namespace eval does not create a new stack frame, it's the proc call what creates it.

    From second command I understand namespace eval creates a new stack frame and that's the reason for level 2 rather than 1 as it should be since I execute only one proc

    Third commands seems to support the last conclusion, two namespace eval returns a level 2, rather than level 0

    I'm a bit confused.

    regards

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Rich@21:1/5 to [email protected] on Tue Apr 12 21:15:19 2022
    pd <[email protected]> wrote:
    I should parse all body to see what commands modify variables in any
    way, even as a colateral effect.

    That is also not /easy/, in that this is perfectly valid Tcl:

    foreach var {a1 b2 c3} {
    set $var [expr {rand()}]
    }

    The result is four variables: var, a1, b2 and c3.

    And, of course, even more 'indirection' can be done (not that it is a
    good idea, but it is possible to do):

    $ rlwrap tclsh
    % set a1 z1
    z1
    % set b1 a1
    a1
    % set [set [set b1]] [expr {rand()}]
    0.7771059185159885
    % set z1
    0.7771059185159885

    From a 'parser' viewpoint, 'z1' above was just a string that was stored
    in variable a1.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From pd@21:1/5 to All on Wed Apr 13 02:06:40 2022
    El martes, 12 de abril de 2022 a las 23:15:23 UTC+2, Rich escribió:
    pd <[email protected]> wrote:
    I should parse all body to see what commands modify variables in any
    way, even as a colateral effect.
    That is also not /easy/, in that this is perfectly valid Tcl:

    The result is four variables: var, a1, b2 and c3.

    And, of course, even more 'indirection' can be done (not that it is a
    good idea, but it is possible to do):

    yes I know, for this reason I discarded that option as low performarce even if possible (not to say if imposible) ;-)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Andreas Leitgeb@21:1/5 to [email protected] on Fri Apr 15 15:46:32 2022
    pd <[email protected]> wrote:
    El martes, 12 de abril de 2022 a las 13:18:32 UTC+2, Andreas Leitgeb escribió:
    -) manually copying all variables from all outer scopes into current scope. >> downside: performance, maybe also clash between variables and arrays
    yes, and maybe clash with dicts also or any other variable type could appear

    tcl has only variables and arrays, the latter of which are really collections of variables. dicts are values that can be stored in normal variables, and are almost, but not quite entirely, unlike arrays.

    -) creating read-traces for a list of variables that you might want to
    read from within "let*".
    may you further expound this?

    check this: https://www.tcl.tk/man/tcl8.5/tutorial/Tcl37.html
    or this: https://wiki.tcl-lang.org/page/trace
    or this: https://www.tcl.tk/man/tcl8.6.11/TclCmd/trace.html

    But trace isn't a very good fit for the problem discussed here, because you still need to know the variable names in advance, and then you could just as well copy them to local scope...

    So the question is when a new stack frame is created? my answer is everytime you execute a proc ok but what is executing a proc? obviously is executing
    a defined proc, but also executing a lambda (apply)

    I think that's all from plain Tcl: procedure calls and apply-invocations. With tclOO, there are also class and object methods.

    for example, namespace eval creates a new stack frame?

    I'd guess, it creates it only, if the named space didn't exist so far. Otherwise it just runs its body with that existing namespace.

    % namespace eval XXX { proc l {} {info level} }
    % XXX::l
    1

    Command "info level" only counts proc/apply/method-levels.
    namespaces do switch scope, but do not add a stacklevel like
    procedures/etc do.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From pd@21:1/5 to All on Fri Apr 15 16:44:51 2022
    El viernes, 15 de abril de 2022 a las 17:46:36 UTC+2, Andreas Leitgeb escribió:
    pd <[email protected]> wrote:

    -) creating read-traces for a list of variables that you might want to
    read from within "let*".
    may you further expound this?
    check this: https://www.tcl.tk/man/tcl8.5/tutorial/Tcl37.html
    or this: https://wiki.tcl-lang.org/page/trace
    or this: https://www.tcl.tk/man/tcl8.6.11/TclCmd/trace.html

    But trace isn't a very good fit for the problem discussed here, because you still need to know the variable names in advance, and then you could just as well copy them to local scope...

    thanks, I will read all those links

    regards

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From pd@21:1/5 to All on Fri Apr 15 16:43:47 2022
    El martes, 5 de abril de 2022 a las 15:02:51 UTC+2, Ian Braithwaite escribió:

    And with the risk of making a fool of myself again.. another suggestion


    proc let* {args} {
    while {[namespace exists Let[incr i]]} {}
    set n Let$i
    namespace eval $n {}
    foreach {v e} [lrange $args 0 end-1] {
    set e [uplevel [list namespace inscope $n expr $e]]
    namespace inscope $n variable $v $e
    }

    set x [uplevel [list namespace inscope $n [lindex $args end]]]

    namespace delete $n
    return $x
    }

    % set z 34
    34
    % let* x {2} y {2*$x} {puts "$x $y $z"; set z 56; info level}
    2 4 34

    I think It's a smart approach to use namespaces but I'm afraid they are not the solution for this problem, namespace variable resolution is done in current namespace and in global namespace (if not resolved in current one), so creating variables in a
    namespace and executing the expresion value in that namespace allows the use of previously defined vars but cannot handle using values of variables defined in previous stack frames but toplevel (global namespace).

    As an example:

    % namespace children
    ::twapi ::platform ::zlib ::pkg ::oo ::nsf ::tcl
    % set y 2
    2
    % set x 5
    5
    % let* x 3 {puts "$x $y"; incr x;}
    3 2
    4
    % set x
    5
    % let* x 3 {puts "$x $y $z"; incr x;}
    can't read "z": no such variable
    % proc f {} {set z 100 ; let* x 10 {puts "$x $y $z"; incr y;}}
    % f
    can't read "z": no such variable
    % set z 50
    50
    % f
    10 2 50
    3
    % namespace children
    ::twapi ::platform ::zlib ::Let1 ::pkg ::oo ::Let2 ::nsf ::tcl

    As you can see let* handles properly vars defined in proc itself (let*) by using the created namespace and also global variables (becouse created namespace look up unknown variables in global namespace) but not variables created in local to proc f (
    because are invisible to namespace created)

    Also it appears created namespaced are not deleted in a right way, at the end I still have ::Let1 and ::Let2 as children namespaces of global namespace despite the [ namespace delete $n ] command

    I think the most elegant approach is your apply based approach, but whatever approach you choose yours or mine, at the end you have to choose the poison you want as said in a message, in order to keep good performance I think there's only to poisons:
    resolve free vars in toplevel or resolve free vars in current level (the one calling the proc let*)

    regards

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From pd@21:1/5 to All on Fri Apr 15 16:47:19 2022
    El sábado, 16 de abril de 2022 a las 1:43:49 UTC+2, pd escribió:

    namespace variable resolution is done in current namespace and in global namespace (if not resolved in current one), so creating variables in a namespace and executing the expresion value in that namespace allows the use of previously defined vars but
    cannot handle using values of variables defined in previous stack frames but toplevel (global namespace).

    maybe playing with namespace path could solve the problem, but it would be tricky at the best ;-)

    regards

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