Objects - preview of upcoming changes

Discuss the future of the AutoHotkey language
lexikos
Posts: 6652
Joined: 30 Sep 2013, 04:07
GitHub: Lexikos

Re: Objects - preview of upcoming changes

06 May 2019, 17:08

I have updated the script to utilize the __Item property added in v2.0-a101.

The x.Item[y] properties are removed. Use x[y] instead. When the script performs recursion into a property, such as with obj.prop[y] or arr[x, y], it now uses v[y] instead of v.Item[y]. To handle this in custom objects, override __Item rather than Item.

Do not use x[y] to access properties or methods. Use x.%y% instead.

__Item (added in v2.0-a101) is not intended to be invoked directly, and I felt that Item was too common a name to reserve. By contrast, the Item property in previous versions of this script had to be invoked directly, so I left out the underscores.


@Helgef I think I've demonstrated that any decision I make during the (very long) alpha stage is not necessarily final. At this point, it made much more sense to extend the %expr% syntax than to add something new or overload something that had a different meaning before. I also figured that it will be used considerably less often than the indexing operator [].

I do not like <fn>(), and would not even if it was not ambiguous. a <b>(c) is a valid but strange way to use the comparison operators. (If the parser was more flexible, I would perhaps interpret it as a < b && b > c, and a < b < c would be a < b && b < c.)

(obj[expr])() would mean obj[expr].Call(), as the context of "invoking obj" is limited to inside the parentheses. obj[expr]() is still unambiguous (and allowed in the current build), but I thought it best for properties and methods to be consistent. If () is allowed as the call operator on any expression, obj[expr]() should probably also mean obj[expr].Call() (or perhaps obj[expr].Call(obj) if the () binds more closely to the previous operator).
User avatar
kczx3
Posts: 921
Joined: 06 Oct 2015, 21:39

Re: Objects - preview of upcoming changes

10 May 2019, 18:52

Just wanted to say thanks for all the "Note" messages on the documentation for Objects in v2 . It definitely helps in understanding what is still to change.
lexikos
Posts: 6652
Joined: 30 Sep 2013, 04:07
GitHub: Lexikos

Re: Objects - preview of upcoming changes

10 May 2019, 19:47

I probably should have added more notes to the Object method documentation, but it wasn't a priority since: 1) I was more concerned with getting the build released by the end of the day; and 2) I was planning to start making the bigger script-breaking changes immediately anyway. (v2.0-a102 shows that I got a bit side-tracked, but some of it touches on what I've started doing with Array and the more fundamental changes that are planned.)

One can see how things are likely to turn out by using Object.ahk or looking at its documentation. If someone wants to volunteer to update the documentation with more cautionary notes, please do so and submit a pull request. However, depending on how things go over the next four days (I have off work), it might be redundant. On the other hand, the v1 documentation should perhaps get some attention too.



So far the only change I anticipate to how things are in Object.ahk (that I can recall) is with bound checking for arrays. I intend to support a[i++] := v as a means to grow the array, but aside from allowing a[a.Length+1] := v, any out of bounds access should be prohibited. Otherwise, attempting something like a[hwnd] := v might succeed, but actually increase the length and capacity of the array by a very significant amount. If it is intentional, the script should do a.Length := hwnd first.

Since a[n] is equivalent to a[n+a.Length+1] when n <= 0, a[0] := v is equivalent to a.Push(v) when a[a.Length+1] := v is permitted.
Helgef
Posts: 3984
Joined: 17 Jul 2016, 01:02
Contact:

Re: Objects - preview of upcoming changes

11 May 2019, 02:40

