Objects+

Discuss the future of the AutoHotkey language
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Objects+

22 Oct 2018, 22:11

Split from:
Objects - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=37&t=57591

- Thanks again. The material is very complex, and so the responses have been a bit slow. It might help if someone could summarise what new functionality is available, and if any old functionality is affected (breaking old scripts).

- Here are my concerns re. the functionality of objects, taking into account what I've understood from reading through the md files and looking at the code.

OBJECT MODES
- one object type for everything has proved a good, simple and clear paradigm, normally when I create an array, I think, I'd like the standard AutoHotkey array but with these deviations (even if you had multiple object types e.g. Map, granular mode control might still be useful)
- so, for any AHK object, there'd be an on/off/count etc mode for each of the following:
- (additionally you could have functions that can override defaults)
- integer keys: e.g. key name 1 (string) is converted to 1 (integer) (perhaps this could be bypassed via ObjRawGet/ObjRawSet, e.g. so that hex numbers as strings are returned in the correct order)
- takes precedence: keys/properties take precedence (or perhaps, in one mode, adding a key replaces a property, and vice versa) [EDIT: perhaps not needed]
- takes precedence: keys/methods take precedence (no longer an issue in Object.ahk: you can create a key with the same name as a method)
- [KEY] set default value: blank string/0/new empty object (default value for a non-existent key) (e.g. for immediate concatenate/increment/push and for code simplification/greater readability)
- object type: case-sensitive/case-insensitive key names
- object type: e.g. fixed-length array, e.g. integer keys only, e.g. prevent certain key names from being added (experts can use these settings to achieve more efficient performance, equivalent to separate object types in other programming languages)
- object type: (in general: 1 object with modes/restrictions, versus different object 'types')
- enumerator: specify a common built-in enumerator type that will be used by a for loop [see 'FUNCTIONALITY (ENUMERATORS)' below]
- perhaps a Mode method for objects (to handle such options) (or something else)

FUNCTIONALITY (SIMPLICITY)
- [KEY] I would suggest .key and [key] be equivalent, for simplicity and to avoid bugs (and to use methods for any special functionality)
- _NewEnum and Next, seem like simple intuitive names, that perhaps wouldn't benefit from being changed (new related methods could still be added)
- [KEY] I would consider having everything available as methods and no built-in properties (or any built-in properties also available as methods) [Length/Count properties could be built-in, but if a key existed with the same name, it would override them, whereas Length()/Count() methods would always work]
- ... perhaps obj.base := {} and obj["base"] := {} should not set the base object (perhaps a base() method could allow direct assignment)

FUNCTIONALITY (ENUMERATORS)
- functionality to support common enumerators would be well worth considering:
- loop as variadic functions (linear array) (keys 1 to .Length(), including undefined keys)
- loop integer/string keys only
- loop alphabetical/key creation order
- loop reverse order (and the ability to loop and delete) (get the last key name)
- (perhaps loops should only retrieve keys by default, or keys and properties)
- (functions to list all properties/list all methods)

FUNCTIONALITY (GENERAL)
- multiple assign: a shorthand or function that can accept [...] or {...} for adding keys to an existing array (e.g. create a blank array, set the modes, add keys)
- (ObjToString, or another function, to list keys/values)
- (methods for strings/integers/floats, not just objects)
[EDIT: the only functionality I especially want in this regard, can be implemented by the user like so:]
if var in/contains comma-separated list/array - Page 2 - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=37&t=43593&p=247622#p247622
"".base.base := {split:"StrSplit"}
- [KEY] (classes: it might be useful to have the ability to do something whenever non-existent *and existing* keys are read from/written to, although this can be done with existing functionality, by placing data inside the class object) [note: __Get/__Set, in AHK v1, only handle *non-existent* keys]

LISTS
- I find with anything it helps to have a list (more or less complete):

Code: Select all

methods:
__forin()
__getprop()
__initclass()
__setprop()
Clear()
DefineMethod()
DefineProperty()
DeleteMethod()
DeleteProperty()
Has() [cf. HasKey]
HasMethod()
HasOwnProperty()
HasProperty()
New() [cf. .__New/new]
Properties()
ToString()

also:
Count property [cf. Count method]
Item[] [not 'Item()']
ObjFromPtr function
prototype property
Last edited by jeeswg on 23 Aug 2019, 18:42, edited 20 times in total.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
User avatar
nnnik
Posts: 4500
Joined: 30 Sep 2013, 01:01
Location: Germany

Re: Objects

23 Oct 2018, 00:02

Maybe it would be better if you post a few things about this in Ask for Help than one massive post here. That would make explaining these things a little bit easier.
And also I don't think that this is the correct place for new suggestions.
Recommends AHK Studio
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: Objects

23 Oct 2018, 00:26

