v2.0-a129 - global clarification

Get help with using AutoHotkey (v2 or newer) and its commands and hotkeys
robinson
Posts: 189
Joined: 12 Sep 2019, 20:28

v2.0-a129 - global clarification

17 Mar 2021, 11:42

Hi, total noob here again.
Sorry but can you clarify how global variables work now?
Why does this code throw this error at the myVar++ line?
Error: Expected a Number but got an unset variable. Specifically: myVar

Code: Select all

myVar:=0

#HotIf WinActive("ahk_exe notepad.exe")
^+n:: {
	if(IsInteger(A_TimeSincePriorHotkey) && (A_TimeSincePriorHotkey<=1000)) {
		myVar++
		SendInput("{Home}" myVar ".{Space 2}{Down}{Home}")
	} else {
		myVar:=1
		SendInput("{Home}" myVar ".{Space 2}{Down}{Home}")
	}
}
#HotIf
iPhilip
Posts: 814
Joined: 02 Oct 2013, 12:21

Re: v2.0-a129 - global clarification

17 Mar 2021, 18:30

Unless declared global, all variables inside a hotkey are assumed local. In your case, you can declare myVar a static variable as follows:

Code: Select all

#HotIf WinActive("ahk_exe notepad.exe")
^+n:: {
	static myVar:=0
	if(IsInteger(A_TimeSincePriorHotkey) && (A_TimeSincePriorHotkey<=1000)) {
		myVar++
		SendInput("{Home}" myVar ".{Space 2}{Down}{Home}")
	} else {
		myVar:=1
		SendInput("{Home}" myVar ".{Space 2}{Down}{Home}")
	}
}
#HotIf
Cheers!
Windows 10 Pro (64 bit) - AutoHotkey v2.0+ (Unicode 64-bit)
swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: v2.0-a129 - global clarification

18 Mar 2021, 05:51

for reading purposes u can use global variables freely without declaring them as such
if u want to assign to them, however, ull have to import them from the global namespace with keyword global
20170201225639
Posts: 144
Joined: 01 Feb 2017, 22:57

Re: v2.0-a129 - global clarification

06 Oct 2021, 00:41

1. Does that mean that accessing a class's static variables also require declaration? I've read that super-globals have been eliminated. Does that mean it's no longer possible to directly set Class.staticvar in the scope of a function (or even in auto-exec section) without declaration?

2. Is there a way in v2 to simulate the convenience of the super-globals of v1? One idea is put all of one's functions in a giant function BigFunc. Then call BigFunc in the auto-exec section. (And hotkeys presumably need to be set up with the Hotkey function (you can't nest a hotkey definition in another function, can you?) at the beginning of BigFunc with the action parameter set to functions inside BigFunc.) Then all the local vars of BigFunc would be effectively super-global vars for the nested functions (we just need to be sparing with creating them like we are with super globals of v1). Would that work?
iPhilip
Posts: 814
Joined: 02 Oct 2013, 12:21

Re: v2.0-a129 - global clarification

08 Oct 2021, 01:47

20170201225639 wrote:
06 Oct 2021, 00:41
1. Does that mean that accessing a class's static variables also require declaration? I've read that super-globals have been eliminated. Does that mean it's no longer possible to directly set Class.staticvar in the scope of a function (or even in auto-exec section) without declaration?
In v2, a class name is a global variable and all global variables can be read without declaring them global inside a function. Thus, you can read (but not write to) a class' static variable within a function without a global declaration. See the following example:

Code: Select all

MsgBox foo.x " " goo()  ; -> 1 1

class foo
{
   static x := 1
}

goo()
{
   return foo.x
}

20170201225639 wrote:
06 Oct 2021, 00:41
2. Is there a way in v2 to simulate the convenience of the super-globals of v1? One idea is put all of one's functions in a giant function BigFunc. Then call BigFunc in the auto-exec section. (And hotkeys presumably need to be set up with the Hotkey function (you can't nest a hotkey definition in another function, can you?) at the beginning of BigFunc with the action parameter set to functions inside BigFunc.) Then all the local vars of BigFunc would be effectively super-global vars for the nested functions (we just need to be sparing with creating them like we are with super globals of v1). Would that work?
Have you tried? It seems cumbersome to me.
Windows 10 Pro (64 bit) - AutoHotkey v2.0+ (Unicode 64-bit)
20170201225639
Posts: 144
Joined: 01 Feb 2017, 22:57