lexikos wrote: I think I've demonstrated that any decision I make during the (very long) alpha stage is not necessarily final.
Yes.
I do not like <fn>(), and would not even if it was not ambiguous.
That is fine, but do you like %fn%()? I think expr in %expr% is mostly just var, so it doesn't matter much that two %s doesn't work as brackets. <> doesn't work with implicit concatenation, it doesn't work to do partial%name%. But, removing implicit concatenation might be a good idea anyways, and likewise, there is no need to support partial%name%, since %% now takes any expression. I think it is better to remove partial%name% and along with it any mention of pseudo-arrays. No user searching for arrays in ahk should risk encountering and wasting time on pseudo-arrays. Perhaps * expr and * expr (), it is easy to type and non-blob like. I like the *expr syntax in v1, although it is not very useful. If you scrap %% for vars and funcs, I wouldn't mind keeping %% for #include.
I thought it best for properties and methods to be consistent
If you let the brackets [ ] be used for calclated names, methods, properties and indices can all be consistent, with the exception that indices cannot be accessed with any literal notation (unless you come up with something). I do not think calculated var and func names (refs) necessarily needs to be consistent with object notation. A summary of ideas to look at,

Code: Select all

; Calculated names:
; A
obj[index]
obj.*prop
obj.*method()
*var
*fun()

; B
obj[index]
obj<prop>
obj<method>()
<var>
<fun>()

; C
obj[index]
obj.[prop]
obj[method]()
%var%
%fun%()

; D (now)
obj[index]
obj.%prop%
obj.%method%()
%var%
%fun%()
I think C is better than D, and very uncontroversial. Again, there is really no need for obj.partial%name%()

Syntax aside, I'm very excited about the more impactful changes you are making :thumbup:.

So far the only change I anticipate to how things are in Object.ahk (that I can recall) is with bound checking for arrays. I intend to support a[i++] := v as a means to grow the array, but aside from allowing a[a.Length+1] := v, any out of bounds access should be prohibited
Specifically a[a.Length+1] would be a common out of bounds error I believe, allowing it might greatly reduce the usefulness of oob checks. Allowing a.push to expand would suffice.
a[0] := v is equivalent to a.Push(v) when a[a.Length+1] := v is permitted.
This doesn't seem beneficial at all, first, v := a[0] wouldn't work which is a symetrical disturbance in my view, second, a[0] := v also makes a looking like it's zero-based. Especially repeated use would look wired,

Code: Select all

loop x
	a[0] := y	; :(
loop x
	a.push y	; :)
Further, a[0] might also be a common out of bounds error, for example from something like a[--i] := v in a loop which does one iteration more than intended.

Overall, I very much appreciate out of bound checks.

Cheers.


Edit:
@lexikos , since
lexikos wrote: I considered .[] and a bunch of other ideas over several years, and my conclusion was that if it does the same thing as %expr% (substitute an expression for an identifier), it should use that syntax even if no one really likes percent signs.
I do not think this discussion is going to lead anywhere and I do not want to further derail this topic, I only leave a few comments,
Leaving partial%name% as an error?
Yes, if it is disabled it should either yield an error or have some other meaning.
Pseudo-arrays will still be possible with %"partial" name%, but uglier.
Exactly why there is no need to support partial%name%, since %% now takes any expression.
Parameter lists have higher precedence than any preceding operator, so *expr(x) would call the function expr() and dereference whatever it returns.
My thought was that *expr() would be equivalent to %expr%(), to do %(%expr%())%, it would be **expr(), that is, in effect *() is a sequence rather than two distinct operators, I think it is similar to []().
Why should methods and properties be consistent with indices but inconsistent with variables and functions? Methods and properties are meant to be distinct from array elements, not consistent with them.
It seems to me, that it makes more sense to have a consistent object syntax, than a object syntax which is partially consistent with variable and func, and partially inconsistent within itself. Elements represent a value, methods represent an action more than than a value, a property, usually represents a value and an action, that is, elements and methods are both connected to properties, so imo it doesn't make more sense for properties to have consistent syntax with methods than it does with elements. Some times inconsistent is a good thing, we can call it variety instead, consider for example, %var%.%prop% vs %var%.[prop] where the inconsistent syntax is much more readable (yeah, it is subjective).
this is unnecessarily inconsistent. The last one should be obj.[method]()
I suppose I omitted the . for habitual reasons rather than for logic / preference. I'm neutral to it.

Cheers.
Last edited by Helgef on 13 May 2019, 03:18, edited 1 time in total.
lexikos
Posts: 6652
Joined: 30 Sep 2013, 04:07
GitHub: Lexikos