- @nnnik: Please don't post things like this:
Maybe it would be better if you post ... in Ask for Help .... That would make explaining these things a little bit easier.
- I presented things simply, but you have to look more deeply at what I've said to appreciate it. If you can come up with some constructive criticism then go ahead, but be clear and try to understand the intentions behind things.
- Everything I mentioned is tightly knit to Object.ahk, except the mention of methods for strings/integers/floats (I included it so that all of my AHK v2 object concerns were in one post, and because I had thought it might be covered).
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
User avatar
nnnik
Posts: 4500
Joined: 30 Sep 2013, 01:01
Location: Germany

Re: Objects

23 Oct 2018, 01:58

I am understanding the intentions behind your suggestions.
But yeah I could point to every suggestion and tell you why it is a bad idea, where you misunderstood what lexikos implied and so on.
Also I don't really want to disrupt this constructive topic with a discussion as to why your new suggestions are not that useful.
So I'm asking you to make a new topic or if you would be ok with just splitting this off - because your topic doesn't seem to have a lot to do with Objects.ahk
Recommends AHK Studio
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: Objects

23 Oct 2018, 02:22

If you have any problems, DM me. (Nothing personal, but I just can't spend any more time debating this today.)
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
User avatar
nnnik
Posts: 4500
Joined: 30 Sep 2013, 01:01
Location: Germany

Re: Objects

23 Oct 2018, 04:34

I have split the topic because it does not relate to the other one.
- one object type for everything has proved a good, simple and clear paradigm, normally when I create an array, I think, I'd like the standard AutoHotkey array but with these deviations (even if you had multiple object types e.g. Map, granular mode control might still be useful)
It could be useful, but even more useful is having multiple types.
Each of the described types cannot be changed into the other so changing it is not an option.
Each of the described types contains completely different data and contains a different interface.
Each of the described types would have their respective names returned by type().
Each of the described types would imply that they have their types fixed after their creation and that nothing could change that.
The list of reasons and advantages goes on a lot longer.
- integer keys: e.g. key name 1 (string) is converted to 1 (integer) (perhaps this could be bypassed via ObjRawGet/ObjRawSet, e.g. so that hex numbers as strings are returned in the correct order)
- takes precedence: keys/properties take precedence (or perhaps, in one mode, adding a key replaces a property, and vice versa)
- takes precedence: keys/methods take precedence (no longer an issue in Object.ahk: you can create a key with the same name as a method)
- default value: blank string/0 (default value for a non-existent key)
- object type: case-sensitive/case-insensitive key names
- object type: e.g. fixed-length array, e.g. integer keys only, e.g. prevent certain key names from being added (experts can use these settings to achieve more efficient performance, equivalent to separate object types in other programming languages)
- object type: (in general: 1 object with modes/restrictions, versus different object 'types')
- enumerator: specify a common built-in enumerator type that will be used by a for loop
- perhaps a Mode method for objects (to handle such options) (or something else)
It seems like this could possibly mean something if I try hard enough, but for now I will just treat it as incomprehenisble gibberish.
What are you even doing here? Are you listing topics that relate or are you making suggestions?
Please write in full sentences.

- _NewEnum and Next, seem like simple intuitive names, that perhaps wouldn't benefit from being changed (new related methods could still be added)
You have no idea why the .call key is special or?
When you have understood it you will conclude that this is new functionality different from previous existing functionality.
The old functionality has not been replaced - it might be retained in an additional way or new functionality might replace it.
Either way naming a key "_NewEnum" when it doesn't have anything to do with creating a new enumeration is hardly good practice.


The rest ranges from valid suggestions to things you just like to do. It's kinda hard to differentiate when everything is so tightly packed.
In the end a whole 3 sentences of your post actually relate to Objects.ahk.
Please try to stay on topic and keep your points concise.
Recommends AHK Studio
guest3456
Posts: 3454
Joined: 09 Oct 2013, 10:31

Re: Objects

23 Oct 2018, 09:28

lol

User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: Objects

23 Oct 2018, 11:41

- @nnnik:
- How many object types do you want?
- Some distinctions can't or shouldn't be implemented by creating a new type.
- Should an associative array (integer key names automatically converted to string key names) be a separate type from an associative array (integer key names disallowed).
- Should a case-sensitive array be a different type from a case-insensitive array. What strings should Type report.
Each of the described types cannot be changed into the other so changing it is not an option.
- It's quite common in programming languages that people have to convert objects to different types to perform different actions.
Each of the described types contains completely different data
- Or are most/all of them a subset of the existing AHK basic object.
- Internally, some settings/types could have a separate storage model, some could be one model with different options.
- You could do something like this (the syntax could be different):

Code: Select all

;linear array optimised for performance
obj := []
obj.mode().isArray := 1
obj.mode().allowStringKeys := 1
;obj.mode().allowNewKeys := 1
;obj.mode().keyCount := -1
obj.add([1,2,3])

;linear array not optimised for performance
obj := [1,2,3]
- This would create an optimised integer-key array, but if you added a string key to it, the object would be converted to an associative array. The type reported by Type would be: basic AHK object.

- To summarise the list you mentioned, objects could have settings for:
- whether keys/methods/properties take precedence [EDIT: this might not be needed]
- key names: integer key names (automatically converted to strings/blocked)
- key names: string key names (automatically converted to integers/blocked)
- key names: case sensitive/insensitive
- key count: fixed key count/block adding keys of a certain type/block adding keys

- Re. _NewEnum and Next, there was an ambiguous sentence. What's your point about .call?
- A lot was said re. enumerators, my query was, let's say you wanted an object loop to take place in reverse/date creation order. How could you specify that?
Last edited by jeeswg on 09 Nov 2018, 23:52, edited 1 time in total.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
User avatar
nnnik
Posts: 4500
Joined: 30 Sep 2013, 01:01
Location: Germany

Re: Objects

23 Oct 2018, 16:31

- How many object types do you want?
As many as are useful, as few as possible without cutting corners or creating worse alternatives.
- Should an associative array (integer key names automatically converted to string key names) be a separate type from an associative array (integer key names disallowed).
- Should a case-sensitive array be a different type from a case-insensitive array. What strings should Type report.
Yes indeed. But generally there are a few rules regarding this.
Every change that results in changing the behavior of an object can either be implemented by creating a new class or by adding a dynamic state to this object.
In your case that would be the mode - it changes the behavior of the arrays.

Consider the following example:
You have a lot of chess figures. And you want to move them across the board.
Their behavior greatly changes with they type they are from.
So you might want to consider using different classes to each chesspiece and give them all different rule logic and pictures.
And since they are essentially all pieces you give them a common class they inherit from:

Code: Select all

class ChessPiece {
;stuff like position and color..
	getPicture() {
		return this.picture[this.color]
	}
}
class Pawn extends ChessPiece {
	static picture := {Black:"BlackPawn.png", White:"WhitePawn.png"}
;stuff like the picture thats used and the movement logic..
	getMoves() {
		;...
	}
}
class Rook extends ChessPiece {
	static picture := ..
;stuff like the picture thats used and the movement logic..
	getMoves() {
		;...
	}
}
However there is that specific move where the pawn can turn into any other piece when it reaches the last row of the enemies side.
At that point it actually makes sense to rather make the type of the piece part of the state and the piece itself a single class.
This eases conversion and makes things work seemlessly.
In this design you would only create one big class. That class would contain a value and make that class treat each piece differently.
For example you could do so with big If/Else ladders:

Code: Select all

class AllChessPieces {
	static picture := {Pawn: {Black:"BlackPawn.png", White:"WhitePawn.png"}}
;current position, the current type of piece and the logic of the movement
	getMoves() {
		;..
		if (this.type == "Pawn")
		;...
		else if (this.type == "Rook")
		;...
	}
	getPicture() {
		return this.picture[this.type, this.color]
	}
}
I think it's fine to code that way if you want to be fast.
If you want to achieve good results it would be better to create ChessPieceType classes.
This keeps all the information about a specific type in one place and makes the code easier to read, test and debug -> easier to maintain.

Code: Select all

class AllChessPieces {
;current position, the current type of piece
	getMoves() {
		return this.type.getMoves()
	}
	getPicture() {
		return this.type.picture[this.color]
	}
}

class PawnType {
	static picture := ...
	getMoves() {
		;...
	}
}
This is an architecture sololy made for and focused on the rules.
It does not look at the pictures at all.
What if you wanted to support different skins for the chessboard.
What if you need to notify the game board itself that the picture changed...
But yeah I'm getting off track here.



A completely different case is when new features are added or removed and when not all abilities are shared.
If you have 2 things and their Interface changes - meaning they gain new abilities beyond changing their behavior when using existing ones, then you should use different classes.
A keyboard and a mice are both InputDevices and you can set them up to some extend and they share a few attributes.
So them having a relation is not far fetched - but you cannot .move() a keyboard and expect anything to happen.
You cannot .scroll() a mousewheel on a keyboard. You cannot .toggleCapsLock() on a mouse.
You actually could make these work by only using a single class. But then the inputs they posess would become states.
You would have to add an entry for every key the keyboard owns and would have to add a special entry for mouse movements and so on...
And have you thought about joysticks and other controllers?
Doing this creates a problem which can only be solved in a good way that is far removed from your actual goal - which is simplicity.

Just setting a few types and limiting the whole library just to keyboards and mice without any chance of an outsider participating might be another idea.
This is actually what you suggested. However I don't think it's a good choice for the AHK source to limit itself like that.



If we apply this to your questions:
A numerical array containing integers vs an associative array containing strings.
Both are lists in a way so they definitely have a lot in common.
They both contain other things, are accessible using list[] and have a few other similarities like .count(), .hasKey()...
So they definitively could be the same class if we only look at the similarities.
There are differences. An array is accesses and stores it's data entirely different than a associative array.
There are almost no similarities as to how count() hasKey() and all other methods are actually implemented.
Code wise you would do an else/if list in every method and do this across the entire class essentially creating more work than just seperating the 2.
But even worse than that are other problems - while these 2 things share a few settings and methods there are ones that they do not share.
While an Array maybe has a minIndex() and maxIndex() it wouldn't make sense to have the same method in an associative array.
What should the associative array return? The lowest numerical number it was passed? What if it had a Hex number passed to itself that is lower than the lowest numerical number?
Even worse - in the associative array it makes sense that you can define a sorting function which defines the sorting of the string keys.
However in the array the sorting is fixed - 0 comes before 1 and 1 comes for 2 and so on...
And we actually use this fixed order in the array to access the array - we cannot just redefine it.
At this point we have methods that don't do anything even though they should report or do something to the object.
This is something that should never happen. If a class has a specific method it should be available.

That's why we use 2 types here that inherit from a common parent:

Code: Select all

class List {
;...
}

class Array extends List {
;...
}

class AssociativeArray extends List {
;...
}
If you just need to use the features of the List you would use some form of if (instance implements List) where implements is an operator that checks if a class is the instance of a class or one of its children.
If you need to use more you just check for the speicifc classes.

For CaseSensitive and CaseInsensitive AssociativeArrays I would just use a state meaning that they are the same class.
Recommends AHK Studio
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: Objects

23 Oct 2018, 17:19

- The way I see is that you have one basic object type.
- Externally:
- You can set various settings/modes/states. (Object info such as count and capacity is already stored.)
- Either there is one object type or multiple types. (I'm neutral on this.)
- Internally:
- The object's 'type' is based directly on the settings.
- AFAIAC whether there is one or multiple types has little to no impact on the internal C++ code, or the structure of the classes internally.
- None of my suggestions would make it harder to add new functionality in future.
- The settings/'types' could for example allow you to use a simple array implementation, which could then be (to the user) seamlessly converted into an associative array (also a setting could block such conversions).

- My main concerns are usually:
- Externally:
- Is it newbie-friendly. Ease of use, e.g. the ability to force a key name to be a string (when creating hex value key names). Avoid confusion, e.g. separate keys and methods v. assign a key, lose the method.
- Is it convenient. E.g. set the default value to 0 instead of a blank string, loop and delete, loop reverse order/alphabetical order/key creation order, store integers as numbers or strings, case-sensitive/case-insensitive.
- Performance. Although, I haven't ever found the AHK v1 objects particularly slow.
- Internally:
- As with all of my suggestions, I try to come up something that is easy to implement and expand.

- (We already have a defined behaviour for MinIndex() and MaxIndex() on an associative array: they do nothing unless integer keys are present.)
- (We already have a defined behaviour for sorting an associative array: integer keys then string keys.)
- (If you try to use an non-applicable method, you get an error, or an appropriate result, or something.)

- One annoyance in other programming languages is inconsistencies over whether you can use .key or [key] or get(key), keeping things consistent across 'types' is worthwhile.

- At the moment, none of the object types are especially different from each other (outwardly), re. array/object/map.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
User avatar
nnnik
Posts: 4500
Joined: 30 Sep 2013, 01:01
Location: Germany

Re: Objects

23 Oct 2018, 23:42

Returning nothing or throwing an error is not well defined behavior.

What I mean by order is that it should be possible for the user to change the order of an associative array.
Recommends AHK Studio
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: Objects

27 Oct 2018, 00:25

- Possible modes for a Mode()/Opt() method:
- (Other words for mode: configuration/default/flag/mode/option/setting/state/switch/type.)
- (I'm not concerned re. what the method would be called, or what these keys would be called.)
- [KEY] List of modes/settings:

Code: Select all

;note: where 'array' means linear array

;the following functionality is common in other languages:
;priority:
StoreCreationOrder := 0 ;[KEY] maps: key names
CaseSensitiveKeys := 0 ;[KEY] maps: key names

;not a personal priority (but allow you to recreate the functionality of types from other languages):
AllowStringKeys := 1 ;maps: key names
AllowIntegerKeys := 1 ;maps: key names
AllowStoreObjects := 1 ;maps/arrays: values
AllowStoreStrings := 1 ;maps/arrays: values
AllowStoreIntegers := 1 ;maps/arrays: values
AllowStoreFloats := 1 ;maps/arrays: values
AllowNewKeys := 1 ;for: classes, fixed-length arrays, read-only maps/arrays
KeyMax := "" ;maps/arrays: key names (including for fixed-length arrays)
KeyMin := "" ;maps and possibly arrays: key names

;the following functionality may or may not be common in other languages:
ForceLookIntegralAsStringKeys := 0 ;maps: key names e.g. integer/string 1 treated as string 1
ForceLookIntegralAsIntegerKeys := 0 ;[AHK v1: 0, AHK v2: 1] maps: key names e.g. integer/string 1 treated as integer 1 (a function to bypass this may be better than a setting) [EDIT: no longer relevant, prior to AutoHotkey v2.0-a104, if a new key name was requested that looked like an integer it was created as an integer, even if it was of string type]

;the following functionality may or may not be common in other languages (further):
DefaultValue := "" ;[KEY] maps/arrays: easily change the default value e.g. blank string or 0
DefaultEnum := "" ;maps and possibly arrays: easily change the enumerator used by 'for key, value in object'
Type := "Object" ;convert the object to another type
- [EDIT:] Here is an example of the issue where AHK v2 (until it changed in AHK v2.0-a104) forced string keys that looked like integers to be assigned as integer keys. I.e. at one point you couldn't have both a string key "1" and an integer key 1 at the same time. Having both at the same time was possible in AHK v1, and is now possible in AHK v2 (since AHK v2.0-a104).

Code: Select all

;test: are oObj[1] and oObj["1"] separate, or both treated as oObj[1]
MsgBox(A_AhkVersion)

if Func("Map")
	vFunc := "Map" ;AHK v2.0-a104
else
	vFunc := "Object" ;AHK v1.1 to AHK v2.0-a103
oObj := %vFunc%()
oObj[1] := "number"
oObj["1"] := "string"
MsgBox(oObj[1])

;for AHK v1:
MsgBox(vText)
{
	MsgBox, % vText
}

;'MsgBox(oObj[1])' reports:
;number: AHK v1.1
;string: AHK v2.0-a103 ;unusual
;number: AHK v2.0-a104
- Perhaps {base} could work differently when creating objects e.g. obj := {base:{}} [EDIT: Perhaps not.]
- But not when assigning keys e.g. obj.base := {}
- Although this could be made available: obj.base() := {} [EDIT: we have ObjGetBase/ObjSetBase.]

- One query is a way to quickly create a static, 0-based, optimised (as fixed length, integer keys only) array.
E.g. static oArray := ["zero", "one", "two"], this would be static but 1-based and non-optimised.

- I remembered more specifics re. 'take precedence'. If a key isn't found in the instance object, the base object is checked, and, a property/method/key in the base object could be bypassed, if a new key is created in the instance object.
- A classic example: creating a key called _NewEnum/HasKey(/base?) overriding for loops/HasKey().
- A simple remedy is to separate keys and methods. Methods requiring parentheses. Which is what it appears Object.ahk has done.
- Perhaps other 'take precedence' issues remain, but it's not immediately clear to me.

- @nnnik: (You didn't change the thread title when you created the thread. I've renamed it.)
- Change the order that for loops return keys?
Last edited by jeeswg on 26 Aug 2019, 10:16, edited 4 times in total.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
lexikos
Posts: 9553
Joined: 30 Sep 2013, 04:07
Contact:

Re: Objects+

18 Nov 2018, 00:53

I think nnnik made the right call in splitting the topic, but I'll address it in the context of the original topic.

This seems to be a list of your own half-formed ideas, in parts contradicting Object.ahk without sufficient (or any) explanation.
I would suggest .key and [key] be equivalent, for simplicity and to avoid bugs
I can interpret your suggestion two ways:
  • In contradiction with Object.ahk and the theories provided in the readme, do not separate properties and array elements. Why? How is this simpler? How does this avoid bugs? The mixing of properties (and methods) and array elements is a cause of bugs.
  • Keep all other changes outlined in Object.ahk, but make [key] actually perform property lookup and not array access. That would be more likely to cause bugs (and less useful) since [key] is most often used for array access.
_NewEnum and Next, seem like simple intuitive names, that perhaps wouldn't benefit from being changed (new related methods could still be added)
I think you've missed the point of changing them. Also, I'm not sure how "_NewEnum" is supposed to be intuitive (being able to remember the name after having already learned it in v1 or from working with COM is something else).
I presented things simply, but you have to look more deeply at what I've said to appreciate it.
I personally don't care whether you go into more detail, but you should know that I'm not going to go to much effort to try to understand what you've written (or left unsaid). In that case, who is going to implement your suggestions?
Should a case-sensitive array be a different type from a case-insensitive array. What strings should Type report.
Case-sensitivity cannot be changed after array elements are added without potentially invalidating data, so it makes sense to select case-sensitivity only when the array is created. The most concise way to do this would be to specify a different class name. Type, as always, would return the name of the class.
It's quite common in programming languages that people have to convert objects to different types to perform different actions.
I disagree. It is quite uncommon in programming languages to be able to convert an object from one type to another, unless you're talking about creating a new object based on an old one, in which case the restriction nnnik posed is not an issue. Typecasts change the type of an expression, and might create a new object or give you a reference to a different interface, but will not change the type of the original object.
[...] let's say you wanted an object loop to take place in reverse/date creation order. How could you specify that?
Create a function which creates an enumeration function which returns items in that order. Let the function accept the object as a parameter, and optionally make the function a method of the object. Then call the function/method and pass its result (which is the enumeration function) to the (future) for statement. Basically the same as before, except that the enumeration function doesn't need to be (but can be) wrapped in a separate object, and different terminology is used.

There are also other, more general solutions.
Perhaps {base} could work differently when creating objects e.g. obj := {base:{}}
I feel that obj := {xxx: ...} should always be equivalent to obj := {}, obj.xxx := ..., given that xxx is an identifier (unquoted literal) in both cases. That would mean {base:{}} would assign the base property (which could hypothetically be the built-in property which sets the object's prototype, or just a normal property). Regardless, you can use obj := new {} (in v2).

Re "possible modes":
  • StoreCreationOrder: If any type will preserve the order in which keys are added, it will likely be mandatory behaviour for that type.
  • AllowStringKeys/AllowIntegerKeys: I see no need to make this an option. Map allows all types and differentiates between them. If you don't want that, use another type or be selective about what you pass to the map. In theory, restrictions can be imposed by redefining the item operator (currently the Item property) for one map instance or for a subclass of Map.
  • AllowStore...: I see no need for this.
  • AllowNewKeys: Instances of classes other than Map (and hypothetically other custom collection types) don't have keys. Array only has elements (values), and other objects only have properties. That aside, I have considered something equivalent to JavaScript's Object.preventExtensions(object1), which prevents new properties from being added to object1.
  • ForceLookIntegralAs...: I don't get it.
  • DefaultEnum: That would be the __forin method, which can be redefined with DefineMethod.
  • Type: I don't get it.
One query is a way to quickly create a static, 0-based, optimised (as fixed length, integer keys only) array.
I have no intention of providing a way to create 0-based arrays, other than ComObjArray() or defining your own array type.
this would be static but 1-based and non-optimised
No, it would be an Array. All Arrays would be 1-based and optimized for direct access by index.
nnnik wrote:At that point it actually makes sense to rather make the type of the piece part of the state and the piece itself a single class.
When playing chess, it would be typical to physically replace the pawn with the appropriate piece, since a pawn is generally just a piece of wood/plastic/stone and can't change its appearance. It would be analogous to replacing the existing instance of Pawn with an instance of the new type (a different object, most likely a new Queen).
User avatar
nnnik
Posts: 4500
Joined: 30 Sep 2013, 01:01
Location: Germany

Re: Objects+

18 Nov 2018, 02:27

When playing chess, it would be typical to physically replace the pawn with the appropriate piece, since a pawn is generally just a piece of wood/plastic/stone and can't change its appearance. It would be analogous to replacing the existing instance of Pawn with an instance of the new type (a different object, most likely a new Queen).
This is not a good reasoning. The restrictions of the real world do not matter in the implementation of the game. If I understand your post correctly and you say that the most naive implementation simply removes one piece and replaces it with another then I agree. But it is certainly not the best way.
Case-sensitivity cannot be changed after array elements are added without potentially invalidating data, so it makes sense to select case-sensitivity only when the array is created. The most concise way to do this would be to specify a different class name. Type, as always, would return the name of the class.
The easiest way would be specifying different parameters in the constructor.
Recommends AHK Studio
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: Objects+

23 Nov 2018, 19:52

- @lexikos: Thank you. I always appreciate your responses, and especially so for this topic.
- Object.ahk was difficult to follow. It would be useful to have a summary of changes that would break existing scripts, and of the likely benefits of new additions for newbies.

- Some background:
Spoiler
METHODS/PROPERTIES: PROPERTY VERSIONS OF METHODS
- It might be useful to have Length and Count properties.

METHODS/PROPERTIES: METHOD/FUNCTION VERSIONS OF PROPERTIES
- I had considered ObjGetClass (and ObjSetClass), but this is already possible:
classname := ObjGetBase(obj).__Class

0/1/N-BASED INDEXES: LINEAR ARRAY STARTING AT INDEX
- I'm fine with arrays only being 1-based, using ComObjArray for 0-based indexes is a good workaround.
- If introduced, 'obj.Count(FirstKey, LastKey)' could help facilitate 'n-based arrays' via a Map object. However, this also might be useful instead, or in addition: 'obj.Count(CriteriaObject)'.

ASSIGN: OBJ.XXX SYNTAX
- I had envisaged obj.xxx as 'key else property'. And ObjGetValue/ObjGetPropertyValue, and key/property/key-or-property enumerators, to guarantee the correct result.
- Removing obj.xxx syntax for keys has disadvantages (and advantages), however, the main 2 problems have been addressed:
GitHub - Lexikos/Object.ahk: This library for AutoHotkey v2-alpha tests some ideas for potential changes to how objects work.
https://github.com/Lexikos/Object.ahk
{a: b, c: d} is most often used to create ad-hoc objects with known properties, so these should be properties.
...
Array also allows a.1 to refer to element 1 of array a
- Whether 'obj.xxx' for keys will be sorely/hardly missed is not yet clear. It could be useful to have a 'key else property' mode, to allow arrays to work more like they did in AHK v1.

ASSIGN: MULTIPLE ASSIGN KEYS/PROPERTIES
- The {} syntax can create an object with 'properties that are really keys'.
- A way to use the {} syntax to add/overwrite keys/properties, might be useful also.
- As would a way to dynamically add 'properties that are really keys'.

ASSIGN: KEYS / PROPERTIES / METHODS
- This can create properties: {a: b, c: d}. But what about keys/methods?
- (Btw I had wondered if a property could be based upon one function, and passed a parameter indicating 'get'/'set'.)

TYPES AND SYNTAX
- [KEY] I found the major problem with objects in other programming languages to be that for relatively similar object types, you could end up with a dozen object types, each with conflicting syntaxes e.g. obj.key, obj[key], obj.get(key).
- Re. arrays/maps, I would suggest:

Code: Select all

keep: k, v in array (default, more consistent)
not: v in array (optional via a mode, easier translation from some languages)
[EDIT: AHK v2 has 'k, v in array' (AHK v1/v2) and 'v in array' (AHK v2), but not 'k in array' (AHK v1), which is fine.]
- In general, all kinds of modes, could allow you to neatly recreate object types in AHK, in line with other programming languages.

LOOPS/_NEWENUM/NEXT/__FORIN
- I don't have strong views re. _NewEnum/Next/__forin.
- (My initial instincts are that _NewEnum ('new enumerator') and Next may be worth keeping. The names say what they do, and '_NewEnum' is well-known outside of AHK.)
- (Note: Next allows you to output multiple variables, not just 1 or 2 as in a for loop, and to loop through 2 objects simultaneously.)
- Perhaps '__forin' should be '__ForIn', since everything in AutoHotkey is CamelCase usually.
- [KEY] (What did concern me was AHK objects storing the key creation order, so that you could loop in that order. That could be optional, off by default to improve performance. Loops by key creation order would be, IMO, the number one concern re. any new object functionality in AHK v2.)
- [KEY] (A method to remove/filter blank/zero/specific keys from an array, or perhaps loop-and-delete functionality, or something else, would also be important, as at present this simple task can bloat/obscure scripts/functions.)

KEYS/PROPERTIES/METHODS
- [KEY] (For easy conversion to and from Scripting.Dictionary objects, I would consider introducing an Item property, and perhaps an Exists method.)
- (One idea might be to store a function or obj.method in a variable, e.g. to create a custom A_ variable. Defined at loadtime.)
- Regarding keys v. properties. I felt that the whole point of, and benefit of, properties was that they were effectively a method, but that syntactically they looked just like keys. [However, I'm open to the Object.ahk idea of completely separating keys/properties/methods.]
- Having to use obj["xxx"] for keys, and not obj.xxx for properties seems like a big(/massive?) disadvantage.
- I would consider having the two be interchangeable, although there could be methods to specifically call a key or a property:

Code: Select all

;retrieve a key/property value (like AHK v1)
obj.1
obj.x
obj[1]
obj["x"]
obj[key]
obj[property]
;call a method (like AHK v1)
obj.x()
obj["x"]()
obj[method]()
;emulate a Scripting.Dictionary object
obj.Item[1]
obj.Item["x"]
;retrieve a key value (alternative) [never a property]
obj.GetValue(1)
obj.SetValue("x")
;store a property as an object
obj.GetProperty(1)
obj.SetProperty("x")
;retrieve a property value [never a key]
obj.GetPropertyValue(1)
obj.SetPropertyValue("x")
;store a method as an object
obj.GetMethod(1)
obj.SetMethod("x")
Last edited by jeeswg on 23 Aug 2019, 18:40, edited 1 time in total.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: Objects+

01 Aug 2019, 11:08

- I've added the word '[KEY]' to various posts to indicate highlights.
- I've revised the previous post.
- I've clarified the list of possible modes, above:
Objects+ - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=37&t=58215&p=246205#p246205

OBJSETDEFAULTVALUE
- There's been a lot of interest in 'ObjSetDefaultValue':
python - Dictionaries and default values - Stack Overflow
https://stackoverflow.com/questions/9358983/dictionaries-and-default-values
python - Why dict.get(key) instead of dict[key]? - Stack Overflow
https://stackoverflow.com/questions/11041405/why-dict-getkey-instead-of-dictkey

CHECKLIST
- Here is a checklist of functionality to consider for objects:
jeeswg's objects tutorial - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=7&t=29232

Code: Select all

array - sort/reverse/shuffle
array/map - frequency count (case sensitive/insensitive)
array/map - get/set value/property/method
array/map - search/filter: has value
array/map - search/filter: list matching key names/values (or copy values) (e.g. key name/value contains/equals/starts/ends/RegExMatch)
array/map - search/filter: min/max key name/value
array/map - search/filter: remove duplicates (case sensitive/insensitive)
array/map - set default value
array/map - set multiple values
array/map - to string: join
array/map - to string: print object/key names/values
map - key names: case sensitive/case insensitive (will be in AHK v2)
map - key names: get correct case
variable - is array/map
ONE CURIO
- In AHK v1, adding too many keys in one go causes a crash e.g. 200K via a variadic parameter.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: Objects+

23 Aug 2019, 19:19

AUTOHOTKEY V2.0-A104 OBJECTS SUITE

AutoHotkey v2 alpha (UPDATES) - Page 3 - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=37&t=2120&p=288705#p288705

A few points on the new objects suite, which I'm very happy with.
(Any syntax names are just suggestions and can be changed.)

Queries:
- default value? obj.DefaultValue := value or obj.SetDefaultValue(value)
- ... more needed now that the blank string default value has been removed
- ... although a property might be sufficient, a method would allow omitting the value parameter to indicate no default value [or a special null variable?]
- ... would be very useful for AHK v1 also
- ... perhaps some functionality for DefaultPropValue would also be worthwhile
- array.xxx and obj.xxx, where xxx is an integer, are failing for items defined via []/Array()/{}/Object()

Good features from AHK v1:
- array.Count would be useful [how many keys have values, is the array sparse/dense]
- ObjRawGet/ObjRawSet equivalent for keys
- adding array methods to maps would actually be pretty useful [for handling integer keys, multiple key insertion, and for n-based arrays]
- case-insensitive mode for maps? [you can use lower-case key names, but sometimes you want the original case of the first match]
- ... perhaps all that's needed is a case-insensitive lookup method to return the first matching key name
- ... and/or a way to list all keys that have case-insensitive duplicates
- keys/properties: could be useful: a mode where keys take priority over properties (like AHK v1)

New functionality:
- obj.MinKey(Type:="")/obj.MaxKey(Type:="") methods would be useful for arrays/maps
- ... or obj.MinKey/obj.MaxKey/obj.MinIndex/obj.MaxIndex properties
- ... especially since array.Length has a new meaning [in AHK v1, it returned the last non-empty key]
- ... would be good for AHK v1 also
- 2 methods to assign multiple keys to a map [key-value pairs, and perhaps keys only (using the map's default value)]
- object to string
- (sort array values, and remove duplicates)
- (filter object keys/values) [e.g. keys/values that match certain criteria, e.g. get the correct case for the key name]

Two-way compatibility:
- Has method for AHK v1(/v2)
- Length/Count properties for AHK v1(/v2)
- obj.%prop% and obj.%method%(), and/or method(/function) equivalents, for both AHK v1/v2
- ... perhaps obj.GetPropValue(prop) and obj.SetPropValue(prop, value)
- ... perhaps obj.InvokeMethod(method, params*) [or CallMethod]
- ... the %% syntax could be permitted in AHK v1, even if not implemented (and if run, to cause an error)
- perhaps 'if (var is "array")', in AHK v1 this would be true if the object only contained positive integer keys

SET DEFAULT VALUE EXAMPLE CODE

Code: Select all

;default value method:
oMap := Map()
oMap.SetDefaultValue(0)
Loop Parse, vText, "`n", "`r"
	oMap[A_LoopField]++

;default value property:
oMap := Map()
oMap.DefaultValue := 0
Loop Parse, vText, "`n", "`r"
	oMap[A_LoopField]++

;otherwise:
oMap := Map()
Loop Parse, vText, "`n", "`r"
{
	if oMap.HasKey(A_LoopField)
		oMap[A_LoopField]++
	else
		oMap[A_LoopField] := 1
}
OBJXXX FUNCTIONS

A list of AHK v1/v2 ObjXXX functions, for reference:

Code: Select all

;AHK v1/v2 (currently):
ObjAddRef
ObjBindMethod
ObjClone
Object
ObjGetBase
ObjGetCapacity
ObjRawGet
ObjRawSet
ObjRelease
ObjSetBase
ObjSetCapacity

;AHK v1 only (currently):
ObjCount
ObjDelete
ObjGetAddress
ObjHasKey
ObjInsertAt
ObjLength
ObjMaxIndex
ObjMinIndex
ObjNewEnum
ObjPop
ObjPush
ObjRemoveAt

;AHK v2 only:
ObjOwnPropCount
ObjHasOwnProp
ObjHasProp
ObjOwnMethods
ObjOwnProps
LINKS

Key links:
Objects - Definition & Usage | AutoHotkey v2
https://lexikos.github.io/v2/docs/Objects.htm
Object - Methods & Properties | AutoHotkey v2
https://lexikos.github.io/v2/docs/objects/Object.htm
Array Object - Methods & Properties | AutoHotkey v2
https://lexikos.github.io/v2/docs/objects/Array.htm
Class Object - Methods & Properties | AutoHotkey v2
https://lexikos.github.io/v2/docs/objects/Class.htm
Map Object - Methods & Properties | AutoHotkey v2
https://lexikos.github.io/v2/docs/objects/Map.htm
Enumerator Object - Definition & Usage | AutoHotkey v2
https://lexikos.github.io/v2/docs/objects/Enumerator.htm

Key links (further):
For Loop - Syntax & Usage | AutoHotkey v2
https://lexikos.github.io/v2/docs/commands/For.htm
Concepts and Conventions | AutoHotkey v2
https://lexikos.github.io/v2/docs/Concepts.htm
Scripting Language | AutoHotkey v2
https://lexikos.github.io/v2/docs/Language.htm
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA

Return to “AutoHotkey Development”

Who is online

Users browsing this forum: No registered users and 39 guests