v1.1.16 alpha - Properties

Community news and information about new or upcoming versions of AutoHotkey
lexikos
Posts: 9553
Joined: 30 Sep 2013, 04:07
Contact:

v1.1.16 alpha - Properties

01 Aug 2014, 22:23

Some information in this thread applies only to v1.1.16.00-a01, not v1.1.16.01+.

See v1.1.16 - Properties, bug fixes and optimizations or the documentation. If in doubt, assume the information in this thread is not applicable.

Properties

I've uploaded a new alpha build with a new feature for testing.

Working example:

Code: Select all

class MyClass {
    MyProperty {
        get {
            MsgBox Getting MyProperty.
            return this._myProperty  ; Return the property's value.
        }
        set {
            MsgBox Setting MyProperty to %value%.
            this._myProperty := value  ; Do something with the value being assigned.
            return value  ; Return the property's new value.
        }
    }
}

x := new MyClass
MsgBox % "x.MyProperty was set to " (x.MyProperty := 42)
MsgBox % "x.MyProperty returned " x.MyProperty
There are two types of property declarations:

Code: Select all

class MyClass {
    WithoutParams {
        ;...
    }
    With[Params] {
        ;...
    }
}
Params supports the same syntax and behaviour as a normal function parameter list (but surrounded by square brackets). The parameter list is defined only once, and this definition is used by both get and set. Get and set have the usual hidden this parameter, while set has an additional parameter value, which contains the value being assigned. (If you define the property as Property[Params*], value is not included in the Params array.)

For accessing properties, all of the following are valid, but x must be a derived object, not the class itself:

Code: Select all

value := x.MyProperty
value := x.MyProperty[params]
value := x.MyProperty(params)  ; Method calls are treated the same as [].
value := x["MyProperty", params]
x.MyProperty := value
x.MyProperty[params] := value
x.MyProperty(params) := value  ; () is translated to [] at load time, as in previous versions.
x["MyProperty", params] := value
Overriding Properties
Each class can override the properties of its base class. Omitting get or set allows the respective operation to be handled by the base class, which could use a meta-function, property or simple static class variable. A property definition can explicitly call on the base-class behaviour by using base.MyProperty, with the following caveats:
  • As with methods, this never triggers a meta-function.
  • The object's base can't be accessed this way. If you defined a property named base (with getter), the object's base would be inaccessible because this.base invokes the property.
How It Works
When the property definition is encountered at load time, it is translated into a Property object and two methods. When the Property is inherited by a derived object, instead of returning the Property, the getter or setter is called. An important consequence is that if MyClass is the exact class in which MyProperty is defined, MyClass.Property does not call the property - it returns the Property object.

Property Objects
Property.Get and Property.Set each hold a reference to a function which implements the respective operation. These can be modified, but can only a hold function reference (not any other type of object) or nothing. They can also be called - the first example above could have been written like this:

Code: Select all

; ...
MsgBox % "x.MyProperty was set to " MyClass.MyProperty.set(x, 42)
MsgBox % "x.MyProperty returned " MyClass.MyProperty.get(x)
Again, note that MyClass must be the exact class in which MyProperty was defined, not a derived class/object. Also note that the value to be assigned is always the second parameter, whereas the __Set meta-function requires it to be the last parameter.

Reusing Properties
Properties can be reused by other classes, or by the default base object. For example:

Code: Select all

class Extensions {
    Length {
        ; StrLen is assigned directly to .get, for performance.
    }
    static Length.get := Extensions._def("Length", Func("StrLen"))
    _def(P, ret) {
        "".base[P] := this[P]
        return ret
    }
}

MsgBox % "string".Length
Background
Currently the rules for how meta-functions work are complicated and error-prone, in addition to being a little cumbersome. I have long been considering how best to improve on it for v2, but the only satisfactory answer I came up with was to reduce the need for meta-functions - by implementing Properties. Please keep discussion about v2 to the v2 forum, not this thread.

Other Changes
Fixed objects so that if __Call assigns a value to the appropriate key and does not explicitly return, the value is automatically called. This was the original intent, and is consistent with __Get and __Set. Previously, the value was ignored and a built-in method was potentially called (but the next invocation would see the new field and call it).

Download v1.1.16.00-a01-019fb99 - contains a (ANSI), w (Unicode 32-bit) and x64 (Unicode 64-bit) builds.

I don't intend to provide binaries for compiled scripts. I do intend to merge the new features into v2 soon.
Coco
Posts: 771
Joined: 29 Sep 2013, 20:37
Contact:

Re: v1.1.16 alpha - Properties

02 Aug 2014, 00:01

Looks good. Makes life easier if you want to run a routine every time a property/field is accessed (by __Get or __Set) without having to maintain a dummy object (e.g.: ObjInsert(this, "_", [])). By the way, I noticed that I can't do something like instance.Property.param must be instance.Property[param]...
User avatar
joedf
Posts: 8940
Joined: 29 Sep 2013, 17:08
Location: Canada
Contact:

Re: v1.1.16 alpha - Properties

02 Aug 2014, 00:03

very intriguing... :ugeek:
Image Image Image Image Image
Windows 10 x64 Professional, Intel i5-8500, NVIDIA GTX 1060 6GB, 2x16GB Kingston FURY Beast - DDR4 3200 MHz | [About Me] | [About the AHK Foundation] | [Courses on AutoHotkey]
[ASPDM - StdLib Distribution] | [Qonsole - Quake-like console emulator] | [LibCon - Autohotkey Console Library]
Coco
Posts: 771
Joined: 29 Sep 2013, 20:37
Contact:

Re: v1.1.16 alpha - Properties

02 Aug 2014, 00:18

@lexikos: How do I implement this in a non-class syntax way? I tried something below but it doesn't work:

Code: Select all

test := {
(Join
	"property": {
		"set": Func("test_property_setter"),
		"get": Func("test_proeprty_getter")
	}
)}

t := new test
MsgBox % t.property
return

test_property_getter(this, params*) {
	return "Hello World"
}

test_property_setter(this, value, params*) {
	MsgBox %value%
	return value
}
just me
Posts: 9423
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: v1.1.16 alpha - Properties

02 Aug 2014, 05:34

Looks promising:

Code: Select all

C := New MyClass
MsgBox, 0, Alpha Test, % C.Protected
MsgBox, 0, Alpha Test, % C.Protected := "Goodbye!"