Re: Objects - preview of upcoming changes

11 May 2019, 21:28

I do not think calculated var and func names (refs) necessarily needs to be consistent with object notation.
I do not think we need to avoid %% to the point of adding inconsistency.

That is fine, but do you like %fn%()?
More than any of the proposed alternatives, all things considered; and especially more than <>.

I think it is better to remove partial%name% and along with it any mention of pseudo-arrays.
Leaving partial%name% as an error? Pseudo-arrays will still be possible with %"partial" name%, but uglier. Ugly syntax discourages use, which is an argument for keeping % in general... :HeHe:

Perhaps * expr and * expr (), it is easy to type and non-blob like.
Parameter lists have higher precedence than any preceding operator, so *expr(x) would call the function expr() and dereference whatever it returns. In C, I suppose you would do (*expr)(x); but in that case, what do we need * for? In C++, expr and *expr have different types, and could potentially both implement operator ().

obj.*prop and obj.*method() is how it's done in C++, except that prop and method are strongly-typed pointers to members/member functions.

For * expr (x) with auto-concat, it would not be a function call, but a variable reference. Without auto-concat, fun (x) should be a function call, giving * expr (x) the same issue with precedence as *expr(x).

If * binds to the following identifier, like ., then *expr(x) is much the same as %expr(x), which was valid in v2.0-a001.

If you scrap %% for vars and funcs, I wouldn't mind keeping %% for #include.
Neither one seems likely. I plan to eventually give #include a subset of expression syntax, but ideally it would not require much unique code. If the expression parser supported constant folding, it would be trivial.

If you let the brackets [ ] be used for calclated names, methods, properties and indices can all be consistent
Why should methods and properties be consistent with indices but inconsistent with variables and functions? Methods and properties are meant to be distinct from array elements, not consistent with them.

obj[index]
obj.[prop]
obj[method]()
Even putting aside variables/functions, this is unnecessarily inconsistent. The last one should be obj.[method]().

I considered .[] and a bunch of other ideas over several years, and my conclusion was that if it does the same thing as %expr% (substitute an expression for an identifier), it should use that syntax even if no one really likes percent signs.

With the addition of __item, both x[y] and x.prop[y] are invoking a property with parameter y, which has meaning only to the property. x.[y] is another form I considered at some point for invoking the default property (i.e. because the property name normally found between the dot and bracket was omitted). I think that makes more sense than invoking the property named by y. By convention in v1, x.() invokes the default method of x (i.e. calls a function object), actually passing the method name ("") to __call if it's a user defined object.

makes a looking like it's zero-based
That's a good point. I think I was fixated on how it could be used (before I implemented bound checking) given what results the formula give, and didn't really think about whether it should.
lexikos
Posts: 6652
Joined: 30 Sep 2013, 04:07
GitHub: Lexikos

Re: Objects - preview of upcoming changes

12 May 2019, 19:33

v2.0-a103: yet another update which breaks scripts in only relatively minor ways. My intention is to make it easier to prepare for the changes, while also preparing on the development side.

After v2.0-a102 I resumed work on the Array object, but immediately found that it would need a replacement for (or duplication of) SetCapacity and GetAddress. So now we have the Buffer object (see related discussion).

Currently the (unreleased) Array acts like a built-in object; it does not allow new properties or methods. I will likely hold it back until there is no visible distinction between built-in objects and user-defined objects (so expect that in the near future). In the end, it should be easy to update a script to work with the new changes.
User avatar
nnnik
Posts: 4274
Joined: 30 Sep 2013, 01:01
Location: Germany

Re: Objects - preview of upcoming changes

03 Aug 2019, 06:05

We use arr[entry] for numerical arrays.
We use object.entryName for objects.

I do not understand why you would decide to make associative arrays consistent with the object notation when they are more closely related to arrays?
Why keep the consistency between associative arrays and classes/their instances when the proposed changes are implemented?
If we start distinguishing between associative arrays and normal arrays it would make even more sense to differentiate between class structure and associative arrays.
Just as much as it was possible for arrays and associative arrays it will be possible for normal classes and associative arrays.

