Objects+

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

Objects+

22 Oct 2018, 22:11

Split from:
Objects - AutoHotkey Community
https://autohotkey.com/boards/viewtopic ... 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)
- 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
- perhaps a Mode method for objects (to handle such options) (or something else)

FUNCTIONALITY (SIMPLICITY)
- 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)
- I would consider having everything available as methods and no built-in properties (or any built-in properties also available as methods)
- ... 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)
- 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 ... 22#p247622
"".base.base := {split:"StrSplit"}
- (classes: I've thought about the ability to do something whenever *existing* keys are read from/written to, however, this can be done with the existing functionality, by placing data inside the class object, and is very rarely needed, so needn't be built-in)

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 Nov 2018, 19:56, edited 16 times in total.
User avatar
nnnik
Posts: 3546
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: 5435
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).
User avatar
nnnik
Posts: 3546
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: 5435
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.)
User avatar
nnnik
Posts: 3546
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: 2469
Joined: 09 Oct 2013, 10:31

Re: Objects

23 Oct 2018, 09:28

lol

User avatar
jeeswg
Posts: 5435
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.
User avatar
nnnik
Posts: 3546
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: 5435
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.
User avatar
nnnik
Posts: 3546
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: 5435
Joined: 19 Dec 2016, 01:58
Location: UK

Re: Objects

27 Oct 2018, 00:25

- Possible modes for a Mode() 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.)

Code: Select all

StoreCreationOrder := 0 ;[KEY]
CaseSensitiveKeys := 0 ;[KEY]
AllowStringKeys := 1
AllowIntegerKeys := 1
AllowStoreObjects := 1 ;(for traditional dictionaries/maps)
AllowStoreStrings := 1 ;(for traditional dictionaries/maps)
AllowStoreIntegers := 1 ;(for traditional dictionaries/maps)
AllowStoreFloats := 1 ;(for traditional dictionaries/maps)
AllowNewKeys := 1 ;(for traditional classes/arrays)
KeyMax := "" ;(for traditional arrays)
KeyMin := "" ;(for traditional arrays)
ForceLookIntegralAsStringKeys := 0
ForceLookIntegralAsIntegerKeys := 0 ;[KEY] [AHK v2: 1] (a function to bypass this may be better than a setting)
DefaultValue := "" ;[KEY]
DefaultEnum := ""
Type := "Object" ;perhaps a method that could be a shorthand for multiple switches
- Perhaps {base} could work differently when creating objects e.g. obj := {base:{}}
- But not when assigning keys e.g. obj.base := {}
- Although this could be made available: obj.base() := {}

- 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?
lexikos
Posts: 6207
Joined: 30 Sep 2013, 04:07
GitHub: Lexikos

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: 3546
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: 5435
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.
- I (and probably others) found Object.ahk difficult to appreciate in full, the code, and sometimes the possible motivations. I'd imagine that the community, experts and non-experts, would very much welcome any further explanatory material. [EDIT:] There's a fair amount already, but it assumes expertise, so specifically, aimed at newbies: anything that would break existing scripts, and the likely benefits of any new additions for a newbie.

KEYS/PROPERTIES/METHODS
- In AHK v1, creating a key (in the object) with the same name as a method (in the base), blocks access to the method, e.g. HasKey.
- (Adding a prefix e.g. 'z' can prevent name collisions, but clutters the code a bit, as the prefix is added during assignments, and removed during retrievals.)
- Separating keys/properties would solve this problem.
- I suppose two separate indexes would be needed for each object, a key/property index and a method index.
- In AHK v1: .key and [key] would both check for a key/property in the object, and if not found, check the base. .method() and [method]() would both call a method.
- ... I tried to understand how and why Object.ahk differed from this, rereading multiple times, but haven't had the time to revisit the matter.
- (For easy conversion to and from Scripting.Dictionary objects, I would consider methods directly equivalent to Exists and Item, possibly with the same names.)
- (Also, if in AHK v2: [] and HasKey force "1" (string key name) as 1 (numeric key name), then Item and Exists methds, as with the Scripting.Dictionary, could allow you to avoid this forced conversion.)

LOOPS/_NEWENUM/NEXT/__FORIN
- '__forin'. '__ForIn'? 'New enumerator' means something, what does 'for in' mean? And why would it be lower case? Everything in AutoHotkey is CamelCase usually.
- '_NewEnum' is used outside of AutoHotkey.
- What I'm unclear of is what is the exact difference between _NewEnum and __forin, would _NewEnum and Next continue to exist, if not, why not. I'm not for or against anything per se.
- And, more generally, what, if anything, would be broken in AHK v1 scripts being converted to AHK v2 as a result of the Object.ahk proposals.
- I didn't see any particular problems with loops/enumerators etc in AHK v1. So I don't see what problem is being solved.
- (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.)
- (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.)
- [EDIT:] (Next allows you to output multiple variables, not just 1 or 2 as in a for loop, and to loop through 2 objects simultaneously.)

CONVERTING OBJECTS: CASE SENSITIVITY
- Converting from a case-insensitive array (key names) to a case-sensitive array (key names), would outwardly affect nothing, but depending on the internals, might involve restructuring the data.
- Trying to convert from a case-sensitive array (key names) to a case-insensitive array (key names), could simply result in an error. Alternatively, some useful behaviour could be implemented, e.g. keys 'ABC', 'Abc', 'abc', could be combined (summed/concatenated/added as subkeys) and put into the first key (alphabetically or by creation date).