Re: v2.0-a129 - global clarification

08 Oct 2021, 02:12

iPhilip wrote:
08 Oct 2021, 01:47
20170201225639 wrote:
06 Oct 2021, 00:41
1. Does that mean that accessing a class's static variables also require declaration? I've read that super-globals have been eliminated. Does that mean it's no longer possible to directly set Class.staticvar in the scope of a function (or even in auto-exec section) without declaration?
In v2, a class name is a global variable and all global variables can be read without declaring them global inside a function. Thus, you can read (but not write to) a class' static variable within a function without a global declaration. See the following example:

Code: Select all

MsgBox foo.x " " goo()  ; -> 1 1

class foo
{
   static x := 1
}

goo()
{
   return foo.x
}

20170201225639 wrote:
06 Oct 2021, 00:41
2. Is there a way in v2 to simulate the convenience of the super-globals of v1? One idea is put all of one's functions in a giant function BigFunc. Then call BigFunc in the auto-exec section. (And hotkeys presumably need to be set up with the Hotkey function (you can't nest a hotkey definition in another function, can you?) at the beginning of BigFunc with the action parameter set to functions inside BigFunc.) Then all the local vars of BigFunc would be effectively super-global vars for the nested functions (we just need to be sparing with creating them like we are with super globals of v1). Would that work?
Have you tried? It seems cumbersome to me.

Thanks for the information about static vars. I also found out you can't use them as initializers. Oh well, but maybe it's a good thing, since it was a constant source of the most mysterious bugs in my old scripts.

I haven't tried that method (#2), because I found a substitute that satisfies my purposes. Namely you can put all your former global vars in an object, and it looks like you don't need to declare anything in order to modify object properties.

Why is this the case though? Is it because when you set the value it is a method call?

Code: Select all





G := {
	prop1: "haha",

	prop2: "hoho",

	prop3: {
		prop4: "wow",
		prop5: "ok",
		arr1: [
			1,
			2
			3,
			4,
			5
		]
	}
}


f()


mm G.prop1 ; gaga
mm G.prop3.prop5 ; ouch
mm G.prop3.arr1[4] ; 9
return

f(){
	G.prop1 := "gaga"
	G.prop3.prop5 := "ouch"
	G.prop3.arr1[4] := 9
}


mm(k) => msgbox(k)

20170201225639
Posts: 144
Joined: 01 Feb 2017, 22:57

Re: v2.0-a129 - global clarification

08 Oct 2021, 02:14

wait a minute... it looks like you *can* write to static vars after all?

Code: Select all

class C {
	static v := 1
}

f()
mm C.v ; 123!!!!!!!!
return

f(){
	C.v := 123
}
iPhilip wrote:
08 Oct 2021, 01:47
20170201225639 wrote:
06 Oct 2021, 00:41
1. Does that mean that accessing a class's static variables also require declaration? I've read that super-globals have been eliminated. Does that mean it's no longer possible to directly set Class.staticvar in the scope of a function (or even in auto-exec section) without declaration?
In v2, a class name is a global variable and all global variables can be read without declaring them global inside a function. Thus, you can read (but not write to) a class' static variable within a function without a global declaration. See the following example:

Code: Select all

MsgBox foo.x " " goo()  ; -> 1 1

class foo
{
   static x := 1
}

goo()
{
   return foo.x
}

20170201225639 wrote:
06 Oct 2021, 00:41
2. Is there a way in v2 to simulate the convenience of the super-globals of v1? One idea is put all of one's functions in a giant function BigFunc. Then call BigFunc in the auto-exec section. (And hotkeys presumably need to be set up with the Hotkey function (you can't nest a hotkey definition in another function, can you?) at the beginning of BigFunc with the action parameter set to functions inside BigFunc.) Then all the local vars of BigFunc would be effectively super-global vars for the nested functions (we just need to be sparing with creating them like we are with super globals of v1). Would that work?
Have you tried? It seems cumbersome to me.
iPhilip
Posts: 814
Joined: 02 Oct 2013, 12:21

Re: v2.0-a129 - global clarification

08 Oct 2021, 11:00

20170201225639 wrote:
08 Oct 2021, 02:14
wait a minute... it looks like you *can* write to static vars after all?
Looks like an undocumented feature. ;)

Code: Select all

global_var := 10
MsgBox foo.x "`n" global_var  ; -> 1 10
goo()
MsgBox foo.x "`n" global_var  ; -> 2 10