I can imagine that a change similar to this would increase the acceptance of the change for most users.
This would also allow us to change the implementation of associative arrays and classes in different directions.
I can understand why you dont want to support byref for associative arrays that are accessed like obj[var].
But for objects which have a high grade of similarity to normal variables it makes less and less sense to prohibit certain random actions (like byref for example).

Hope you have a nice day :)
Recommends AHK Studio
Helgef
Posts: 3984
Joined: 17 Jul 2016, 01:02
Contact:

Re: Objects - preview of upcoming changes

03 Aug 2019, 06:57

I do not understand why you would decide to make associative arrays consistent with the object notation when they are more closely related to arrays?
Hi nnnik, I'm not sure I follow exactly, but note that for maps you will use the index operator ([]) to access data. eg, myMap[entry]. The dot operator (.) will access properties and methods, eg, myMap.count and myMap.Has(entry).

Cheers.
User avatar
nnnik
Posts: 4274
Joined: 30 Sep 2013, 01:01
Location: Germany

Re: Objects - preview of upcoming changes

03 Aug 2019, 07:24

oops :oops:
Recommends AHK Studio
iseahound
Posts: 460
Joined: 13 Aug 2016, 21:04
GitHub: iseahound

Re: Objects - preview of upcoming changes

03 Aug 2019, 08:53

A few questions and comments. I'll be editing this post as I can sort through my thoughts.

1) Could you backport obj.%n% to v1? By losing dynamic access with obj[n] and given the fact that currently obj.%n% throws an error in v1 makes it impossible to write v1/v2 compatible scripts to ease the transition.

2) Have you considered separating methods and properties based on variance? This is currently one of the weakest "separations" as I can't see why I'd use a property obj.n over a data structure obj["n"] or even the established getter/setter interface obj.getN(). Currently inheritance is covariant, with the new methods being called over the base methods. If properties are contra-variant, the base properties would overwrite the new properties. It's something to think about besides "easier data access". Properties are a highly underutilized part of AutoHotkey.

3) Might be necessary to backport prototype as well.

4) I can't tell if I'm doing things right, but are numeric properties supposed to return the corresponding data? For example arr := Array("a", "b", "c") works with both arr[1] and arr.1. If so that's very much needed for code readability!

5) In the map object if the same thing is possible that'd be great. Properties that are purely numeric should evaluate to the corresponding index. _map.4 evaluating to _map[4] because of a built in property would be nice. This is mostly for readability reasons while parsing through very deep objects or as a convenient shorthand.

6) Any resolution on the IsFunc(obj.method) problem? Currently trying to port a script. Not sure if GetMethod() like you were suggesting is the best choice.

7) Going off of the previous question the problem with Function objects has to be resolved in a manner that promotes easy function composition. I don't think Func("fn").Bind() or ObjBindMethod(obj, "method") are intuitive at all. Functions might be first-class, but partial functions are incredibly difficult to write and compose, that they might as well not be. It's really awkward having to put the function name in quotes or add an additional symbol.

Thanks. Looking forward to seeing a release implementation.
Helgef
Posts: 3984
Joined: 17 Jul 2016, 01:02
Contact:

Re: Objects - preview of upcoming changes

04 Aug 2019, 12:09

4) I can't tell if I'm doing things right, but are numeric properties supposed to return the corresponding data? For example arr := Array("a", "b", "c") works with both arr[1] and arr.1. If so that's very much needed for code readability!
Supposed as in planned, I can't answer, but supposed as in shouldn't(imo) and doesn't I can answer, no. That would require custom behaviour. I expected this to work

Code: Select all

class myArray extends Array {
	__new(p*) => this.push(p*) ; must pass at least one arg
	__get(ind, *) => this[ind]	
}
a := myArray.new(1)
msgbox a.1 "`n"  a[1]
but there seems to be a problem with (dynamic properties) obj.i when i is an integer. Edit: This was fixed now :thumbup: .

7) Perhaps this would interest you :arrow: Changes to %fn%(), fn.call() or func('fn') syntax?.