CONVERTING OBJECTS
- An example of 'converting' objects would be a linear array to an associative array. The array could then permit gaps.
- Various classic array operations often involve 'converting' objects in different programming languages (sometimes as intermediary steps):
linear/associative array: frequency count/remove duplicates (case sensitive/insensitive) (sort/maintain order).
- (Since there is an overlap between some types of objects, multiple 'types' could in fact be implemented as one type, just with different 'modes'. AHK v1 uses one type for linear and associative arrays, and, theoretically, that one type could be extended, adding support for case-sensitive key names, and for storing the key creation date order.)

CREATING OBJECTS
- Perhaps: obj := {MyKey:"key", MyProperty:oProperty, MyMethod():oMethod}, or something.
- To me, having Base as a method, not a property, appears to solve a tonne of problems. Maybe it would simplify AHK basic objects to have no built-in keys/properties (there are only base and __Class at present).
- (Btw since property getters/setters are separate functions, and so don't share the same variable space, you could have a way to create a property based upon one function. It's tempting to not use properties at all for this reason, unless they're read-only or based on keys/other properties.)

MODES
- Re. the AllowStoreXXX modes: I view the AHK v1 basic object as one flexible type that can do many things. By adding certain AllowStoreXXX restrictions, objects could match more closely/exactly certain types common in other programming languages. (Preventing keys that contain objects gives you a one-dimensional array, i.e. a map. Certain restrictions could increase performance by allowing the use of less costly data structures. Also, restrictions could aid debugging/prevent unexpected assignments. And there could be functionality to force numeric values to be stored as strings or vice versa, without using "" or +0.)
- What does concern me is newbies getting confused: with converting lines of text to key names, or creating one key name per hex number, with, in AHK v2, anything that looks like an integer being stored as an integer, and thus retrieved out of sequence in a loop. So, having a sensible default (assume it's an integer) is fine, but IMO it's better to have the option of setting the mode e.g. ForceLookIntegralAsStringKeys / ForceLookIntegralAsIntegerKeys (AHK v2 at present) / allow both (AHK v1). Using a Mode method seems like one way, a good way, a one-line fix, to avoid unwanted results.
- Although, instead, an alternative way to assign keys, that would differentiate between "" vNum and vNum+0 would be fine, e.g. an Item method to match Scripting.Dictionary objects. (In general, I'm not especially attached to modes, but they might be a neat solution.)
- Mode: Type. E.g. I store my data as a linear array, for higher performance, but now I want to convert it to an associative array. Some method is needed to change the type of an existing object, a mode would be a way to 'convert' the object. (Note: converting a linear array to an associative array might cause changes internally, but outwardly, the data would be the same. Note: Adding a key with a string name would automatically convert a linear array to an associative array, unless this was restricted by a mode.)
- (It is often inconvenient that RegExMatch objects are read-only, it would be useful if they could be converted to regular AHK arrays. And any loss of methods etc would be acceptable.)

LINEAR ARRAY STARTING AT INDEX
- I like that AutoHotkey objects use 1-based indexes, but sometimes you want the index to start at a higher or lower index.
- Perhaps a method could be made available that would be more intuitive than:

Code: Select all

	static (oArray := ["zero", "one", "two"]).RemoveAt(0)
	static oArray2 := {}, _ := oArray2.InsertAt(4, "four", "five", "six")
- (Similarly, a way to use {} notation to add keys, instead of only creating/overwriting an object.)
- (And perhaps an array that would only contain integer key names could be specified as such. Or this could be assumed via [], i.e. [] and {} are still interchangeable, but [] is optimised for arrays until the first key with a string name is added. Similarly, a means for specifying a read-only array might (or might not) aid performance.)
- (It must be said though, that I have not experienced any problems with AutoHotkey objects being slow. So performance concerns may me more theoretical than real.)

==================================================

[EDIT:] Further points.

- 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).
- The AHK v1 system kept things simple by combining lists/linear arrays (with gaps allowed, and starting at any index), and dictionaries/maps/associative arrays, and single/multi-dimensional arrays as one object type, where you can simultaneously have key names/values that are both numeric and string. Support for case sensitivity and storing the key creation order could also be added, both within the concept of that one basic object type.
- There could be 'modes' that would restrict what the general-purpose basic object could do, for performance gains. There could be 'types' so that you could specify various modes with one familiar word. (An interesting question would be whether adding a string key to a linear array 'type' would automatically convert the linear array to an associative array. Would types be 'locked' by default, or would they be seamlessly converted by default.)

- 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.
- I would have the 2 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[key]()
obj[property]()
;retrieve a key value (like Scripting.Dictionary object)
obj.Item(1)
obj.Item("x")
;retrieve a property value (something like this:)
;(alternatively HasKey or another method with an additional parameter)
obj.PropVal(1)
obj.PropVal("x")
obj.CallProp(1)
obj.CallProp("x")

Return to “AutoHotkey v2 Development”

Who is online

Users browsing this forum: No registered users and 5 guests