class foo
{
   static x := 1
}

goo()
{
   global_var := 22  ; this doesn't affect the value of global_var
   foo.x *= 2        ; apparently, class static variables can be written to (undocumented)
}
Windows 10 Pro (64 bit) - AutoHotkey v2.0+ (Unicode 64-bit)
iseahound
Posts: 1444
Joined: 13 Aug 2016, 21:04
Contact:

Re: v2.0-a129 - global clarification

08 Oct 2021, 11:23

It's working as expected, I promise. The reason is because of variable shadowing. By default, all global variables in a local scope are read-only.

Example #1: Local variable shadows global variable.
Recall that variable and function namespaces have been merged.

Code: Select all

; In this example, MsgBox is declared as a local variable that shadows the global MsgBox function.
f(MsgBox) {
   MsgBox += 15
   return MsgBox
}

MsgBox f(12)
Example #2: Objects are passed by reference, not value.
So the global object cannot be overwritten, but its properties can be modified

Code: Select all

global obj := {myProp: 7}

s() {
   obj.myProp := 99999999
}

s()
MsgBox obj.myProp

Code: Select all

global obj := {myProp: 7}

s() {
   obj := {myProp: 999999} ; Doesn't work! global obj is read only, so this creates a local variable called "obj"
   obj.myProp := 99999999 ; Modifies the LOCAL variable called obj. 
}

s()
MsgBox obj.myProp
Example #3 - So you can actually modify all global objects this way.

Code: Select all

s() {
   MsgBox.var := 17
}

s()
MsgBox MsgBox.var
iPhilip
Posts: 814
Joined: 02 Oct 2013, 12:21

Re: v2.0-a129 - global clarification

08 Oct 2021, 12:46

iseahound wrote:
08 Oct 2021, 11:23
It's working as expected, I promise. The reason is because of variable shadowing. By default, all global variables in a local scope are read-only.

Example #2: Objects are passed by reference, not value.
So the global object cannot be overwritten, but its properties can be modified

Code: Select all

global obj := {myProp: 7}

s() {
   obj := {myProp: 999999} ; Doesn't work! global obj is read only, so this creates a local variable called "obj"
   obj.myProp := 99999999 ; Modifies the LOCAL variable called obj. 
}

s()
MsgBox obj.myProp
Example #3 - So you can actually modify all global objects this way.

Code: Select all

s() {
   MsgBox.var := 17
}

s()
MsgBox MsgBox.var
Thank you. The examples you provided are helpful. I read up on the topic of variable shadowing here.

Just to convince myself that MsgBox is a global object I modified Example #3 as follows:

Code: Select all

s() {
   MsgBox.var := 17
}

s()
MsgBox MsgBox.var
MsgBox IsObject(MsgBox)    ; MsgBox is a global object
MsgBox HasMethod(MsgBox)   ; MsgBox is callable function object
MsgBox.Call("hello world")
Windows 10 Pro (64 bit) - AutoHotkey v2.0+ (Unicode 64-bit)
lexikos
Posts: 9583
Joined: 30 Sep 2013, 04:07
Contact:

Re: v2.0-a129 - global clarification

13 Oct 2021, 08:19

iPhilip wrote:
08 Oct 2021, 01:47
In v2, a class name is a global variable and all global variables can be read without declaring them global inside a function. Thus, you can read (but not write to) a class' static variable within a function without a global declaration.
The first statement does not support the second one.

In v2, each class is referenced by a global constant, or read-only variable. It cannot be assigned inside or outside a function. However, if you attempted to assign to it inside a function (without declaration), you would instead shadow it with a local variable.

A "class' static variable" is not a variable in the same sense as a global or local variable, and does not have global or function scope. It is a property of the class object. If you have a reference to that object, you can retrieve the property. The expression C.v := 1 reads a reference to the class from variable C, and sets property v of the class.
20170201225639
Posts: 144
Joined: 01 Feb 2017, 22:57

Re: v2.0-a129 - global clarification

15 Oct 2021, 18:26

lexikos wrote:
13 Oct 2021, 08:19
A "class' static variable" is not a variable in the same sense as a global or local variable, and does not have global or function scope. It is a property of the class object.
It just occured to me:
Isn't there a sense in which the Obj.Prop notation is merely shorthand for the more "canonical" and strictly correct notation Obj["Prop"] that arguably more accurately reflects how AHK interprets the statement under the wraps? [1]

