fat function in class method changes var outside itsscope

Discuss the future of the AutoHotkey language
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: fat function in class method changes var outside itsscope

21 Jan 2019, 10:18

After some thinking, AHK could have a global variable to change this behavior. To change fat-arrows assume mode.
no it couldn't, a directive would work, but please spare us.
User avatar
nnnik
Posts: 4500
Joined: 30 Sep 2013, 01:01
Location: Germany

Re: fat function in class method changes var outside itsscope

21 Jan 2019, 10:37

Yes spare us - this is what we fought against - this is what we wanted to remove.
This is why we made v2 - to remove all those global settings and variables that change the global state of the language.
Your suggestion is way worse than how arrow functions currently behvave imo.
You completely change the language with a single variable essentially creating 2 versions of it.
Recommends AHK Studio
User avatar
vvhitevvizard
Posts: 454
Joined: 25 Nov 2018, 10:15
Location: Russia

Re: fat function in class method changes var outside itsscope

21 Jan 2019, 11:33

U have a point. I'm not an advocate of global vars that change language behavior and would like the whole AHK language to have uniformity. It was just brainstorming :) Thou some global settings make sense, e.g. defining:
A_FileEncoding:="UTF-8"
saves lots of space and relieves from a headache of having that param all over FileOpen, FileAppend, etc functions.
User avatar
vvhitevvizard
Posts: 454
Joined: 25 Nov 2018, 10:15
Location: Russia

Re: fat function in class method changes var outside itsscope

21 Jan 2019, 20:11

ok. back to the topic. here is what expected from a closure. it allows to change a topvar of the function containing the current closure. it is ok - it affects only the current function. The rationale behind that is we can create CallBacks with Closures - it is understood.
but please pay attention that a closure cant change a var outside a function by default, w/o declaring it global.
when we create a fat arrow closure we expect it to mess up with the body of the function at most.

Code: Select all

t:="not changed"
u:="not changed"
func0()
msgbox("#3| " u)

func0(){
	msgbox("#1| " t)
	func1()
	msgbox("#2| " t) ;here we access to downvar in the closure

	func1(){
		t:=1 ;it changes upvar t inside the function
		u:=2 ;this doesnt change global u
	}
}

so in the next example a fat arrow method (object key assigned a Func, whatever - its callable) is not expected to change global vars by default, w/o declaring its name as global.
simply because methods and functions behave this way. Doesnt matter what rationale stands behind static object key - what we have after static initialization procedure done is object key assigned a Func and that object method should behave as expected respecting enclosed class/object scope by default.
when we create a fat arrow func (method here) we dont expect it to mess up with the vars outside functions (outside class/object here), in auto-execute code, etc.

Code: Select all

t:="not changed"
s1:=class1.func1(), msgbox(t) ;global var: "not changed"
s2:=class1.func2(), msgbox(t) ;NOT EXPECTED: global var:  "2"
msgbox(s1 " | " s2) ;retvals: "1 | 2"

class class1{
	func1(){
		t:=1
		return t
	}
	static func2:=()=>(t:=2, t)
}
Last edited by vvhitevvizard on 21 Jan 2019, 20:24, edited 5 times in total.
swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: fat function in class method changes var outside itsscope

21 Jan 2019, 20:16

i thought what if it was called from within another lambda, so as to game the " The function is assume-local if it is nested inside another function" rule, but it doesnt work

Code: Select all

