variable scope in Tcl inside the procedure - scope

I have below dummy program,
proc main2 {} {
set mainVar 100
proc subproc1 {} {
puts $mainVar
}
subproc1
}
main2
it throws an error can't read "mainVar": no such variable. my question is if I declare a variable (i.e mainVar )in proc isn't that variable should be accessible everywhere inside that proc? why it can't accessible in another proc which is declared inside mainproc proc? please put some light on this

Tcl's procedures do not nest; there is no shared scope at all. The main reason for declaring a procedure inside another one is if you are doing some kind of code generation in the outer procedure (whether of the name, the variable list or the body).
Now, you can simulate a read-only version like this (simplified version; a full-service variant is a lot more complex):
proc closure {name arguments body} {
set vars [uplevel 1 {info locals}]
set prologue {}
foreach v $vars {
upvar 1 $v var
append prologue [list set $v $var] ";"
}
uplevel 1 [list proc $name $arguments $prologue$body]
}
proc main2 {} {
set mainVar 100
closure subproc1 {} {
puts $mainVar
}
subproc1
}
main2
I'll leave making it work correctly with global and arrays (as well as all the other nuances of doing this job properly) as exercises for the reader.

Related

What's wrong with this groovy for-loop of Closures? [duplicate]

In the context of Jenkins pipelines, I have some Groovy code that's enumerating a list, creating closures, and then using that value in the closure as a key to lookup another value in a map. This appears to be rife with some sort of anomaly or race condition almost every time.
This is a simplification of the code:
def tasks = [:]
for (platformName in platforms) {
// ...
tasks[platformName] = {
def componentUploadPath = componentUploadPaths[platformName]
echo "Uploading for platform [${platformName}] to [${componentUploadPath}]."
// ...
}
tasks.failFast = true
parallel(tasks)
platforms has two values. I will usually see two iterations and two tasks registered and the keys in tasks will be correct, but the echo statement inside the closure indicates that we're just running one of the platforms twice:
14:20:02 [platform2] Uploading for platform [platform1] to [some_path/platform1].
14:20:02 [platform1] Uploading for platform [platform1] to [some_path/platform1].
It's ridiculous.
What do I need to add or do differently?
It's the same issue as you'd see in Javascript.
When you generate the closures in a for loop, they are bound to a variable, not the value of the variable.
When the loop exits, and the closures are run, they will all be using the same value...that is -- the last value in the for loop before it exited
For example, you'd expect the following to print 1 2 3 4, but it doesn't
def closures = []
for (i in 1..4) {
closures << { -> println i }
}
closures.each { it() }
It prints 4 4 4 4
To fix this, you need to do one of two things... First, you could capture the value in a locally scoped variable, then close over this variable:
for (i in 1..4) {
def n = i
closures << { -> println n }
}
The second thing you could do is use groovy's each or collect as each time they are called, the variable is a different instance, so it works again:
(1..4).each { i ->
closures << { -> println i }
}
For your case, you can loop over platforms and collect into a map at the same time by using collectEntries:
def tasks = platforms.collectEntries { platformName ->
[
platformName,
{ ->
def componentUploadPath = componentUploadPaths[platformName]
echo "Uploading for platform [${platformName}] to [${componentUploadPath}]."
}
]
}
Hope this helps!

Scope of var and variables

If I have a function like
<cfscript>
function say(what) {
var a = what;
variables.b = what;
return what;
}
</cfscript>
I thought the scope of a was variables, but dumping variables returns just b. What is the scope of a?
This really is a comment, but it is way too long. Consider the following code
<cfscript>
function doMath() {
var a = 1;
local.b = 2;
return a + local.b;
}
</cfscript>
At first glance on might think that var and local. have the same scope. After all they both exist only within the function. When then function is done, both variables cease to exist. End of story? Maybe not.
In ColdFusion, we have both implied scopes and implicit scopes.
url.a
form.a
cookie.a
session.a
application.a
local.a
arguments.a
myQuery.a
Are all different. If I have all of the above as valid variables and I say <cfoutput>#a#</cfoutput> which a do I get? ColdFusion goes through its list of implied scopes until it hit one that matches. And that is the one that gets displayed. So back to the question.
So when I am inside of a function, if I use var I am saying to ColdFusion, look through all the implied scopes until you find one that matches. If I use local.a, I am saying look in exactly one place and use that.
Benefits
I know exactly what variable I am picking up. If you are writing code that needs to be as fast as possible you won't use implicit scopes. If you are writing code that is the most readable, you won't use implicit scopes.
So no. var is not the same as local.
Understanding scoping can help you avoid some issues that can be extremely difficult to track down. For all intents an purposes, var a puts a into the local scope, and it can be referenced as a local variable. And if declared afterwards, it will overwrite any a variable already in the local scope.
https://trycf.com/gist/faf04daa53194a5fad2e69e164518299/acf2016?theme=monokai
<cfscript>
function say() {
local.a = "local" ;
var b = "var" ;
lv = local.b ; // We didn't explicitly assign b to Local scope.
try {
v = variables ; // Let's dump the variables scope.
} catch (any e) {
v = "Error: " & e.message ;
}
variables.nh = "Now here." ; // Explicitly populate variables scope.
var c = "var c" ; // We have a variables scope, what happens to var?
try {
v2 = variables ; // Let's dump the variables scope.
} catch (any e) {
v2 = "Error: " & e.message ;
}
var d = "var" ;
local.d = "local" ;
local.e = "local" ;
var e = "var" ;
return {
a : a , // Local.
b : b , // Var.
d : d , // Which one?
e : e , // Which one?
el : local.e , // Which one??
l : lv , // Ref b in local scope.
l2 : local , // Dump local scope.
v : v , // There doesn't seem to be a variables scope yet.
v2 : v2 // Defined a variable scope and now here.
} ;
}
writeDump(say());
</cfscript>
We can see above that declaring var b puts b into the local scope, and that the variables scope doesn't exist until we declare something into it. We can reference local.b, but variables.b can't exist. After we explicitly created and populated variables scope with nh, we create a var c. This doesn't go into the variables scope either, but into the local scope.
When a variable is declared with the same name in either local or by var, the last one declared will overwrite the other one (see d,e and el). Watch out for this.
Also note that the empty arguments scope from the function is also in the local scope.
On that note, my last two observation of scope things to watch out for:
https://trycf.com/gist/65b73e7a57d0434049d0eb9c0d5f9687/acf11?theme=monokai
<cfscript>
function ks() {
variables.jay = "Snoogins." ; // variables scope leaks out of function.
local.silentbob = "____" ; // local scope stays inside function.
}
function sayArgs(arg) {
local.arg = "local" ; // Order after agruments in CF10 but not 2016.
ks() ; // Run ks() function to instantiate jay and silentbob.
return {
arg : arg , // CF10 = arguments scope. CF11+ = local scope.
args : arguments ,
local : local , // No leakage from ks().
vars : variables // Jay leaks from ks().
} ;
}
writeDump(sayArgs("argue")) ;
</cfscript>
Two things I noted here:
First, there is a difference between the order of evaluation of arguments and local scopes in CF10 vs later CF versions. Current CF2016 (or 2011+) behavior is that local scope inside of a function will not overwrite the arguments scope, but it will be evaluated first. The opposite happens in CF10: arguments is evaluated first. Lucee and Railo behave like ACF2016.
The second note has to do with variable leakage as it applies to variables and local. local will only exist inside the function that it is declared in. variables is more global and can be reached outside of the function.
https://helpx.adobe.com/coldfusion/developing-applications/the-cfml-programming-language/using-coldfusion-variables/about-scopes.html << For ACF2016. Displays evaluation order with local over arguments.
http://www.learncfinaweek.com/week1/Scopes/ << Lists the order that ACF10 evaluates in, but the order of arguments and local are switched in later versions.
https://help.adobe.com/en_US/ColdFusion/9.0/Developing/WSc3ff6d0ea77859461172e0811cbec09af4-7fdf.html << This is the documentation for ACF9. It lists the same order as ACF2016, but is different in ACF10. I don't have a copy of CF9 to test against right now, so I don't know how CF9 and earlier handle the evaluations.
Declaring a variable using the var keyword puts it into the local scope, not the variables scope.
Let's go line by line (see the comments):
<cfscript>
function say(what) {
// using the "var" keyword define a variable named "a" in the "local" scope
// the "local" scope is private to the current function context
// give to "a" the value of the "what" argument
var a = what;
// explicitly define a variable "b" in the "variables" scope
// the "variables" scope is available anywhere in the current template context
// give to "b" the value of the "what" argument
variables.b = what;
// return the value of "what" argument
return what;
}
</cfscript>
var is a keyword
variables is a scope
local is a private scope (also a keyword)
The local scope can be populated either using var a = "foo" or explicitly using local.a = "foo".
In the current example, var a is available only within the scope of the function.

How do I declare a local variable within a hotkey in AutoHotkey?

TL:DR; I can't declare variables inside a hotkey as local, which means temp and index are globally accessible.
I recently discovered that local variables cannot be declared as local from within a hotkey or if they are used as parameters within a for-loop.
^j::
local temp = Hello, world! ; Error: This line does not contain a recognized action
Return
SampleFunction() {
for local temp in myArray { ; Error: Variable name contains an illegal character
; do stuff
}
}
This becomes an issue with #warn enabled. Unless I remember to use unique variable names for each of my for loops, I run into the following error:
Warning: This local variable has same name as a global variable. (Specifically: index)
For example:
#warn
^j::
index = 42 ; This *index* variable is global
Return
UnrelatedFunction() {
for index in myArray { ; This *index* variable is local
MsgBox % myArray[index]
}
}
In particular this becomes a problem when using imports, as variables from my own scripts often conflict with variables from my imported scripts.
Ideally, I would be able to put a local declaration before any for-loops, but as shown earlier, I'm not able to do this within hotkeys.
Is it possible to declare a variable as local from within a hotkey?
I implement my hotkeys with functions. Variables within functions have by default local scope unless they are declared global
F1::alpha(10,10)
F2::alpha(20,30)
F3::beta()
alpha(x,y)
{
myvar := x*2 + y
}
beta()
{
myvar := 47
}

Tcl nested proc taking output as input in nested proc

proc str2hex { string } {
set str [binary scan $string H* hex]
puts $hex
regsub -all (..) $hex {\1 } t1
set res [format "%s" $t1 ]
return $res
proc hex2str { $hex } {
puts "HIIHI"
foreach c [split $$hex ""] {
if {![string is xdigit $c]} {
return "#invalid $$hex"
}
}
set hexa [binary format H* $$hex]
return $hexa
}
}
The above is simple code for conversion of string to hexadecimal.I have made nested proc,where a hex from "set str [binary scan $string H* hex]" script is taken as input so as to re -convert the hex to string .Plz help me .
You shouldn't normally nest procedures inside procedures in Tcl; the results of it are not what you're expecting. Currently, the Tcl proc command takes almost no notice of the context in which it is called (except for knowing what the current namespace is), and in particular it does not affect what the “inner” procedure sees for variables.
What's more, proc is an ordinary command (that happens to create another command) and must actually be called for it to do anything. Putting it after the only return in a procedure will guarantee that it has no effect at all. Tcl's very simple-minded (and predictable) that way.
Finally, it's inadvisable to put $ in a variable name. It's legal, but the syntax for accessing it is awkward (in your case, it would be ${$hex}).
If you really want local procedure-like things, consider using apply and a lambda term. They were introduced in Tcl 8.5.
If you're using Tcl 8.6 (recommended now) then you've got some more elegant ways of doing these two operations:
proc str2hex {string {encoding "utf-8"}} {
binary scan [encoding convertto $encoding $string] cu* bytes
return [lmap value $bytes {format %02x $value}]
}
proc hex2str {hex {encoding "utf-8"}} {
return [encoding convertfrom $encoding [binary format H* [join $hex ""]]]
}
(The encoding needs to be specified as otherwise there isn't a unique mapping between bytes — which binary scan and binary format work with — and characters. But we can set a sensible default.)

why do we need nested procedures in tcl

Hi i have been working with tcl scripting for almost a year and now understand almost basics of it completely. But today i just came across nested procedures which is kind of strange as i did not get the use of it.
Anyways, i read about nested proc here but did not get the clear idea as of why do we need it.
The article says that since proc's are global in a namespace so to create a local proc you make nested proc's.
proc parent {} {
proc child {} {
puts "Inside child proc";
}
...
}
Now one usage i can think of is like
proc parent {} {
proc child {intVal} {
puts "intVal is $intVal";
}
puts "[::child 10]";
... #some processing
puts "[::child 20]";
... #some processing
puts "[::child 30]";
... #some processing
puts "[::child 40]";
... #some processing
puts "[::child 50]";
... #some processing
}
So now the child proc is local to the parent proc and could be used only inside parent proc. And also as i understand it is useful when you want to do same processing at multiple places inside that parent proc.
Now my confusion is that Is this the only use of nested proc or is there anything else that i did not understand???. I mean the nested proc just seems like a kind of private proc.
So please shed some light on it and help me understand the use of nested proc's.
Tcl doesn't have nested procedures. You can call proc inside a procedure definition, but that's just creating a normal procedure (the namespace used for resolution of the name of the procedure to create will be the current namespace of the caller, as reported by namespace current).
Why would you put proc inside proc? Well, the real reason for doing so is when you want to have the outer command act as a factory, to create the command when it is called. Sometimes the name of the command to create will be supplied by the caller, and sometimes it will be internally generated (in the latter case, it is normal to return the name of the created command). The other case that comes up is where the outer command is some sort of proxy for the (real) inner one, allowing the postponing of the creation of the real command because it is expensive in some fashion; if that's the case, the inner procedure will tend to actually be created with the same name as the outer one, and it will replace it (though not the executing stack frame; Tcl's careful about that because that would be crazy otherwise).
In the case where you really need an “inner procedure” it's actually better to use a lambda term that you can apply instead. That's because it is a genuine value that can be stored in a local variable and which will automatically go away when the outer procedure terminates (or if you explicitly unset the variable or replace its contents). This inner code won't have access to the outer code's variables except via upvar; if you want to return the value while still binding variables, you should use a command prefix and include a bit of extra trickery to bind the variables as pre-supplied arguments:
proc multipliers {from to} {
set result {}
for {set i $from} {$i <= $to} {incr i} {
lappend result [list apply {{i n} {
return [expr {$i * $n}]
}} $i]
}
return $result
}
set mults [multipliers 1 5]
foreach m $mults {
puts [{*}$m 2.5]
}
# Prints (one per line): 2.5 5.0 7.5 10.0 12.5
Using an inner proc to simulate apply
Note that the apply command can actually be simulated by an inner procedure. This was a technique used in Tcl 8.4 and before:
# Omitting error handling...
proc apply {lambdaTerm args} {
foreach {arguments body namespace} $lambdaTerm break
set cmd ${namespace}::___applyLambad
proc $cmd $arguments $body
set result [uplevel 1 [linsert 0 $args $cmd]]; # 8.4 syntax for safe expansion!
rename $cmd ""
return $result
}
This was somewhat error-prone and very slow as it would recompile on each invocation; we don't do that any more!
Tcl does not have nested procs. From the proc man page:
Normally, name is unqualified (does not include the names of any containing namespaces), and the new procedure is created in the current namespace.
(emphasis mine)
To demonstrate:
% namespace eval foo {
proc parent {} {
proc child {} {
puts "the child"
}
puts "the parent, which holds [child]"
}
}
% foo::parent
the child
the parent, which holds
% foo::child
the child
We can still call the "inner" proc directly -- it's not local to the enclosing proc.
One item you missed in the discussion in that wiki page is that to make a proc truly local only to the enclosing proc, one must delete it at the end of the enclosing proc:
% namespace eval foo {
proc parent {} {
proc child {} {
puts "the child"
}
puts "the parent, which holds [child]"
# now, destroy the inner proc
rename child ""
}
}
% foo::parent
the child
the parent, which holds
% foo::child
invalid command name "foo::child"
As to the use of a local proc, I'd agree with you that it's beneficial to encapsulate repetive tasks that are only useful in the current proc. I wouldn't get too hung up on that though: clear documentation or code conventions will do just as well.

Resources