A strange __set() disturbance Topic is solved

Get help with using AutoHotkey (v2 or newer) and its commands and hotkeys
User avatar
FredOoo
Posts: 186
Joined: 07 May 2019, 21:58
Location: Paris

A strange __set() disturbance

03 Aug 2020, 09:22

This was originally an exercise in using the __get/__set methods.
The code below works fine as long as the __set() method is not defined.

Code: Select all

; AutoHotkey - v2.0-a119-179d27fd - U64
#SingleInstance force
#Persistent
setWorkingDir a_ScriptDir

#Include CConsole.ahk
global Console := CConsole.new()

class CA {
	__new(){
		this.Sections := {
			Versions: [ 11, 22, 33 ],
			Options:  [ 'AA', 'BB', 'CC' ],
		}
		this.Sections.defineMethod( 'has', func('Sections_has') )
		Sections_has( this, key ){
			try {
				this.%key%
				return true
			} catch
				return false
		}
		;| If __get/__set name is not in Sections, I'll do something else.
	}
	__get( name, Params ){
		;Console.log( "GET:" )
		;Console.log( name )
		;Console.log( params )
		if( this.Sections.has( name ) ){  ;<| Critical Error: Function recursion limit exceeded
			if( Params.length==0 )        ; | but only if __set() is defined.
				return this.Sections.%name%
			else
				return this.Sections.%name%[ Params[1] ]
		}
	}
	;__set( name, Params, value ){
	;	;Console.log( "SET:" )
	;	;Console.log( name )
	;	;Console.log( params )
	;	;Console.log( value )
	;	if( this.Sections.has( name ) && Params.length==1 ){
	;		this.Sections.%name%[ Params[1] ] := value
	;	}
	;}
}
A := CA.new()
Console.log( A.Versions )      ; [ 11, 22, 33 ]
Console.log( A.Versions[2] )   ; 22
Console.log( A.Options[2] )    ; "BB"
;A.Versions[2] := 222

But if __set() is defined, the code enters a critical error. Why ?
CConsole.ahk
(Alan Turing) « What would be the point of saying that A = B if it was really the same thing? »
(Albert Camus) « Misnaming things is to add to the misfortunes of the world. »
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: A strange __set() disturbance

03 Aug 2020, 10:06