Suppose I'm right. Then if everyone is forced to always use Obj["Property"] rather than Obj.Property, then one source of misconception could be entirely eliminated. Of course, some convenience is lost, but it could at least be helpful at the learning stage.

(Consider, for example, the issue in v1 about passing Obj.Property ByRef to a function. Because Obj.Prop looks like a variable, and in most contexts behave like a variable, many people are surprised they find passing them ByRef doesn't work (if they haven't learned the lesson from their earlier discovery that %Obj.Prop% doesn't work). But imagine if they had to write Obj["Property"] instead! Then it'd be immediately clear that they'd be passing the result of evaluating an expression if they were to call

Code: Select all

FuncThatTakeByRefArg(Obj["Property"])
)

It reminds me of the old pseudoarrays of AHK v1! Pseudoarrays is the same trade-off between explicitness and convenience, but in reverse. A pseudoarray item like

Code: Select all

Array%n%
is a variable disguising as an expression, whereas

Code: Select all

Obj.Property
is an expression disgusing (at least to uninitiated me) as a variable.

(Now of course that's not completely correct, because even the notation

Code: Select all

Obj["property"]
is not free of the convenience-explicitness tradeoff: it's not always an expression. When appearing on the left hand side of assignment operator (:=), it certainly is not an expression, because assigning to expression is nonsensical.)

[1] What I mean is, presumably variable names only exist in the source code, but property names are real strings that exist at runtime. For example, if I go through my source code, and change all occurrence of "for index, element ..." to "for indeeeeeeeeex, element", the compiled executable shouldn't differ in size at all, whereas if I change my property names from "propA" to "propAAAAA", then the change could well be reflected in the executable, because the string would need to actually exist in the binary. Of course, AHK being an interpreted language, the possibility of evaluating %var% means AHK probably does store even variable names at runtime, so some of what I said is moot, but I imagine it would hold true for compiled languages.
lexikos
Posts: 9583
Joined: 30 Sep 2013, 04:07
Contact:

Re: v2.0-a129 - global clarification

17 Oct 2021, 01:09

20170201225639 wrote:
15 Oct 2021, 18:26
Isn't there a sense in which the Obj.Prop notation is merely shorthand for the more "canonical" and strictly correct notation Obj["Prop"] that arguably more accurately reflects how AHK interprets the statement under the wraps? [1]
Not in v2.
Then if everyone is forced to always use Obj["Property"] rather than Obj.Property, then one source of misconception could be entirely eliminated.
Doubtful. .Property is already different to Variable.

Even if a class static variable was a variable, it would still be scoped to the object, not the function or the script.
Consider, for example, the issue in v1 about passing Obj.Property ByRef to a function.
Consider that this is v2, and no (breaking) changes will ever be made to v1 syntax. In v2, passing of variables by reference must be explicit. If you attempt to pass &obj.Prop in v2.0-beta.2, you will get an immediate load time error drawing attention to the problem; in future it may simply work.
Then it'd be immediately clear that they'd be passing the result of evaluating an expression
I disagree. I do not believe it is so easy for a new user to grasp what is or is not an expression, what that expression returns, or that they would even think to make these distinctions. In f(x := y), you are passing the result of evaluating a sub-expression, but that result is itself a variable.
Array%n% is a variable disguising as an expression
False (and I think that supports my previous point). Array%n% is an expression (in the general sense) which evaluates to a variable reference. Array1 would be a variable. n is a variable. Array is a string. The expression concatenates "Array" with n and returns the variable with that name.

The important thing in v1 is that Array%n% and x := y and ++x evaluate to the variable, not just its value.
[1] What I mean is, presumably variable names only exist in the source code, but property names are real strings that exist at runtime.
No. For Array%n%, Array1 may or may not exist in the source code and/or at runtime. For obj.Prop, Prop clearly exists in the source code. For x := y, both x and y exist in the source code and at runtime. How would a double-deref work if the name didn't exist at runtime?
For example, if I go through my source code, and change all occurrence of "for index, element ..." to "for indeeeeeeeeex, element", the compiled executable shouldn't differ in size at all
"Compiling" a script is just packaging it with the interpreter in a single file. The "compiled executable" contains all of your original variable names.
... so some of what I said is moot, but I imagine it would hold true for compiled languages.
Keep in mind that your comments were supposed to support your assumptions about "how AHK interprets the statement under the wraps".

Return to “Ask for Help (v2)”

Who is online

Users browsing this forum: omar, songdg, TAC109 and 30 guests