__Set/__Get get called in constructor for defined field; they shouldn't.

Discuss the future of the AutoHotkey language
megavlad
Posts: 26
Joined: 01 Mar 2018, 00:42

__Set/__Get get called in constructor for defined field; they shouldn't.

03 May 2022, 21:31

[Moderator's note: Topic moved from Bug Reports.]

This code issues an error when it shouldn't (v2-beta.3):

Code: Select all

class ExampleClass {
    mArray := []

    __New() {
        this.mArray.Push 5 ; Error: "This value of type 'String' has no method named 'Push'."
    }

    __Get(name, params) {
        return name
    }

    __Set(name, params, value) {
    }
}

ec := ExampleClass()
The Manual states:
Meta-functions define what happens when an undefined property or method is invoked.
This is the reasonable, expected behavior of those functions. However, the "this.mArray.Push" line seems first to be calling '__Set()', followed by '__Get()', followed by trying to invoke '.Push' on the returned value from '__Get'. It shoudn't do that because 'mArray' is a defined field of ExampleClass.

I assume this is a bug.
lexikos
Posts: 9593
Joined: 30 Sep 2013, 04:07
Contact:

Re: __Set/__Get get called in constructor for defined field; they shouldn't.

04 May 2022, 02:29

In particular,
They are declared and behave like normal assignments
This is an assignment:

Code: Select all

mArray := []
Before this assignment is evaluated, the property is not defined.

When the assignment is evaluated, __Set is called, because the property does not exist.

After the assignment, the property is still not defined, because
If a meta-function is defined, it must perform whatever default action is required.
megavlad
Posts: 26
Joined: 01 Mar 2018, 00:42

Re: __Set/__Get get called in constructor for defined field; they shouldn't.

04 May 2022, 04:33

I'm sorry to say, but that is such a silly design. That behavior should not trigger on declared fields. I think the prototypical language for the magic methods feature is php5, which came out in 2004. They do it just the way one would expect.
https://www.php.net/manual/en/language.oop5.overloading.php#object.set
Before this assignment is evaluated, the property is not defined.
The compiler can make whatever decisions it wants based on the tokens it sees. I can't fathom that it would be impossible for it to inject instructions about creating those fields before the __get/__set takes effect.

That behavior feels like a deficiency, not a feature. I hope that it is transitory and that, eventually, declared fields won trigger magic methods.
lexikos
Posts: 9593
Joined: 30 Sep 2013, 04:07
Contact:

Re: __Set/__Get get called in constructor for defined field; they shouldn't.

04 May 2022, 22:39

Well, too bad. Meta functions are intended to provide the flexibility to change how the object behaves, including what occurs when you assign a new property in the body of the class or a subclass.
megavlad
Posts: 26
Joined: 01 Mar 2018, 00:42

Re: __Set/__Get get called in constructor for defined field; they shouldn't.

05 May 2022, 00:17

Hey man, lexikos, I just want to say that, personally, I think you're doing a fantastic job with leading AutoHotkey. I can imagine that, often, it can be a thankless job. The feedback I provide, if one can call it that, is because I think AutoHotkey is pretty cool. I don't mean for the feedback to be hurtful, if it ever comes across that way. For me, its about providing data from a user's perspective, which I think can be valuable when deciding between competing options. I try to state my positions as objectively and dispassionately as I can, and I always mean for them to be useful, but I know users have opinions on all manner of things, and they're not always right; and I know that leading a project such as this is about balancing competing interests. You're the AutoHotkey skipper, of course, so in the end, you'll be the one making the ultimate decisions about its future. I wish you continued luck with the project, and I hope it continues to do well.

Regards
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: __Set/__Get get called in constructor for defined field; they shouldn't.

06 May 2022, 13:00

I find it pretty unlikely that one would want mArray := [] in a class body to call a meta function. I preferred the alpha behaviour when objrawset was used. I didn't notice the change though, so I suppose it doesn't really matter for me :lol: .

@megavlad, you can work around by enabling the meta functions only after __init is called, eg

Code: Select all

class x {
	a := 1 ; no meta function
	__new(){
		this.b := 2 ; no meta function
		this.base := x.x.prototype
		this.c := 3 ; meta function
	}	
	class x {
		__set(*) => 0
	}
}
And of course, you bypass meta functions with defineprop/getownprop.

Cheers.
lexikos
Posts: 9593
Joined: 30 Sep 2013, 04:07
Contact:

Re: __Set/__Get get called in constructor for defined field; they shouldn't.

06 May 2022, 19:02

The property can also be defined explicitly, once for all instances:

Code: Select all

class ExampleClass {
    mArray := []
    
    static __New() {
        ; This is the value it will have prior to the first assignment.
        this.Prototype.DefineProp 'mArray', {Value: ''}
    }

    __New() {
        this.mArray.Push 5 ; No error
    }

    __Get(name, params) {
        return name
    }

    __Set(name, params, value) {
    }
}

ec := ExampleClass()
MsgBox ec.mArray[1] ; 5

The behaviour of assignments in the class body affects not only meta-functions, but also properties. Consider this real-world example:

Code: Select all

class iMap extends Map {
    CaseSense := "off"
}
There is no syntax for making an assignment which will invoke a dynamic property but will not invoke a meta-function (unless you explicitly retrieve the property descriptor and call the property setter). A property could also be implemented via a meta-function, in which case one may want the meta-function to be called.

Even without this, meta-functions are prone to causing infinite recursion and other problems. DefineProp and related are intended to replace meta-functions for most common uses, leaving flexibility as the priority for meta-functions rather than convenience.

I think that saying you wouldn't want the meta-function to be called isn't compelling, as it lacks necessary context: What is the meta-function being used for? Why are you not using individual properties?

If you wanted to override the behaviour of newly added properties for subclasses, how would you do it? With the alpha behaviour when ObjRawSet was used, it couldn't be done for properties "declared" in the class body. This was an actual problem that I encountered while prototyping the current model with Object.ahk

It is safe to assume there are other uses I haven't imagined. For instance, enabling a script to define a subclass that constructs a native Python object using AutoHotkey syntax (well, now I've imagined it).

Return to “AutoHotkey Development”

Who is online

Users browsing this forum: No registered users and 29 guests