Cheers.
Last edited by Helgef on 24 Sep 2019, 09:25, edited 1 time in total.
lexikos
Posts: 6652
Joined: 30 Sep 2013, 04:07
GitHub: Lexikos

Re: Objects - preview of upcoming changes

10 Aug 2019, 00:03

iseahound wrote:
03 Aug 2019, 08:53
Could you backport [x] to v1?
The answer to questions of this form is virtually always yes. It could be done.

As for feasibility; it is a question of how much work it would be to port the change and anything that the change relies on with the additional constraint of requiring backward-compatibility.

As for would I, the answer is likely no, I'm just not interested. If or when that changes, maybe I will, maybe I won't.
By losing dynamic access with obj[n] and given the fact that currently obj.%n% throws an error in v1 makes it impossible to write v1/v2 compatible scripts to ease the transition.
Personally, I think aiming for two-way compatibility is a waste of time. v2 was started on the premise that many changes for the better would require breaking compatibility with other scripts. It may be that some of the changes so far could be implemented in v1 without breaking scripts, but 1) at a higher cost, and 2) only a minority of the changes. The average script cannot work in both versions, and even a script specifically designed to do so is likely to have bugs due to differences that weren't considered.

I backported some changes in v1.1.21, in the interest of bringing some of the improvements back to v1, and to a lesser degree to improve compatibility. But v2 is still in alpha, and has changed since then. As a result, Loop Reg/File in v1 is locked into having legacy syntax, incompatible with v2.

As for .%x%, it doesn't interest me at the moment. If it's important to you, why don't you backport it to v1?
2)
I fail to see your point.
3) Might be necessary to backport prototype as well.
It isn't. It can be (and has been) implemented in script. Prototype is hardly more of a problem than the numerous other differences (to objects), almost all of which can also be (and have been) implemented in script.

The abstraction added by this script comes at a cost, but backporting these changes while preserving backward-compatibility would also come at a cost, perhaps a greater cost than implementing the changes in the first place.
4) I can't tell if I'm doing things right, but are numeric properties supposed to return the corresponding data?
The script stores array elements as properties because variadic function calls in the current alpha require it. It is not my intention to retain this behaviour. However, it is possible (in the next alpha) to define a meta-function for all Arrays, or to define your own Array subclass.
6) Any resolution on the IsFunc(obj.method) problem?
What problem?
  • If you want to know whether the object has a method, use the obvious method: HasMethod.
  • If you want to know whether a value is a reference to a Func object, use Type.
  • If you want to know whether a value is callable, call it - or use .HasMethod("Call").
  • If you want to know whether a value is a function name, use IsFunc as before.
bourdin07
Posts: 15
Joined: 23 Jun 2019, 12:57

Re: Objects - preview of upcoming changes

13 Sep 2019, 04:12

Separate data and interface: array elements and properties/methods are separated, and generic objects do not have array elements by default and do not support obj[n]. Use obj.%n% (v2.0-a101+) to access dynamic properties/methods.
Why use obj.%n% instead obj[n]
I prefer obj[n] seriously
guest3456
Posts: 2656
Joined: 09 Oct 2013, 10:31

Re: Objects - preview of upcoming changes

13 Sep 2019, 23:58

bourdin07 wrote:
13 Sep 2019, 04:12
Separate data and interface: array elements and properties/methods are separated, and generic objects do not have array elements by default and do not support obj[n]. Use obj.%n% (v2.0-a101+) to access dynamic properties/methods.
Why use obj.%n% instead obj[n]
I prefer obj[n] seriously
maybe because he wants to keep obj[n] syntax for linear numeric array elements only

Helgef
Posts: 3984
Joined: 17 Jul 2016, 01:02
Contact:

Re: Objects - preview of upcoming changes

19 Sep 2019, 05:58

obj[n] is not only for linear numeric array elements. n can be anything depending on how __item is implemented.
Why use obj.%n% instead obj[n]
To separate data and interface. Perhaps you suggest to switch the roles of obj.%x% and obj[x]?

Cheers.

Return to “AutoHotkey v2 Development”

Who is online

Users browsing this forum: No registered users and 7 guests