global Time := (() => ({Unix: () => (DllCall("GetSystemTimeAsFileTime", 'int64p', t), t // 10000001 - 11644473600)})).Call()
global Time := {Unix: () => ((() => (DllCall("GetSystemTimeAsFileTime", 'int64p', t), t // 10000001 - 11644473600)).Call())}
it would be nice if u could force local with a keyword though
User avatar
vvhitevvizard
Posts: 454
Joined: 25 Nov 2018, 10:15
Location: Russia

Re: fat function in class method changes var outside itsscope

21 Jan 2019, 20:48

swagfag wrote:
21 Jan 2019, 20:16
but it doesnt work
its 5 am here. :D Im afraid I didn't get ur construction. .Call() doesnt call the 2nd nested lambda - it never gets to msgbox("here") inside it.

Code: Select all

t:="unchanged"
global Time := (() => ({Unix: () => (DllCall("GetSystemTimeAsFileTime", 'int64p', t)
	, msgbox("here"), t // 10000001 - 11644473600)})).Call()
global Time := {Unix: () => ((() => (DllCall("GetSystemTimeAsFileTime", 'int64p', t)
	, msgbox("here"), t // 10000001 - 11644473600)).Call())}
msgbox(Time " | " t)
Last edited by vvhitevvizard on 21 Jan 2019, 21:11, edited 1 time in total.
User avatar
vvhitevvizard
Posts: 454
Joined: 25 Nov 2018, 10:15
Location: Russia

Re: fat function in class method changes var outside itsscope

21 Jan 2019, 21:00

And concerning warnings:

Code: Select all

#Warn LocalSameAsGlobal
t:="unchanged"

func0(){
	t.=" changed" ;OK: warning "local var has the same name as a global"
}

func1()=>t.=" changed" ;BAD: no warning here

func2(){
	local t
	t.=" changed" ;OK: we declared it local explicitly
}
func3(t:=""){
	t.=" changed" ;OK: we "allocated" t in the arg list making it effectively local
}
I neither want to know the rationale of that nor the fact that it is not an omission and even might be documented somewhere as a "feature" (the way closures not having "base" keyword support is documented).
It just feels inconsistent and it doesn't allow me to debug. I try to highlight such quirks. :D
swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: fat function in class method changes var outside itsscope

21 Jan 2019, 21:24

u havent called Time.Unix()
User avatar
vvhitevvizard
Posts: 454
Joined: 25 Nov 2018, 10:15
Location: Russia

Re: fat function in class method changes var outside itsscope

21 Jan 2019, 21:36

swagfag wrote:
21 Jan 2019, 21:24
u havent called Time.Unix()
Oh indeed. :thumbup: yeah. it corrupts t in both cases

2.
swagfag wrote:
21 Jan 2019, 20:16
i thought what if it was called from within another lambda, so as to game the " The function is assume-local if it is nested inside another function" rule, but it doesnt work
btw, v2 documentation states (at least at one place):
Functions are assume-local by default. Variables accessed or created inside an assume-local function are local by default
nnnik wrote:
21 Jan 2019, 09:07
AHKs scope system does not work that way.
What you want are changes to this scope system - not the fat arrow functions.
No. Overall, the current scope system is fine. The unexpected issues start with fat-arrow shortcomings and quirks.

3. To sum up:
1. if fat-arrow returns Func -> its body has to be in assume-local mode! Simple as that.
2. fat-arrow Func ignores local keyword:
3. fat-arrow Func doesn't have a proper #Warn LocalSameAsGlobal behavior: it never raised.
4. declaring methods (and get/set halfmethods) via fat-arrow syntax is undeveloped.
5. oh. It doesnt allow ListVars executed inside fat-arrow body so we cant even check AHK's namespace logic inside fat-arrows:

Code: Select all

t:="not changed"
class1.func1()
class1.func2()
class class1{
	func1(){
		t:=1
		ListVars
		return t
	}
	static func2:=()=>(t:=2, ListVars, t)
}
6. As u might have guessed there is more to mention. But Ill skip it for now. Main complaint is not assume-local and half-baked fat-arrow Func declaration
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: fat function in class method changes var outside itsscope

22 Jan 2019, 01:41

i thought what if it was called from within another lambda, so as to game the " The function is assume-local if it is nested inside another function" rule, but it doesnt work
Nested funcions wrote:if the outer function is assume-global, nested functions behave as though assume-global by default
You can let the outer function's parameters be free vars, then you get a (inner) function without parameters which has vars which doesn't reference globals, assuming that was the goal. This is of course a game with syntax and nothing to recommended. If you want to control the scope, use a full function definition instead.
There is nothing wrong with #warn, when a function is assume global it has no locals with the same name as a globals.

Cheers.
Last edited by Helgef on 22 Jan 2019, 02:18, edited 1 time in total.
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: fat function in class method changes var outside itsscope

22 Jan 2019, 01:44

oh. It doesnt allow ListVars executed inside fat-arrow body so we cant even check AHK's namespace logic inside fat-arrows:
Please, listvars() :crazy:
Expressions wrote: () => expr
User avatar
nnnik
Posts: 4500
Joined: 30 Sep 2013, 01:01
Location: Germany

Re: fat function in class method changes var outside itsscope

22 Jan 2019, 02:50

AHKs classes are not their own scope - you are in global scope thats why the callback func (defined by a fat arrow) defined in global scope messes with global scope.
This is not a keyword - it's a hidden 1st parameter on any method.
Recommends AHK Studio
swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: fat function in class method changes var outside itsscope

22 Jan 2019, 04:28

Helgef wrote:
22 Jan 2019, 01:41
i thought what if it was called from within another lambda, so as to game the " The function is assume-local if it is nested inside another function" rule, but it doesnt work
Nested funcions wrote:if the outer function is assume-global, nested functions behave as though assume-global by default
You can let the outer function's parameters be free vars, then you get a (inner) function without parameters which has vars which doesn't reference globals, assuming that was the goal. This is of course a game with syntax and nothing to recommended. If you want to control the scope, use a full function definition instead
im not entirely sure what u meant by this. there is a vague idea concocting in my head, but i cant be too sure. can u elaborate with an example
assuming that was the goal
the goal and the reasoning were as follows:

Code: Select all

; we want 'Time.Unix()', callable from anywhere, to return the correct timestamp, w/o clobbering other vars named 't'

  global Time := (() => ({Unix: () => (DllCall("GetSystemTimeAsFileTime", 'int64p', t), t // 10000001 - 11644473600)})).Call()
; 				 ^----------------------------------------------------------------------------------------------------^^^^^^^^ - this calls the outer-lambda
;                 ^^^^^^^--------------------------------------------------------------------------------------------^ - we make an outer-lambda, with the intention of abusing "The function is assume-local if it is nested inside another function" when we later create the inner-lambda
;                        ^^^^^^^------------------------------------------------------------------------------------^ - the outer-lambda when called, returns an obkect with key 'Unix' and value 'the inner-lambda', so when u do Time.Unix(), it calls the inner-lambda
;                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - the inner-lambda, which wouldve hopefully been force-local by now, since it was 'nested inside another function'
of course, none of that works, but maybe it will give u an idea
also i hadnt taken Nested funcionswrote: if the outer function is assume-global, nested functions behave as though assume-global by default into consideration
User avatar
nnnik
Posts: 4500
Joined: 30 Sep 2013, 01:01
Location: Germany

Re: fat function in class method changes var outside itsscope

22 Jan 2019, 04:41

Code: Select all

  global Time := ((t) => ({Unix: () => (DllCall("GetSystemTimeAsFileTime", 'int64p', t), t // 10000001 - 11644473600)})).Call("")
^I think this is what he means.
Tested it works - but dont use that please.
Recommends AHK Studio
swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: fat function in class method changes var outside itsscope

22 Jan 2019, 04:45

that works actually lol
now i only have to figure out why...
User avatar
nnnik
Posts: 4500
Joined: 30 Sep 2013, 01:01
Location: Germany

Re: fat function in class method changes var outside itsscope

22 Jan 2019, 04:53

We can ignore and overwrite the first parameter due to the object call we are doing:

Code: Select all

class Time {
static Unix := (t) => (DllCall("GetSystemTimeAsFileTime", 'int64p', t), t // 10000001 - 11644473600)
}
For any extra variables we need .bind():

Code: Select all

class Time {
static Unix := (t, n) => (DllCall("GetSystemTimeAsFileTime", 'int64p', t),("Do something with n"), t // 10000001 - 11644473600).bind("")
}
Recommends AHK Studio
User avatar
nnnik
Posts: 4500
Joined: 30 Sep 2013, 01:01
Location: Germany

Re: fat function in class method changes var outside itsscope

22 Jan 2019, 04:57

@swagfag the inner lambda binds against the outer scopes variables.
t is a parameter of the outer scope - therefore local to the outer scope.
The inner scope then references this local parameter essentially creating a local variable.

Also please don't use this to define methods is ugly af - just use normal methods or wait until there is a feature added for this.
Recommends AHK Studio
swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: fat function in class method changes var outside itsscope

22 Jan 2019, 05:13

i see

and no, im not disputing its handsomeness. multi-statement lambdas already prod my eyes as it is
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: fat function in class method changes var outside itsscope

22 Jan 2019, 05:16

Yes that is what I meant, maybe it is clearer like this,

Code: Select all

class Time {
	static Unix := ((t:=0) => () => (DllCall("GetSystemTimeAsFileTime", 'int64p', t), t // 10000001 - 11644473600)).call()
}

just use normal methods or wait until there is a feature added for this.
Agree :thumbup:. I think it is reasonable that you can define a method like

Code: Select all

class t {
	method() => expr
}
equivalent to

Code: Select all

class t {
	method(){
		return expr
	}
}
with hidden this param ofc.
@ OP.
Even if fat arrow was changed (back) to assume local for outside function fat arrows, since you cannot force local in fat arrow, you should always use a full function/method definition if you are going to reference any non parameter/upvar in a fat arrow, unless you really want to reference a global var. So your complaints are futile, what you really want is to ask for is full function body fat arrows, eg,

Code: Select all

 () => {
	local
	DllCall("GetSystemTimeAsFileTime", 'int64p', t)
	return t // 10000001 - 11644473600
}
User avatar
vvhitevvizard
Posts: 454
Joined: 25 Nov 2018, 10:15
Location: Russia

Re: fat function in class method changes var outside itsscope

23 Jan 2019, 00:48

Helgef wrote:
22 Jan 2019, 01:44
() => expr
kk. it requires explicit use of () for function calls inside fat arrow expr. I skipped them in a haste and didn't even realize there is a difference. In a normal method body with 1 line per built-in function it accepts it w.o (). I would prefer expression evaluation tell me about if some var has a name as a built-in function instead of just failing silently.

Code: Select all

class1.func1()
class1.func2()

class class1{
	func1(){
		ListVars
		Pause
	}
	static func2:=()=>(ListVars(), Pause())
}
If I combine built-in function calls in 1 line:

Code: Select all

func1(){
	ListVars, Pause
}
It raises an error: Function calls require a space or "(". Use comma between parameters

BUT
For fat-arrow's body it fails silently. So the issue persists tho in another form.
:D Due to that unexpected difference using static func2:=()=>(ListVars, Pause) I fell into thinking ListVars simply fails inside fat-arrow.

PS: Please understand me correctly: writing ListVars is unusual to me, I write something like this for one-time breakpoints for debugging just for brevity, I do use () for every function call normally, e.g. I always write ExitApp() instead of ExitApp and for the majority of expressions, e.g. return(var). Just b/c its a good programming style and it does simplify cross-language programming.
But if I missed () by mistake I would expect the expression either a) work as if () r in the place (bad but thats how AHK and high-level interpreters work in general) or 2) give me some hint of error.
Helgef wrote: There is nothing wrong with #warn, when a function is assume global it has no locals with the same name as a globals.
Thats right. Its not #warn misbehavior, the cause of it is inability to change assume-mode for fat-arrow expression. But as a matter of fact I cant find a way to control correctness of my namespace.

Return to “AutoHotkey Development”

Who is online

Users browsing this forum: No registered users and 45 guests