Class MyClass {
   Protected[Params*] {
      Get {
         MsgBox, 0, %A_ThisFunc%, Getting Protected.
         Return "Hello!" ; Return the property's value.
      }
      Set {
         MsgBox, 0, %A_ThisFunc%, Trying to set Protected to "%value%" (won't be done!).
         Return "Hello!" ; This.Protected  ; Return the property's value.
      }
   }
}
One question: Would it be feasible to allow a single-line format which solely defines the return value?

Code: Select all

Protected[Params*] {
   Get {"Hello!"}
   Set {"Hello!"}
}
lexikos
Posts: 9553
Joined: 30 Sep 2013, 04:07
Contact:

Re: v1.1.16 alpha - Properties

02 Aug 2014, 21:38

Coco wrote:Makes life easier if you want to run a routine every time a property/field is accessed (by __Get or __Set) without having to maintain a dummy object (e.g.: ObjInsert(this, "_", [])).
I don't see your point. You don't need to do that with __Get/__Set any more or less than you do with properties. You just need to check what the key/name is before you do anything with __Get/__Set.

Code: Select all

__Get(name) {
    if (name = "MyProperty")
        return ;(property value computed the same way as with property get{}).
    ; Otherwise, do nothing.
}
By the way, I noticed that I can't do something like instance.Property.param must be instance.Property[param]...
I think you misunderstand the concept of parameters. The former is two operations: instance.Property and temp.param.
Coco wrote:@lexikos: How do I implement this in a non-class syntax way?
You don't. The whole point is to make properties straightforward. The non-class way is not straightforward for most people, and is generally only useful for dynamic properties (the kind that you would use __Get for).
just me wrote:One question: Would it be feasible to allow a single-line format which solely defines the return value?
Yes, but there's a measurable increase in code size. It would also mean more to document. Does anyone else think it would be worthwhile?

If the parser is ever improved to allow more flexibility with whitespace, fun() { return something } might automatically become possible, in which case get { something } would be ambiguous. However, at that point compatibility probably won't be a priority, since there'll likely be other parts of the syntax that can be changed for the better. get { return something } wouldn't be ambiguous, but checking for return adds a little bit more to the code size and is less convenient.
lexikos
Posts: 9553
Joined: 30 Sep 2013, 04:07
Contact:

Re: v1.1.16 alpha - Properties

02 Aug 2014, 22:33

One of the syntax possibilities I had considered was:

Code: Select all

MyProperty {
get:
    return something
set:
    return something := value
}
... because it's more compact: it doesn't require an extra line for the closing brace, or extra indentation. It would also be possible to omit the line break after ':'.

However, I rejected that idea because implementing them with the expected semantics would come at too high a cost, and I wasn't even sure that those semantics were suitable. That is, since it looks like one function with two labels, it should act that way and be able to share static variables between the two parts.

Allowing get: expression has a slightly lower cost to code size than get {expression} and wouldn't be ambiguous, but seems a little strange.

Shorthand form MyProperty { get: expression, set: expression } would be feasible with a little more work. It has the advantages of resembling existing syntax and being even more compact, but would probably come at a higher cost to code size.

This sort of shorthand is easier to implement at a low cost when the parser gives some intermediate representation of the code to work with (such as an AST). Unfortunately the current parser doesn't work like that.
User avatar
jethrow
Posts: 188
Joined: 30 Sep 2013, 19:52
Location: Iowa

Re: v1.1.16 alpha - Properties

02 Aug 2014, 23:10

Interesting approach - I like work toward making properties simpler.

Code: Select all

    MyProperty {
        get {
            MsgBox Getting MyProperty.
            return this._myProperty  ; Return the property's value.
        }
In the above code, the correct way to access the property within the get code block is to prefix the name with an underscore, correct? I hesitate to say this since I wouldn't want to keep adding special words, but couldn't this be less redundant? I'd suggest special keyword in order to access the property (perhaps even negating the need for this.) - which would also make code more re-usable.
lexikos
Posts: 9553
Joined: 30 Sep 2013, 04:07
Contact:

Re: v1.1.16 alpha - Properties

03 Aug 2014, 03:06

No, this._myProperty is just another field where I happened to store the value. AutoHotkey does not make any connection between it and the property.

"The property" is just a set of functions, one of which you're already inside. If you want to store a value, just store a value - wherever you want. You don't have to store a value though; the point of a Property is that the value can be computed each time.
just me
Posts: 9423
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: v1.1.16 alpha - Properties

03 Aug 2014, 04:11

OK, if it doen't mesh with the current parser, I'm happy with it as is.
User avatar
RobertL
Posts: 546
Joined: 18 Jan 2014, 01:14
Location: China

Questions about Base

05 Aug 2014, 20:47

  1. How can I get/write the actual base of an object without invokes a meta-function/inheritance-chain?
    Although base come from inheritance-chain, default not an normal key:
    'Meta-Functions' in 'Objects' wrote:If (and only if) no base object handles the operation, processing will continue as normal:
    Get: If the key is "base", the object's base is retrieved.
    So, can't use base.base in class method, because it will never invokes a meta-function.

    Unlike Lua, using a third party function get/setmetatable.
    There is ambiguity of key word base - a normal key or base - that have to face.
    The base can be get at the end of inheritance-chain until there's no other condition reached.
  2. Is there ObjRawGet?
    Or we can get a RawValue using a agent object(AO) which base's base is target object(TO), then RawValue:=AO[key].

    Code: Select all

    ;The base of AO:
    class B_AO extends TO{
    	__Get(k){
    		return base[k]
    	}
    }
    ;Or
    ;B_AO.base:=TO
    ;If TO is not a class.
    
    ;Then
    AO:=new B_AO
    RawValue:=AO[key]
  3. I only find two way to set base as normal key, before there is ObjRawSet.

    Code: Select all

    class c{
    	static base:=...
    }
    ;Second 
    ObjInsert(obj,"base",...)
    Just curious, not an important question.
TBO, I didn't read this topic carefully, only searched for ObjRawGet.
I'm waiting in hope for the formal/final draft v1.1.16. And will translate it, my energy is limited :(
Sorry for a series of questions, but it's more and more improve and perfect :P

Comes from post Behavior of base.k:=v, deliver THIS in inheritance chain.
Last edited by RobertL on 06 Aug 2014, 01:10, edited 1 time in total.
我为人人,人人为己?
lexikos
Posts: 9553
Joined: 30 Sep 2013, 04:07
Contact:

Re: Questions

06 Aug 2014, 00:18

RobertL wrote:How can I get/write the actual base of an object without invokes a meta-function/inheritance-chain?
You can't.
So, can't use base.base in class method, because it will never invokes a meta-function.
No, base.base, base.x := y and base.Remove() all do nothing because they are implemented by the target object itself, whereas base.base invokes a base object (but overrides "this"). There is room for improvement, but mostly only in v2, where backward compatibility isn't an issue.
Is there ObjRawGet?
No. Nor is there ObjRawSet in v1, which this thread is about.
User avatar
RobertL
Posts: 546
Joined: 18 Jan 2014, 01:14
Location: China

Re Re: Questions (Prototype Base RawData)

06 Aug 2014, 00:52

  1. I want to get the actual base, because I'm writing a wrapping/agent object which can't guarantee the input targetObject.base is real base of the targetObject.
    Or, currently the olny way is to require/protocol every object don't change/override-return the base in process of meta-chain (like __Get(base)).
    If can't, there's way to make a blind/dead-base, which can't get/re-set. :twisted:
  2. Oh, I see.. I had treat base as just the base object of current class which contain current method.
    o.base, o.k:=v, o.Remove() (or other built-in method of object-base/root object) should be implemented by the object itself.
    lexikos wrote:whereas base.base invokes a base object (but overrides "this")
    I didn't understand. base.base returns nothing (or value of normal key base if exist), means we can't get the base object of base this way (what I'm trying to do), is it correct?
  3. ObjRawSet. Yes, I get it from V2 0.49a, I'm using it.
    I found a way to get RawValue, using the feature of base in class-method with an agent object, see the post upstairs.
  4. Question wrote:Unlike Lua, using a third party function get/setmetatable.
    There is ambiguity of key word base - a normal key or base - that have to face.
    The base can be get at the end of inheritance-chain until there's no other condition reached.
    Is there any tip to get/set the actual base?
    (Ask again to dash my hope..but I am waiting it in V2 :) )
I feel apologetic to holding your time. :eh:
Should I post these in Wish List?

Other keywords/labels for searching: prototype-based, inherit, RawData
我为人人,人人为己?
lexikos
Posts: 9553
Joined: 30 Sep 2013, 04:07
Contact:

Re: Re Re: Questions (Prototype Base RawData)

06 Aug 2014, 03:12

RobertL wrote:I didn't understand. base.base returns nothing
Right. I said it does nothing. I also said it invokes a base object, so what I mean is that it invokes a base object, which in turn does nothing and has no external effect.
Is there any tip to get/set the actual base?
I already said you can't. That's a lie, because if you google "ObjGetBase" you will find the answer, but it depends on the internal structure of the object not changing (or immediately updating your code when it is changed, but I won't give any notice about such changes).

Future versions might support a way to do it.
User avatar
RobertL
Posts: 546
Joined: 18 Jan 2014, 01:14
Location: China

Re: Re Re: Questions (Prototype Base RawData)

06 Aug 2014, 04:36

lexikos wrote:if you google "ObjGetBase" you will find the answer, but it depends on the internal structure of the object not changing.
Future versions might support a way to do it.
Ahh, I will use base first, if necessary then ObjGetBase. I will hold out until new versions~ :P

I create a new topic, see About meta-functions change, Properties?.
我为人人,人人为己?
just me
Posts: 9423
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: v1.1.16 alpha - Properties

06 Aug 2014, 11:51

I've been hesitant to ask, however I'll do it now (before I'll burst with curiosity):

Would it be feasible to implement property variables shared between Get and Set and not visible/accessible from outside the property?
User avatar
RobertL
Posts: 546
Joined: 18 Jan 2014, 01:14
Location: China

Re: v1.1.16 alpha - Properties

06 Aug 2014, 19:00

just me wrote:Would it be feasible to implement property variables shared between Get and Set and not visible/accessible from outside the property?
I don't understand your purpose. Code could access the property using Object.Property from anywhere if it is public.
I think Get and Set of property should hold the only / private entrance of inner data. Such as InnerDataEntrance[PropertyObject]-InnerDataOfProperty.
我为人人,人人为己?
lexikos
Posts: 9553
Joined: 30 Sep 2013, 04:07
Contact:

Re: v1.1.16 alpha - Properties

07 Aug 2014, 02:48

just me: no.
just me
Posts: 9423
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: v1.1.16 alpha - Properties

08 Aug 2014, 00:54

How It Works
When the property definition is encountered at load time, it is translated into a Property object and two methods. When the Property is inherited by a derived object, instead of returning the Property, the getter or setter is called. An important consequence is that if MyClass is the exact class in which MyProperty is defined, MyClass.Property does not call the property - it returns the Property object.
Is it possible to get any useful informations from that object? I haven't found a way as yet.
lexikos
Posts: 9553
Joined: 30 Sep 2013, 04:07
Contact:

Re: v1.1.16 alpha - Properties

08 Aug 2014, 02:53

You can get each function, as I mentioned. The object doesn't contain any other information. The original property name generally isn't relevant (you can reassign the object to any key), but if you want it, you can extract it from one of the function names.

Return to “Announcements”

Who is online

Users browsing this forum: Ragnar and 40 guests