It is because this.Sections := { in __new invokes __set where this.Sections.has( name ) invokes __get where this.Sections.has( name ) invokes __get... (inf recursion)
Edit, you can avoid __set,

Code: Select all

this.defineProp 'Sections', { 
	value : {
		Versions: [ 11, 22, 33 ],
		Options:  [ 'AA', 'BB', 'CC' ],
	}
}
Cheers.

Edit 2, @swagfag, thanks :beer:
Last edited by Helgef on 03 Aug 2020, 12:32, edited 1 time in total.
User avatar
FredOoo
Posts: 186
Joined: 07 May 2019, 21:58
Location: Paris

Re: A strange __set() disturbance

03 Aug 2020, 11:24

You are right.
If I just uncomment the line Console.log( "SET:" ) in __set, I can see this.Sections := { in __new invokes __set.

Code: Select all

	__set( name, Params, value ){
		Console.log( "SET:" )
	}
  
Regarding the AHK language, this means that this.Sections := { does not define Section as a property ?! Because __set is supposed to be invoked only if the name in __set is an undefined property.

However, this.Sections.has(name) in __set, doesn't invoke __get because I haven't uncommented it. By the way, it doesn't cause any recursion in __get itself as long as _set is not defined at all.

So I understant the idear of your solution but not the origine of the recursion under the infuence of the __set définition.
And I would like to understand because this was originally an exercise.
(Alan Turing) « What would be the point of saying that A = B if it was really the same thing? »
(Albert Camus) « Misnaming things is to add to the misfortunes of the world. »
swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: A strange __set() disturbance

03 Aug 2020, 11:55

given meta-functions have been defined:
  1. when u start constructing ur object, it doesnt yet have a property called Sections
  2. this.Sections := would invoke meta-set(if defined). uve defined a meta-set, so it invokes it
  3. inside meta-set, u assume the property Sections on ur object already exists(the this.Sections in if( this.Sections.has( name ) && ...). it does not, hence meta-get is called
  4. inside meta-get, u assume again the property Sections on ur object already exists(the this.Sections in if( this.Sections.has( name ) ){). it does not, hence meta-get is called and there's the origin of ur recursion

and since u say ure practicing:
  • ; AutoHotkey - v2.0-a119-179d27fd - U64 - we have #Requires now. use it
  • generally u should be calling DefineProp/GetOwnPropDesc inside metafunctions to avoid recursions like this one. unless u know what ure doing, in which case, do what u gotta do.
User avatar
FredOoo
Posts: 186
Joined: 07 May 2019, 21:58
Location: Paris

Re: A strange __set() disturbance

03 Aug 2020, 13:32

3. inside meta-set, u assume the property Sections on ur object already exists

In __set, I don't assume anything since there is only one line: Console.log( "SET:" ).
And no recursion is called if __set is not defined.
If __set is defined A := CA.new() is enough to get a loop around __get.
• generally u should be calling DefineProp/GetOwnPropDesc inside metafunctions

The defineProp from Helgef works fine.
But if I try to move it inside __get, starting with a if( !isSet( this.Sections ) ) I get the "Parameter #1 of IsSet must be a variable" error.
Maybe you cant give a code sample of what you mind ?
(Alan Turing) « What would be the point of saying that A = B if it was really the same thing? »
(Albert Camus) « Misnaming things is to add to the misfortunes of the world. »
swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: A strange __set() disturbance

03 Aug 2020, 15:27

don't assume anything
i was talking about ur first example. if( this.Sections.has( name ) && ... u dont test whether this has a property called Sections, instead uve hardcoded this.Sections in, hence ur assuming the existence of such a property.

no recursion is called if __set is not defined
because in that case AHK will handle creating the property for u. do u understand what the purpose of metafunctions is? it is to override this default behavior of AHK, in other words:
from the docs wrote:decide what happens when an undefined property or method is invoked
if u define metafunctions and dont do anything meaningful in them(such as creating the not yet defined property that ure trying to set) or attempt doing illogical things in them(such as trying to access(ie get) an undefined property with a function, which to work properly requires accessing the very same undefined property), dont act surprised when stuff breaks.

If __set is defined A := CA.new() is enough to get a loop around __get.
this is equal parts due to ur __Set failing to define Sections and ur __Get relying on Sections having been defined, the solutions to which are plenty diverse:
  • bypass metafunction invocation entirely
  • actually define the property inside meta-set
  • dont have meta-get depend on the property's existence

But if I try to move it inside __get, starting with a if( !isSet( this.Sections ) ) I get the "Parameter #1 of IsSet must be a variable" error.
because IsSet
Returns a non-zero number if the specified variable has been assigned a value.
IsSet can be used to check whether a function's unset optional parameter... is set. i dont know what made u gravitate towards IsSet
to test whether a property is defined on an object u can:
  • call .HasOwnProp(name)
  • call .HasProp(name)
  • call .GetOwnPropDesc(name) in a try-catch
Maybe you cant give a code sample of what you mind ?
eg for GetOwnPropDesc

Code: Select all

__set( name, Params, value ){
	try ; get it if it's there
		Sections := this.GetOwnPropDesc('Sections').Value
	catch e ; otherwise define it
		this.DefineProp('Sections', {Value: value})

	if( Sections.has( name ) && Params.length==1 ){
		Sections.%name%[ Params[1] ] := value
	}
}
but it makes very little sense to write it like this(unless ure particularly fixed on everything having to go through meta-set). just do what helgef says
User avatar
FredOoo
Posts: 186
Joined: 07 May 2019, 21:58
Location: Paris

Re: A strange __set() disturbance

03 Aug 2020, 17:38

Are you pissed off swagfag ? It seems your heart is swinging between impatience and exasperation, or maybe a little arrogance, or tired ?

I didn't come here to get yelled at. Calm down.

I don't test whether this has a property called Sections. I test if the property Sections contains Versions or Options items.
Take your time to read before you talk.
this.Sections always exists (except "when u start constructing ur object" as you say… Thanks)

When __set is not defined, I don't call it… So I don't think AHK will handle creating any property for me.

…/…

Your sample code doesn't works at all. It just changes nothing.

You just don't undersant what I'm talking about, or you write faster than you think.
If you are not interested in a question, bypass it.

Here is another example :

Code: Select all

#Requires AutoHotkey v2.0-a119-179d27fd
#SingleInstance force
#Persistent
setWorkingDir a_ScriptDir

#Include CConsole.ahk
global Console := CConsole.new()

;===▽==================================

global IniFile := CIniFile.new()

class CIniFile {
	__new(){
		splitPath a_ScriptName,,,, o_NameNoExt
		this.fileName := o_NameNoExt '.ini'
		if (!fileExist( this.fileName )){
			this.createIniFile( this.fileName )
		}
	}
	createIniFile( fileName ){
		fileAppend format("
		(LTrim
			[VersionsPaths]
			v1=pathTo_V1
			[Options]
			DevMode=0
		)"), this.fileName
	}
	DevMode {
		get {
			return iniRead(this.fileName, "Options", "DevMode", 0)?1:0
		}
		set {
			iniWrite (value?1:0), this.fileName, "Options", "DevMode"
		}
	}
	__get( name, params ){
		if name=='VersionsPaths' {
			;Console.log( params )
			return iniRead(this.fileName, "VersionsPaths", params[1], '')
		}
	}
	;__set( name, params, value ){
	;	if name=='VersionsPaths' {
	;		Console.log( params )
	;		Console.log( "value: " value )
	;		iniWrite value, this.fileName, "VersionsPaths", params[1]
	;	}
	;}
}

Console.log( "VersionsPaths v1: " IniFile.VersionsPaths["v1"] )
Console.log( "VersionsPaths v2: " IniFile.VersionsPaths["v2"] )
;IniFile.VersionsPaths["v2"] := "xxxxxpathToV2"
;Console.log( "VersionsPaths v2: " IniFile.VersionsPaths["v2"] )
Console.log( "DevMode: " IniFile.DevMode )

;===△==================================
Last edited by FredOoo on 04 Aug 2020, 04:24, edited 1 time in total.
(Alan Turing) « What would be the point of saying that A = B if it was really the same thing? »
(Albert Camus) « Misnaming things is to add to the misfortunes of the world. »
swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: A strange __set() disturbance

03 Aug 2020, 19:00

nobody is yelling at anybody. u see any big letters anywhere? dont perceive things that arent there. bold and italics denote things to pay extra attention to. we're here to take code apart, so focus on the code and not ur emotions
I don't test whether this has a property called Sections. I test if the property Sections contains Versions or Options items.
correct. u dont test for the presence of Sections(but u should, because it may not be there. That was what i was implying with my statement), u just assume it exists(which need not be the case) and go about ur business. this is an ok thing to do but u have to accept the fact that ur code now depends on a precondition, and if the precondition isnt satisfied(ie there actually isnt a property called Sections), ur code will break. so u have 2 options, either:
  • test whether the property exists and decide on the spot what happens if it doesnt
  • assume the property always exists, hardcode it in, but then actually ensure(elsewhere) the property exists
we can ignore anything concerning Versions and Options because its of no relevance, the code breaks long before that point is reached anyway.
Take your time to read before you talk.
this... seems like a cool advice to heed urself.
this.Sections always exists (except "when u start constructing ur object" as you say… Thanks)
then we're on the same page that it does not, in fact, always exist.
When __set is not defined, I don't call it…
to be pedantic: you, in particular, never call any metafunction - AHK does the calling for u.
...So I don't think AHK will handle creating any property for me.
well, it will. per the docs:
Objects wrote:... set a property literally named Property: Object.Property := Value
...
... set a property where the name is determined by evaluating an expression or variable: Object.%Expression% := Value

Your sample code doesn't works at all. It just changes nothing.
i wrote this on my phone, so i couldnt test back then. here is the fixed version:

Code: Select all

class CA {
	__new(){
		this.Sections := {
			Versions: [ 11, 22, 33 ],
			Options:  [ 'AA', 'BB', 'CC' ],
		}
		;| If __get/__set name is not in Sections, I'll do something else.
	}

	__get( name, Params ){
		;Console.log( "GET:" )
		;Console.log( name )
		;Console.log( params )
		if( this.Sections.has( name ) ){  ;<| Critical Error: Function recursion limit exceeded
			if( Params.length==0 )        ; | but only if __set() is defined.
				return this.Sections.%name%
			else
				return this.Sections.%name%[ Params[1] ]
		}
	}

	__set( name, Params, value ){
		try
			Sections := this.GetOwnPropDesc('Sections').Value
		catch e
		{
			this.DefineProp('Sections', {Value: value.DefineMethod('Has', Func('ObjHasOwnProp'))})
			return value
		}

		if( Sections.has( name ) && Params.length==1 ){
			Sections.%name%[ Params[1] ] := value
		}
	}
}

You just don't undersant what I'm talking about, or you write faster than you think.
If you are not interested in a question, bypass it.
:roll: anyhoo...
Here is another example :
the example is largely the same(u did a good job at abstracting ur MVCE away... well done, i suppose).
if u DONT define a meta-set
if u DO define a meta-set
User avatar
FredOoo
Posts: 186
Joined: 07 May 2019, 21:58
Location: Paris

Re: A strange __set() disturbance  Topic is solved

03 Aug 2020, 20:33

Here I can see no this.FileName is defined :

Code: Select all

	__new(){
		splitPath a_ScriptName,,,, o_NameNoExt
		this.fileName := o_NameNoExt '.ini'
		Console.log( "===========" )
		Console.log( this.fileName )
		Console.log( "===========" )
		if (!fileExist( this.fileName )){
			this.createIniFile( this.fileName )
		}
	}
Here It works fine :

Code: Select all

	__new(){
		splitPath a_ScriptName,,,, o_NameNoExt
		
		;this.fileName := o_NameNoExt '.ini'   ; Calls __set() where this property name is not defined
		this.defineProp( 'fileName', { value: o_NameNoExt '.ini' } )   ; __set() is not called, the property is defined here
		
		Console.log( "===========" )
		Console.log( this.fileName )
		Console.log( "===========" )
		if (!fileExist( this.fileName )){
			this.createIniFile( this.fileName )
		}
	}

I can now do what I want :

Code: Select all

IniFile.VersionsPaths["v1"]
IniFile.VersionsPaths["v2"]

IniFile.VersionsPaths["v2"] := "xxxxxpathToV2"
IniFile.VersionsPaths["v2"]

IniFile.DevMode
IniFile.DevMode := true
IniFile.DevMode

; with Console.log( )

I think __set should not be called from the constructor __new.
Meta-functions define what happens when an undefined property or method is invoked.
It's strange to call a meta-function, that is built to be called if a property or method is undefined, at the moment I try to define it (this.property := ) at the place I'm supposed to do it (in the constructor __new)

AutoHotKey is a great and powerfull WinAPI script tool but:
v1 is a very old and bizarre script language.
v2 is in a never ending developpement language.
(Alan Turing) « What would be the point of saying that A = B if it was really the same thing? »
(Albert Camus) « Misnaming things is to add to the misfortunes of the world. »
lexikos
Posts: 9583
Joined: 30 Sep 2013, 04:07
Contact:

Re: A strange __set() disturbance

03 Aug 2020, 22:17

__set is used to override the definition of new properties by assignment. To not call it at any point where an assignment is made and the property has not previously been defined would be a loss of flexibility. If you want __set to behave differently before the object is fully constructed than after, you can define that behaviour yourself.
I think __set should not be called from the constructor __new.
...
v2 is in a never ending developpement language.
It sounds like you are criticizing the changing nature of the language, while also requesting a change to the language.
swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: A strange __set() disturbance

03 Aug 2020, 22:43

in an alternate universe BarneyOoo is wondering why __Set isnt being called from the constructor :lol:
Meta-functions define what happens when an undefined property or method is invoked.
thats pretty much all there is to it. it doesn't matter where the invocation occurs: in the autoexecute section, in an included script file, in a ("compiler"-generated, cough __Init) function/(static) method/hotkey/hotstring, in the very same metafunction meant to handle it(as u have by this point observed urself)
User avatar
FredOoo
Posts: 186
Joined: 07 May 2019, 21:58
Location: Paris

Re: A strange __set() disturbance

04 Aug 2020, 02:14

Meta-functions define what happens when an undefined property or method is invoked.
I got this sentence from the doc. But also :
The meta-function is expected to handle cases such as x.y[z] where x.y is undefined.
That sounds like Obj.notDefinedProperty[index]. I'll analyse the property's name, the index and other parameters, search in my data structure and respond the call returning a value.
At this step, I understand that's a little more abrtract than the defined property get / set I used for comparaison on my IniFile sample.

Code: Select all

	DevMode {
		get {
			return iniRead(this.fileName, "Options", "DevMode")
		}
		set {
			iniWrite value, this.fileName, "Options", "DevMode"
		}
	}
So if the property's name is not determinated in advance, I can use the __get/__set meta-functions.

The surpise came from: What is a not defined property ? And when are they considered as not defined ? Before I got time to say whitch ones are defined, in the constructor ?

Code: Select all

	class CB {
		;name := "John"  ; (1)
		__new(){
			;this.name := "John"  ; (2)
			this.defineProp( 'name', { value: "John" } )  ; (3)
		}
		getName(){
			return this.name
		}
		check_HasOwnProp(propName){
			return this.HasOwnProp(propName)
		}
		;__set( propName, params, value ){
		;	Console.Log( "__set is called" )
		;}
	}
	B := CB.new()
	Console.log( B.getName() )
	Console.log( B.check_HasOwnProp('name') ) ; (1) ok, it's defined, but __set is called
	                                          ; (2) ok, it's defined, but __set is called
	                                          ; (3) ok, it's defined, and __set is NOT called
	Console.log( B.check_HasOwnProp('age') )  ; ok, always false

A language should not surprise us too much and it should be predictible not reading the documentation too much.

Through your explanations, I wonder if for you, __get/__ set are made to manage propertie dynamicaly and not to retieve values from the data structure. What would be a beautiful user of __get/__set for you ?
v2 is in a never ending developpement language.
AHK is really a nice tool. But v1 syntaxe of the language is very old and it is really confusing for developers who know more modern languages.
So it's really a good idear you develop the v2, and I know it's not an easy project.
I discoverd AHK one year ago, worked with it one or two monts and come back to it only now. I notices a lot of changes since the last v2 I used. May be I hop you can develop a modern standard and simple language (not too much surprises or originalities).

It's not easy to talk about a language in few words, but here are some examples :

obj := ClassObj.new()
Ok, __new() is static… but it looks like ClassObj.constructor() where in many modern laguages we find simply ClassObj() and new ClassObj() is a good idear, a common usage.

foundPos := inStr(haystack, needle)
foundPos := inStr(haystack, '') ; looking for an empty string
>>> 1 <<< AHK v2.0-a103-56441b52 My first version 2
>>> Error <<< AHK v2.0-a119-179d27fd Last v2
1 was an error, a not true information, I remember I got a hard bug because this one
but Error is blocking
Javascript responds 0 and I think that's more close from the reality : there is no empty in any string.

The affectation operator :=
Looks like old Pascal laguage, I like this one.

I have more examples, but it's long to explain, and @lexikos, I didn't reply yet to another post last week.

If I'm criticizing the language, that's because it would be nice to get a stable and intuitive v2, then focus on the tool itself.
(Alan Turing) « What would be the point of saying that A = B if it was really the same thing? »
(Albert Camus) « Misnaming things is to add to the misfortunes of the world. »
swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: A strange __set() disturbance

04 Aug 2020, 14:56

What is a not defined property ?
any property for which HasProp(Value, name) returns false
or u can define an empty(so it doesnt define any properties) meta-set on ur object, instantiate it and run it through printProps():

Code: Select all

#Requires AutoHotkey v2.0-a119-179d27fd

class CB {
	name1 := "John"  ; (1)
	__new(){
		this.name2 := "John"  ; (2)
		this.defineProp( 'name3', { value: "John" } )  ; (3)
	}
	__set( propName, params, value ){
	}
}
B := CB.new()
printProps(B)

printProps(Obj) {
	DefinedProps := []
	visitBases(Obj, Obj => enumerateProps(DefinedProps, Obj))

	str := ''
	for PI in DefinedProps
		str .= PI.toStr() '`n'
	
	MsgBox str
}

visitBases(Obj, fn) {
	%fn%(Obj)

	if (Obj.__Class != 'Any')
		visitBases(Obj.Base, fn)
}

enumerateProps(ByRef Acc, Obj) {
	for propName in ObjOwnProps(Obj)
		Acc.Push(PropInfo.New(propName, Obj))
}

class PropInfo {
	__New(name, Obj) {
		; required like this, since there isnt a builtin 
		; object agnostic "ObjGetOwnPropDesc(name)" function
		static fnGetOwnPropDesc := Object.GetMethod('GetOwnPropDesc')
		propDesc := %fnGetOwnPropDesc%(Obj, name)

		this.Name := name
		this.Type := Type(Obj) 
		this.Class := Obj.__Class 
		this.Desc := propDesc
		this.IsValue := propDesc.HasProp('Value')
	}

	toStr() => Format('{} prop: "{}", belonging to class "{}" (of type "{}")',
		this.IsValue ? 'Value' : 'Dynamic', 
		this.Name, 
		this.Class,
		this.Type)
}
and whatever properties get dumped, those are the properties for which when assigning a value to, meta-set wont be invoked
And when are they considered as not defined ?
at whichever point u attempt, for the first time, to access(get) them or assign a value(set) to them.
Before I got time to say whitch ones are defined, in the constructor ?
i cant understand the question, elaborate.

Code: Select all

	class CB {
		;name := "John"  ; (1)
		__new(){
			;this.name := "John"  ; (2)
			this.defineProp( 'name', { value: "John" } )  ; (3)
		}
		getName(){
			return this.name
		}
		check_HasOwnProp(propName){
			return this.HasOwnProp(propName)
		}
		;__set( propName, params, value ){
		;	Console.Log( "__set is called" )
		;}
	}
	B := CB.new()
	Console.log( B.getName() )
	Console.log( B.check_HasOwnProp('name') ) ; (1) ok, it's defined, but __set is called
	                                          ; (2) ok, it's defined, but __set is called
	                                          ; (3) ok, it's defined, and __set is NOT called
	Console.log( B.check_HasOwnProp('age') )  ; ok, always false
this example, its also unclear what its meant to illustrate. it looks like 3 distinct examples and 2 temporal states have all been mashed together into a single entity.
A language should not surprise us too much and it should be predictible not reading the documentation too much.
and it strives to do so(simply compare and contrast v2 to v1) but with metafunctions being one of the more obscure language features, expecting to be able to grok them purely by osmosis, is something i find rather naive. while its probably possible for them to be further simplified, implementing such a change would come at the expense of flexibility, which, incidentally, just so happens to be a metafunction's raison d'être.
__get/__ set are made to manage propertie dynamicaly and not to retieve values from the data structure
i think the documentation lays it out pretty clearly - "__Get and __Set can be used to implement properties which are known only at the moment they are invoked.", along with use-case of a proxy-object being described further below. of course, u need not limit urself to only this kind of usage. metafunctions can be used for all sorts of things. u can even use only metafunctions and no regular properties/methods in ur code. with v2's command-like syntax, u can create custom DSLs. u can use metafunctions to force consumers of ur libraries into specific patterns. get creative
obj := ClassObj.new()
Ok, __new() is static… but it looks like ClassObj.constructor() where in many modern laguages we find simply ClassObj() and new ClassObj() is a good idear, a common usage.
well, we used to have keyword new ClassObj() and the .New()/.__New() methods have superseded it, so i doubt we're getting new back
I think that's more close from the reality : there is no empty in any string.
some other excerpts from reality include but are not limited to:
  • an empty set is a subset of any set
  • there is an empty string to be found at the very start of every string, and after its first char(if any), and after its second char(if any), and ...., and after every char(if any) thereafter until the end of the string
all ways of handling this scenario are valid, so being explicit about the ambiguity seems like the sanest, albeit clunkiest, choice.
that said, searching with an empty needle more often than not is likely the result of a semantic error that's been propagating throughout ur program. and in case it's not, u can handle the special case and suppress the warning. at least if u forget to do so, a runtime error would remind u of it.
it would be nice to get a stable and intuitive v2
sure, but by whose standards? im fairly certain that to lexikos most of these things make perfect sense, and if u strictly abide by the rules laid out in the documentation, probably to most other people as well. that isnt to say u should take everything at face value
User avatar
kczx3
Posts: 1640
Joined: 06 Oct 2015, 21:39

Re: A strange __set() disturbance

04 Aug 2020, 20:46

I’m by no means even an advanced AHK user but I’ve had very few uses for meta functions in my day. They seem to make things more complex than they need to be.
lexikos
Posts: 9583
Joined: 30 Sep 2013, 04:07
Contact:

Re: A strange __set() disturbance

04 Aug 2020, 22:02

If you define a property by assigning, it is, by definition, "undefined" when the assignment is evaluated, and "defined" afterward. An assignment in the constructor is just that, and no different to an assignment anywhere else. Its effect occurs when it is evaluated, not by its mere presence in the script.
User avatar
FredOoo
Posts: 186
Joined: 07 May 2019, 21:58
Location: Paris

Re: A strange __set() disturbance

05 Aug 2020, 00:33

Yes, I understant now. But… I'll still talk about.
(I need more time to express myself in English than in French…)
(Alan Turing) « What would be the point of saying that A = B if it was really the same thing? »
(Albert Camus) « Misnaming things is to add to the misfortunes of the world. »
User avatar
FredOoo
Posts: 186
Joined: 07 May 2019, 21:58
Location: Paris

Re: A strange __set() disturbance

06 Aug 2020, 17:35

This is a simple ClassObject with a property name

Code: Select all

class CE1 {
	__new(){
		this.name := ''
	}
}
E1 := CE1.new()
E1.name := "MyName"
E1.name  ; MyName

This is a simple ClassObject with a pseudo-private property _name

Code: Select all

class CE2 {
	__new(){
		this._name := ''
	}
	name {
        get {
            return this._name
        }
        set {
            this._name := value
        }
    }
}
E2 := CE2.new()
E2.name := "MyName"
E2.name  ; MyName
By the way, notice I can create another property very easily…

Code: Select all

E2.anotherName := "My Other Name"
E2.anotherName  ; My Other Name

Now I want to create a ClassObject who has the ability to « decide what happens when an undefined property is invoked ».

Code: Select all

class CE3 {
	__new(){
		this.defineProp( 'PROPS', { value: Map() } )
	}
	__set(propName, params, value){
		this.PROPS[propName] := value
	}
	__get(propName, params){
		if( this.PROPS.has(propName) )
			return this.PROPS[propName]
		else
			return 'unset'  ; unset available as a keywork only in function's optional parameters
	}
}
E3 := CE3.new()
E3.myNameIs := "Slim"
E3.myNameIs  ; Slim
E3.myAge := 22
E3.myAge  ; 22
E3.whatTimeIsIt  ; unset
It's not simply the same thing as writing in a property like in previous examples. I can write in a more complexe data structure, or deal with a iniFile…
In reality, I didn't create yet any new properties in meta-methods. I created new items in the list property named PROPS.
But I destroyed my ability as a developer, to talk about that very well know property called PROPS, and defined as a Map.
I had to strongly hard code it with that not friendly syntaxe this.defineProp( 'PROPS', { value: Map() } ) while it's not an unpredictble property. It's a very well known property, except of course on the left side of the affectation.

When I say this.PROPS := Map() inside the class definition (not only the constructor), I think I give it a name (PROPS) and a value (an empty Map), a type (the type of Map) and a scope (the class object). It is not only well known, it is famous ! Everybody knows what I'm talking about, and specially the class object I'm writing in.

Later if from outside the class I invoke E3.myNameIsWho ? E3.myNameIsWhat ? that's an undetermined propery and it's a good thing if I can write a class that knows how to deal with.

But to give this class the ability to respond to unknown properties, I'll first have to rewrite all the definitions of the well known properties
I think that's not fair.


Now, what if I really want to create new properties dynamically ? I would write something like this.

Code: Select all

class CF6 { ; prop created by first call to __get or __set
	__get( name, Params ){
		if( !this.HasOwnProp(name) ) {
			this.createProp( name )
		}
		return this.%name%
	}
	__set( name, Params, value ){
		if( !this.HasOwnProp(name) ) {
			this.createProp( name, value )
		}
	}
	createProp( name, value:='unset' ){ ; if I use unset keyword, I can't use next line
		Console.log "createProp"
		this.DefineProp( name, {Value: value } )
	}
}
F6 := CF6.new()
F6.anyname  ; 'unset'
F6.anyname := 666
F6.anyname  ; 666
F6.anotherOne := 777
F6.anotherOne  ; 777
The nice thing here is that there is no more data structure except the new properties are dynamically stored in the class as if they where declared with this.anyname := 666 and this.anotherOne := 777.
But before that it will be necessary to pull your hair to rewrite all the known properties of the class…

Next step you understant you can dynamicaly create a new property with it's own get/set :

Code: Select all

class CF7 { ;
	__get( name, Params ){
		if( !this.HasOwnProp(name) ) {
			this.createProp( name )
		}
		return this.%name%
	}
	__set( name, Params, value ){
		if( !this.HasOwnProp(name) ) {
			this.createProp( name, value )
		}
	}
	createProp( name, value:='unset' ){ ; if I use unset keyword, I can't use it
		Console.log "createProp"
		this.DefineProp( name, {
			; Doc: An object with one of the following own properties, or both Get and Set:
	        get: CProp.new(),  ; function object to call
	        set: CProp.new()   ; It's just… I don't know how to write it
	                           ; I should write set: CProp.new( value ) ??
	    } )
	}
}
class CProp {
	value := ''
	__call( self, val :=unset ){
		if( !isSet(val) ){
			return this.value
		}else{
			this.value := val
		}
	}
}
F7 := CF7.new()
F7.anyname := 777  ; Does it works ??
F7.anyname  ; Error: This value of type "CF7" has no poperty named anyname
Here v2 is not anymore a simple modernization of the horrible old v1 syntaxe. It's the project of a new language. An interesting project but creating new syntax difficulties until it is finished.

Now, tell this people they should bypass v1 to enjoy the v2 flexibility.
(Alan Turing) « What would be the point of saying that A = B if it was really the same thing? »
(Albert Camus) « Misnaming things is to add to the misfortunes of the world. »
swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: A strange __set() disturbance

07 Aug 2020, 19:30

FredOoo wrote:
06 Aug 2020, 17:35
This is a simple ClassObject with a property name
sure but note E1.name on a line on its own written like this is the invocation of the method .name() passing 0(1* - the this) params, and not property access(get). largely pedantry, but lets be clear about this
This is a simple ClassObject with a pseudo-private property _name
the underscore-identifier is just a convention, its the backing field to a dynamic property. in AHK, there's no such thing as private(pseudo or otherwise) on account of access specifiers not existing. more pedantry
Now I want to create a ClassObject who has the ability to « decide what happens when an undefined property is invoked ».
...
It's not simply the same thing as writing in a property like in previous examples. I can write in a more complexe data structure, or deal with a iniFile…
In reality, I didn't create yet any new properties in meta-methods. I created new items in the list property named PROPS.
ok, its what u chose to do - not to define properties but rather store them in a Map. so?
But I destroyed my ability as a developer, to talk about that very well know property called PROPS, and defined as a Map.
??? u "destroyed ur ability as a developer to talk about the very well known property called PROPS"... i have no idea what this is supposed to mean
I had to strongly hard code it with that not friendly syntaxe this.defineProp( 'PROPS', { value: Map() } ) while it's not an unpredictble property. It's a very well known property, except of course on the left side of the affectation.
the alternative to this being the very friendly syntaxe ObjRawSet(this, 'PROPS', Map()) - quite the difference, really...oh wait, but that wouldnt allow us to define dynamic properties, so we need it to either support an object(ObjRawSet(this, 'PROPS', {Value: Map()})) or a new function altogether(ObjRawSetDynamic(this, 'PROPS', getterFuncObj, setterFuncObj)). there, much better /s
also ???? what was the point of this sentence(other than to clamor about .DefineProps purported unfriendliness, if any)
When I say this.PROPS := Map() inside the class definition (not only the constructor), I think I give it a name (PROPS) and a value (an empty Map), a type (the type of Map) and a scope (the class object). It is not only well known, it is famous ! Everybody knows what I'm talking about, and specially the class object I'm writing in.
 Later if from outside the class I invoke E3.myNameIsWho ? E3.myNameIsWhat ? that's an undetermined propery and it's a good thing if I can write a class that knows how to deal with.
"famous" properties? what? just more ?????
But to give this class the ability to respond to unknown properties, I'll first have to rewrite all the definitions of the well known properties
I think that's not fair.
ull have to rewrite all the definitions of the well known properties... just more ??????? i have no idea what ure trying to say. what are u trying to say?
Now, what if I really want to create new properties dynamically ?
ud use Object.%Expression% := Value or .DefineProp(), u dont need metafunctions to do that. ur code example would have been the answer to "Now, what i really want to handle the creation/access of unknown/undefined properties in a special way?", except u didnt ask this question. also, in the metafunctions, u dont need to check if the property exists - if it did, the metafunction wouldnt have been invoked in the first place.
The nice thing here is that there is no more data structure except the new properties are dynamically stored in the class as if they where declared with this.anyname := 666 and this.anotherOne := 777.
But before that it will be necessary to pull your hair to rewrite all the known properties of the class… 
rewrite all the known properties of the class… more ???????? we're up to 8 of them now

Code: Select all

...
	createProp( name, value:='unset' ){ ; if I use unset keyword, I can't use it
....
what do u mean u cant use it? declare the optional parameter's default to be unset, then test with IsSet() whether something was passed to this parameter, then u can decide whether to "use it" or not. of course, if "using it" means assigning a plain string to it('unset'), then it makes little sense to use the unset/IsSet() combo - u can define primitives to be the default of parameter in the function definition already!(what u cant declare default, is all other stuff: objects, arrays, taking other code paths, etc... This is the purpose of unset - to unambiguously identify whether the caller used the optional parameter to pass a value in)

Code: Select all

...
		this.DefineProp( name, {
			; Doc: An object with one of the following own properties, or both Get and Set:
	        get: CProp.new(),  ; function object to call
	        set: CProp.new()   ; It's just… I don't know how to write it
	                           ; I should write set: CProp.new( value ) ??
	    } )
...
class CProp {
	...
	__call( self, val :=unset ){
		...
	}
}
as long as u define an appropriate constructor(with the correct number of parameters), u can write whatever u want, including ... set: CProp.new( value ) }

Code: Select all

F7.anyname := 777  ; Does it works ??
it "works", insofar as defining a property called anyname on the object F7, which has been instantiated from the class template CF7. it does not assign 777 to anything, in fact, it discards this value(u arent doing anything with it anywhere).


Code: Select all

F7.anyname  ; Error: This value of type "CF7" has no poperty named anyname
again, this usage is invoking the method .anyname() with 0 params, but lets ignore that and assume ure actually getting the property's value.
  • for the getter, u had assigned an object, instantiated from the class template CProp - a function object to call, so to speak.
  • the property's getter will now attempt to invoke said function object, ie the equivalent of %instanceOfCProp%(F7, 'anyname').
  • having thoroughly read the half-a-paragraph on metafunctions in the documentation, im sure ull recall that "obj.unk() invokes __Call", whereas "%x%(): Calling the object itself only invokes the Call method. This includes internal calls made by built-in functions such as SetTimer and Hotkey."
  • well, ur function object doesnt appear to have a Call method defined on it. the invocation fails, u get an error thrown
the only point of contention here is whether the error message fits or not.
on one hand, u could have gotten the standard "This value of type ???(in ur case CProp, the instantiated funcObj) doesnt have a method named 'Call'". this might be confusing in the context of properties
on the other hand, ur are getting a more relevant, albeit incorrect(with respect to the property having been defined, since it truly is defined - its just that its absolutely unusable thereafter), message "This value of type "CF7" has no property named 'anyname'"
regardless of the nature of the message, there is a semantic error - namely, ur function object not being an actual function object(on account of not having a Call method. again read the docs: "User-defined function objects must define a Call method ...")

Here v2 is not anymore a simple modernization of the horrible old v1 syntaxe. It's the project of a new language. An interesting project but creating new syntax difficulties until it is finished.
wrong. anonymous functions is the only "new language" feature. otherwise, its a modernization of the horrible old v1 syntaxe:
  • commands -> functions
  • hotkeys -> functions
  • gui commands -> gui object model
any other remaining changes relate to:
  • internals(optimizations, data structures, parser changes, type checking, saner separated prototypal model, ridding the codebase of v1 idiosyncrasies)
  • error reporting(self explanatory)
  • streamlining usage(eg ComCall, #DllLoad etc)
as a matter of fact, nothing about these examples cant be reimplemented in v1. the only difference is, unless u a priori knew how to do it, owing to v1's 1% error-reporting and 99% error-suppression, u would have wasted a lot more time doing it(and u probably wouldnt have managed to do it correctly anyway. or worse yet, u wouldnt have even known it wasnt correct to begin with). with v2, at least u know theres a problem as soon as u hit run, so u can go fix it.
Now, tell this people they should bypass v1 to enjoy the v2 flexibility.
they should
User avatar
FredOoo
Posts: 186
Joined: 07 May 2019, 21:58
Location: Paris

Re: A strange __set() disturbance

07 Aug 2020, 23:15

I would have preferred a simple v2 like VB or Java, which remains stable on the language, and which focuses on AHK itself and its own objects.
A language which assigns variables with =, starts indexes at 0, creates classes with simple parentheses but which knows the notion of privacy. But above all a language without surprises. The only goal should be to break out of the sickly alien syntax of v1 and provide good documentation.
The invention of a new language is another project, it could have been called v3 or otherwise, but what good, there are already plenty of languages.
Why do you think people stay on v1? It's a shame… A pain for those who don't know that v1 has a fishmonger's syntax. And a nightmare also for those who are used to modern languages.
No one is interested in v1 or v2 language. The good thing in AHK is to quickly make a little useful and handy script in the Windows API.
They should.
It's a bit short as an argument. I did, from the begining. Just read the examples in v1 (because they are the most numerous) but write in v2.
In fact, AHK is stuck between two problems. An archaic version and a post-modern version in development. It is really regrettable because AHK is such an excellent tool.

But the good thing is that my keyboard writes with accents even on upper case letters, I can write Æ and Œ easily, put thin non-breaking spaces in the right places and other nice small characters. I have been using it every day for over a year.
That's all I asked AHK. The rest is small developments for fun.
(Alan Turing) « What would be the point of saying that A = B if it was really the same thing? »
(Albert Camus) « Misnaming things is to add to the misfortunes of the world. »
swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: A strange __set() disturbance

08 Aug 2020, 18:06

FredOoo wrote:I would have preferred a simple v2 like VB or Java
i, too, would have preferred $LANGUAGE_IM_WORKING_IN to have been an amalgamation of all my favorite features from languages 1..N, but that doesnt seem all that realistic. i guess it could be... if i wrote my own language! however, syntax differences alone hardly provide an impetus for me to take the plunge. that would be the penultimate form of NIH syndrome, right next to designing and manufacturing my own silicon!
FredOoo wrote:which remains stable on the language
to remain stable on the language, means to preserve all inconsistencies. v2 is not about doing that
FredOoo wrote:A language which assigns variables with =
lol, complaining about the one thing that AHK actually got right
FredOoo wrote:starts indexes at 0
0 based or 1 based, spaces or tabs, TP over or under - everlasting questions bound to plague humanity for the rest of eternity.
FredOoo wrote:creates classes with simple parentheses
Class(), new Class(), Class.New() - potato/potato
FredOoo wrote:which knows the notion of privacy
point conceded. this is needed
FredOoo wrote:But above all a language without surprises
theres no such language
FredOoo wrote: The only goal should be to break out of the sickly alien syntax of v1 and provide good documentation.
ure not seriously suggesting we break compatibility just to end up with v2's syntax and the same v1 problems?? there would be absolutely no incentive to switch. today u can rewrite all v1 commands as functions and implement a gui Obj Model(in fact, im pretty sure someone must have already done this, if u dig around a bit). what u cant do is fix the guts of the language. and if ure gonna break compatibility and make people rewrite their scripts come release, those guts had better been damn fixed.
FredOoo wrote:Why do you think people stay on v1?
because up until a year ago there wasnt even a download link for v2 on the main page? because the button says "Alpha" on it?
No one is interested in v1 or v2 language.
well, i dont have any telemetry to back this up(funnily enough, neither do u), but purely based on forum activity, id say interest in both v1 and v2 is steadily increasing(lotsa new users, swamped Ask For Help, ignoring the odd crawler/spammer here and there)
FredOoo wrote:It's a bit short as an argument
did u just skip to the last horizontal bar and forget to read the rest of it? lol
FredOoo wrote: Just read the examples in v1 (because they are the most numerous) but write in v2.
for that to pan out, u would have to A) know very well what the v1 examples are doing and be fully aware of their intricacies; and B) know very well what those intricacies map to in v2. just replacing syntax is probably only applicable to the most basic of scripts
FredOoo wrote: AHK is stuck between two problems. An archaic version and a post-modern version in development. It is really regrettable because AHK is such an excellent tool.
revamping an inherited legacy codebase is a massive undertaking even for big teams, let alone a single dev(and other occasional contributors). its not like working on v2 is lexikos' day job either. as regrettable as things may be, thats the way they are
FredOoo wrote:That's all I asked AHK.
AHK's got a little something for everyone :)

Return to “Ask for Help (v2)”

Who is online

Users browsing this forum: Albireo, coder96, Frogrammer, ntepa, w_i_k_i_d and 59 guests