Objects as functions - question.

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
User avatar
trismarck
Posts: 506
Joined: 30 Sep 2013, 01:48
Location: Poland

Objects as functions - question.

14 Oct 2013, 10:45

Documentation wrote:In most cases obj.func[obj] does not exist and obj.func's __Call meta-function is invoked instead.
How to make it happen so that obj.fun[obj] exists? - if obj contains an object, I can't have a key whose name is an object?
Even if it was obj.fun["obj"] I also couldn't 'invoke' that.
HotKeyIt
Posts: 2364
Joined: 29 Sep 2013, 18:35
Contact:

Re: Objects as functions - question.

14 Oct 2013, 10:51

Like that?

Code: Select all

Call:=[]
obj:={(Call):"Call"}

obj[Call]()

Call(){
    MsgBox Call
}
User avatar
trismarck
Posts: 506
Joined: 30 Sep 2013, 01:48
Location: Poland

Re: Objects as functions - question.

15 Oct 2013, 02:08

... :shock: this is what I was doing so far:

Code: Select all

obj := Object()
obj2 := Object()
obj.fun := {base: objBase, (%obj2%), "bbb"}
(%obj2%) - a _double_ dereference

Thanks HotkeyIt.

One part of the puzzle solved, but another remains:

Code: Select all

class objBase {
	__Get() {
		MsgBox, % "__Get()"
	}
	__Set() {
		MsgBox, % "__Set()"
	}
	__Call(params*) {
		MsgBox, % "__Call()"
	}
}
obj := Object("base", objBase)
ObjInsert(obj, "fun", Object("base", objBase) )
ObjInsert(obj.fun, obj, "aaa")
MsgBox, % obj.fun[obj]	; aaa
MsgBox, % obj.fun()	; ""
	; => because obj.fun contains an object, according to the doc, this should translate to: obj.fun[obj] ?
lexikos
Posts: 9592
Joined: 30 Sep 2013, 04:07
Contact:

Re: Objects as functions - question.

15 Oct 2013, 15:50

If obj.func contains an object, it is invoked using obj as the key. In most cases obj.func[obj] does not exist ...
but in this case, obj.func[obj] does exist - it contains "aaa". If you defined a function named "aaa" in your script, obj.func() would call it.
User avatar
trismarck
Posts: 506
Joined: 30 Sep 2013, 01:48
Location: Poland

Re: Objects as functions - question.

16 Oct 2013, 03:21

Thanks Lexikos, now its more clear for me.
lexikos wrote:but in this case, obj.func[obj] does exist - it contains "aaa".
Documentation wrote:When a call such as obj.func(param) is made, obj.func may contain a function name or an object. If obj.func contains an object, it is invoked using obj as the key. In most cases obj.func[obj] does not exist
The way I understood the documentation was that if obj.func() is called and if obj.func[obj] _does_ exist (if obj.func object _has the key_ whose name is 'the object' obj), then _it_ (the obj.func object) should be _invoked_ (evaluated) using obj _as the key_ (as the key of the obj.func object). => This: obj.func() should translate to this: obj.func[obj] and obj.func[obj] should be evaluated to the value of the key [obj].

If that happened, Msgbox would show "aaa":

Code: Select all

MsgBox, % obj.fun()   ; ""
But it doesn't.


How I should have read the documentation is like this:

if obj.func() is called and if obj.func[obj] _does_ exist (if obj.func object _has the key_ whose name is 'the object' obj), then _it_ (the obj.func object) (the function whose name is inside of the value of the obj.func[obj] key) should be _invoked_ (evaluated) using obj _as the key_ (this part I don't get - what is 'the key' here? - there are no params (no keys / values of keys) passed to the function named "aaa").

The misleading part was the word 'it' here:
Documentation wrote:If obj.func contains an object, it is invoked using obj as the key. In most cases obj.func[obj] does not exist and obj.func's __Call meta-function is invoked instead.
What I've done is that I've incorrectly assumed that 'it' is just obj.fun[obj] (the key), whereas what the documentation meant by saying obj.fun[obj] was _the value_ of that key (the value of that key, which _might_ be a string that is a name of the function).

What I should have taken into the consideration is that later on the documentation states:
In most cases obj.func[obj] does not exist and obj.func's __Call meta-function is _invoked_ instead.
__Call() is called (invoked), instead of some other _function_, that might have been called earlier.


Documentation wrote:If obj.func contains an object, it is invoked using obj as the key.
Does this really translate to:

Code: Select all

obj.fun[obj].()
instead of to:

Code: Select all

obj.fun[obj]
? (as one might presume after reading the docs)



=> if obj.func[obj] contains a valid function name, then this happens:

Code: Select all

obj.fun[obj].()
otherwise (if obj.func[obj] does not contain a valid function name? / or valid fn reference / or valid name of a method?) this happens:

Code: Select all

obj.fun.__Call()
but in either case, this _doesn't_ happen:

Code: Select all

obj.fun[obj]
(or actually, it might happen, but always _as a middle step_, _before_ the if statement?)


Question summary:
  1. What does the documentation mean by 'as the key' in the following sentence: (in the context I've described)
    If obj.func contains an object, it is invoked using obj as the key.
  2. Documentation wrote:If obj.func contains an object, it is invoked using obj as the key.
    Does this really translate to:

    Code: Select all

    obj.fun[obj].()
    instead of to:

    Code: Select all

    obj.fun[obj]
    ? (as one might presume after reading the docs)
  3. Are _method_ names / function _references_ supported with the syntax obj.func()? (actually, I could check this by myself)
  4. Are there any errors in what I've just written?
  5. Why was the behavior of calling the function whose name is inside of the obj key of the obj.fun object, when obj.func() call is made, introduced? (is this an artifact of some other mechanism or is there the use for this behavior?)
lexikos
Posts: 9592
Joined: 30 Sep 2013, 04:07
Contact:

Re: Objects as functions - question.

16 Oct 2013, 05:08

One thing to keep in mind is that the meta-functions (__get, __set and __call) are only ever called when the object is invoked with a key which has no associated value within the object. Whether the value is a valid function has no impact on this.
What does the documentation mean by 'as the key' in the following sentence?
If obj.func contains an object, it is invoked using obj as the key.
In x[y], y is the key. The same goes for x[y](), where the key (y) is used to find the function or method within x. The point quoted above is important because the "key" is passed to __call as a parameter.
Does this really translate to:
obj.fun[obj].()
No. Your code is equivalent to obj["fun", obj].(); i.e. obj is being invoked, not obj.fun. I think what you meant was (obj.fun)[obj].(). However, this is also incorrect as it is evaluated like this:
  • Evaluate obj.fun; call the result tmp1.
  • tmp1[obj]: "Get" the value within tmp1 which is associated with the key obj; call the result tmp2.
  • tmp2.(): "Call" the function/method of tmp2 which is associated with the name/key "". If there is no value associated with "", invoke the __call meta-function, passing "" for the key/name parameter.
obj.fun[obj] is not an appropriate translation as it targets obj rather than obj.fun, and omits the all-important parentheses which mark it as "call" rather than "get".

When obj.func contains an object, obj.func() translates to (obj.func)[obj](), which often translates to something similar to (obj.func).__Call(obj).

There are two differences between (obj.func)[obj]() (A) and (obj.func)[obj].() (B):
  1. (B) has an extra "get" operation, which can result in a __get call.
  2. (B) passes "" to __call, where (A) passes obj. This is important because it means (A) allows the functor (function-object) to know which object was originally being invoked.
Are _method_ names / function _references_ supported with the syntax obj.func()?
func is a method name. A method is just a function which accepts an object (this) as its first parameter. When you define a method (Y) in a class (X), the interpreter assigns a function reference to X.Y.
Why was the behavior of calling the function whose name is inside of the obj key of the obj.fun object, when obj.func() call is made, introduced?
It is the simplest way to achieve the end result of passing obj to the __Call meta-function of the functor (function-object). Otherwise, the only way to know would be to store obj in the functor, which would preclude the use of a single functor for multiple objects (directly or via inheritance). If you like, you can try to decipher the comments I wrote in the source code:
// Something must be inserted into the parameter list to remove any ambiguity between an intentionally
// and directly called function of 'that' object and one of our parameters matching an existing name.
// Rather than inserting something like an empty string, it seems more useful to insert 'this' object,
// allowing 'that' to change (via __Call) the behaviour of a "function-call" which operates on 'this'.
// Consequently, if 'that[this]' contains a value, it is invoked; seems obscure but rare, and could
// also be of use (for instance, as a means to remove the 'this' parameter or replace it with 'that').
I will reword the documentation as below:
If obj.func contains an object, that object is invoked with obj in place of the method name, as in (obj.func)[obj]().
User avatar
trismarck
Posts: 506
Joined: 30 Sep 2013, 01:48
Location: Poland

Re: Objects as functions - question.

22 Oct 2013, 09:13

Amazing, thanks Lexikos.
Lexikos wrote:One thing to keep in mind is that the meta-functions (__get, __set and __call) are only ever called when the object is invoked with a key which has no associated value within the object. Whether the value is a valid function has no impact on this.
Yes, you've probably wrote that because of this topic: . But putting that topic aside.
The following example 'appears' to be contradictory to the quote. Lets suppose that we have a variable named obj. If we now 'call' obj.(), two things may happen, depending on the contents of obj:
  • if obj contains an object*, then the obj.() is equivalent to: call the method, whose name is "" from the object obj. If that method doesn't exist, then call __Call() upon the object.
  • if obj contains an AHK object that is a _function reference_, then 'call the function that the function reference object contains'.
* - by obj in this sentence, I mean an AHK object that is an _associative array_ AHK object (and not i.e. a AHK function reference object or AHK file object). This is important as assigning / removing user-defined keys from objects other than AHK associative objects is impossible:

Code: Select all

chicken() {
msgbox, % "chicken()"
}

obj := Func("chicken")
obj.a := 3
msgbox, % obj.a ; ""
The problem I have with the obj.() syntax is that it looks that, when obj contains the AHK associative array object, that the dot operator just retrieves the key whose name is "" (obj.() is then equivalent to obj[""]() ). But when obj contains the AHK function reference object, it looks like the dot operator does something else? I've concluded that the dot operator 'does something else' in that example after running the following code:

Code: Select all

obj := Func("chicken")
msgbox, % isFunc(obj[""]) ; 0
If the empty key "" of the object obj doesn't contain a reference to a function, then how come obj.() works? - if the dot operator would retrieve the key, then we already know that what's inside of that key isn't a valid function, so that function can't be called? But yet the function _is_ called. The other explanation for this behavior is that "" contains something that is not considered a function by isFunc(), but yet AHK interprets it as a function call.

Lexikos wrote:
Trismarck wrote:What does the documentation mean by 'as the key' in the following sentence?
If obj.func contains an object, it is invoked using obj as the key.
In x[y], y is the key. The same goes for x[y](), where the key (y) is used to find the function or method within x. The point quoted above is important because the "key" is passed to __call as a parameter.
Yes, so x[y]() is really a computed method call - a call to a method whose name is computed. Note that the key is only passed to __Call() if the object x doesn't have the key whose name is the contents of the variable y. Otherwise (the requested key y contains in a [reference to a] method), the method of the object is invoked as usual.
Lexikos wrote:
Trismarck wrote:Does this really translate to:
obj.fun[obj].()
No. Your code is equivalent to obj["fun", obj].(); i.e. obj is being invoked, not obj.fun. I think what you meant was (obj.fun)[obj].().
At that point in time, I've just 'made up' an example that 'would work' (in this case - that would just call the function). What I've meant _was_ (obj.fun)[obj].(), even though I haven't included parentheses around it. What I've later found was that even if obj.fun[obj].() translates to obj["fun",obj].(), then the "three or more parameters __Get() meta-function" (that usually works on the syntax a[b,c,d], when a _doesn't_ have the key b) is _not_ called here. __Get() [,,] is not called, because __Get() is called only for the first _empty_ key in the sequence: a["b", "c", "d", "e"]. I.e. if a["b"] would contain an object, then __Get() would be called for b["c", "d", "e"], but not for a["b", "c", "d", "e"]). The same rule applies to __Set() that is working with [,,]. Actually, at that point in time I didn't even know how __Get() works with [,,] .
I remember I've originally _wanted_ to use the the () operator (instead of the .() 'operator'), but this: (obj.fun[obj])() failed, so I just gave up () in favor of .().


Lexikos wrote: Your code [(obj.fun[obj].() )] is equivalent to obj["fun", obj].(); i.e. obj is being invoked, not obj.fun. I think what you meant was (obj.fun)[obj].()
Note to self: note that if we have: obj.fun[obj].() , then if obj.fun contains an object, then what is 'invoked' is not obj, but obj.fun. ... I guess I've just misunderstood the word 'invoke'. To me, 'invoke' meant: invoke the function [obj] that is inside of the object fun.obj, whereas the meaning was: [if necessary,] 'invoke' __Get() or __Set() and pass obj as this to __Get() or __Set().
Lexikos wrote: Your code [(obj.fun[obj].() )] is equivalent to obj["fun", obj].(); i.e. obj is being invoked, not obj.fun. I think what you meant was (obj.fun)[obj].()
However, this is also incorrect as it is evaluated like this:
  • Evaluate obj.fun; call the result tmp1.
  • tmp1[obj]: "Get" the value within tmp1 which is associated with the key obj; call the result tmp2.
  • tmp2.(): "Call" the function/method of tmp2 which is associated with the name/key "". If there is no value associated with "", invoke the __call meta-function, passing "" for the key/name parameter.


obj.fun[obj] is not an appropriate translation as it targets obj rather than obj.fun, and omits the all-important parentheses which mark it as "call" rather than "get".

Lexikos wrote: obj.fun[obj] is not an appropriate translation as it targets obj rather than obj.fun, and omits the all-important parentheses which mark it as "call" rather than "get".

Right, it targets obj instead of obj.fun, because obj.fun[obj]() -> obj["fun", obj]() and obj["fun", obj]() can't be split - obj["fun", obj]() is treated like a function call (with two implicit? parameters: "fun" and obj and with a target that is the obj object (the target being the left most 'obj' in the expression). Method calls that take more than one implicit? parameter are not supported. Note that those method calls are also not supported:

Code: Select all

obj["a", "b"]()
obj["a", "b", "c"]()
obj["a", "b", "c", "d"]()
and so on, whereas those are valid calls, because .() is not 'binded' to []'s:

Code: Select all

obj["a", "b"].()
obj["a", "b", "c"].()
obj["a", "b", "c", "d"].()
Note also that this: obj.fun["a", "b", "c"]() will _also_ convert to: obj["fun", "a", "b", "c"]() when parentheses are not used. But even if parentheses would be used, then (obj.fun)["a", "b", "c"]() -> tmp["a", "b", "c"](), which is, again, an unsupported method call.
Lexikos wrote: When obj.func contains an object, obj.func() translates to (obj.func)[obj](), which often translates to something similar to (obj.func).__Call(obj).
Right; this is because (if obj.fun contains an _object_ (vs i.e. a reference to a fn) ) obj.fun() -> (obj.func)[obj]() -> tmp[obj]() -(computed method name)-> -(the key whose name is obj usually doesn't exist)-> -(we have something like this: tmp._unknownMethod_())-> __Call() is called when an unknown method is requested from the object.
Lexikos wrote: There are two differences between (obj.func)[obj]() (A) and (obj.func)[obj].() (B):
  1. (B) has an extra "get" operation, which can result in a __get call.
  2. (B) passes "" to __call, where (A) passes obj. This is important because it means (A) allows the functor (function-object) to know which object was originally being invoked.
Are there any general schemes on how to determine, what implicit parameters are passed to the function, depending on how the expression that calls the function looks like? (if yes , what are those schemes)
Lexikos wrote:
Are _method_ names / function _references_ supported with the syntax obj.func()?
func is a method name. A method is just a function which accepts an object (this) as its first parameter. When you define a method (Y) in a class (X), the interpreter assigns a function reference to X.Y.
Lexikos wrote:
Trismarck wrote:Why was the behavior of calling the function whose name is inside of the obj key of the obj.fun object, when obj.func() call is made, introduced?
It is the simplest way to achieve the end result of passing obj to the __Call meta-function of the functor (function-object). Otherwise, the only way to know would be to store obj in the functor, which would preclude the use of a single functor for multiple objects (directly or via inheritance). If you like, you can try to decipher the comments I wrote in the source code:
I don't know that is the function-object here (what particular object is it). I guess the meaning of this is that instead of putting the target object inside of some other object, that target object can be passed _through the syntax of the expression_ to the function (as the second parameter).
// Something must be inserted into the parameter list to remove any ambiguity between an intentionally
// and directly called function of 'that' object and one of our parameters matching an existing name.
// Rather than inserting something like an empty string, it seems more useful to insert 'this' object,
// allowing 'that' to change (via __Call) the behaviour of a "function-call" which operates on 'this'.
// Consequently, if 'that[this]' contains a value, it is invoked; seems obscure but rare, and could
// also be of use (for instance, as a means to remove the 'this' parameter or replace it with 'that').
I'd have to test __Get() and __Call() more to try to grasp that.
lexikos
Posts: 9592
Joined: 30 Sep 2013, 04:07
Contact:

Re: Objects as functions - question.

22 Oct 2013, 17:03

The following example 'appears' to be contradictory to the quote.
In what way? Are you referring to the quote at the top of your post? You can get all sorts of wrong meanings from anything if you pull it out of context. I said "the object", not "any object". It is implied that I was talking about an associative array, because that's what we were working with, you can't define meta-functions for other types, and the concept of "an associated value" only applies to associative arrays.
The problem I have with the obj.() syntax is that it looks that, when obj contains the AHK associative array object, that the dot operator just retrieves the key whose name is ""
Once again, "" is the key. obj[""] retrieves the value associated with that key. obj.() retrieves the value associated with that key and then immediately calls it. Obviously other types of objects don't do this, because they aren't associative arrays and therefore don't have values associated with keys. The expression just "asks" the object to call a method, and the object decides what happens.
Note to self: note that if we have: obj.fun[obj].() , then if obj.fun contains an object, then what is 'invoked' is not obj, but obj.fun.
Incorrect. obj is invoked, then obj.fun is (presumably) invoked. The expression just invokes obj, and obj does whatever it wants. It is equivalent to ObjCall(obj, "fun", obj), where ObjCall is an internal pseudo-function which calls IObject::Invoke (an interface method defined by the object).
Are there any general schemes on how to determine, what implicit parameters are passed to the function, depending on how the expression that calls the function looks like?
I'm not sure what you mean, but I'd say it also depends on the object which you are calling, not just how the expression looks.
I don't know that is the function-object here (what particular object is it).
obj.fun, of course (in the context where obj.fun is an object, but not a reference to a function, and is being called like a function).
I'd have to test __Get() and __Call() more to try to grasp that.
Don't bother. I was just rambling. ;)
User avatar
trismarck
Posts: 506
Joined: 30 Sep 2013, 01:48
Location: Poland

Re: Objects as functions - question.

13 Jan 2015, 09:10

Lexikos wrote:
Why was the behavior of calling the function whose name is inside of the obj key of the obj.fun object, when obj.func() call is made, introduced?
It is the simplest way to achieve the end result of passing obj to the __Call meta-function of the functor (function-object). Otherwise, the only way to know would be to store obj in the functor, which would preclude the use of a single functor for multiple objects (directly or via inheritance). If you like, you can try to decipher the comments I wrote in the source code:
// Something must be inserted into the parameter list to remove any ambiguity between an intentionally
// and directly called function of 'that' object and one of our parameters matching an existing name.
// Rather than inserting something like an empty string, it seems more useful to insert 'this' object,
// allowing 'that' to change (via __Call) the behaviour of a "function-call" which operates on 'this'.
// Consequently, if 'that[this]' contains a value, it is invoked; seems obscure but rare, and could
// also be of use (for instance, as a means to remove the 'this' parameter or replace it with 'that').
Regardless of what was previously in this topic, I guess the quote means: we insert 'this' into the param array so that we're sure that when 'that' is invoked, 'this' won't match an existing name within 'that'. At the same time, we want to somehow pass 'this' to 'that'. So we insert 'this' as the first parameter of the parameter array to be passed to the to-be-invoked 'that', hoping that 'that' won't have the key this and __Call of the base of the functor object will be called instead. This way:
  • the functor knows, based upon what object the functor was invoked upon
  • we're almost sure that we'll be able to define custom behaviour of the functor object through __Call (bc of this not existing within that).
User avatar
Relayer
Posts: 160
Joined: 30 Sep 2013, 13:09
Location: Delaware, USA

Re: Objects as functions - question.

15 Jan 2015, 10:33

OMG... I love objects and classes but I have to say that this thread hurts my brain. I don't even know where to begin to understand or follow what you guys are debating. I like to understand everything but for now I guess I'll have to be satisfied with using what works for me in my scripts and avoid some of the other nuances.

Is there anything you can point to that would help a dweeb like me fully understand the nuances of objects?

Relayer
lexikos
Posts: 9592
Joined: 30 Sep 2013, 04:07
Contact:

Re: Objects as functions - question.

15 Jan 2015, 21:10

Use classes, avoid meta-functions, and you'll be fine.
Relayer wrote: I don't even know where to begin to understand or follow what you guys are debating.
It's mostly academic, since no one should really be using objects that way (where this[MethodName][this] has a value).
I wrote:Don't bother. I was just rambling.
It's also probably going to change in v2, so that when an object is used as a method, the object internally calls method_object.Call(this, ...). That should be easier to use and much easier to understand. Dynamic function calls like %func_object%() in v2 will probably also use func_object.Call() internally, the only difference being that the this parameter isn't passed. You can already use Func.Call() in v1.1.19+ if you call it directly yourself.
User avatar
joedf
Posts: 8958
Joined: 29 Sep 2013, 17:08
Location: Canada
Contact:

Re: Objects as functions - question.

18 Jan 2015, 02:01

In a sense, this is partly related to syntax problems/conflicts... I think?
Image Image Image Image Image
Windows 10 x64 Professional, Intel i5-8500, NVIDIA GTX 1060 6GB, 2x16GB Kingston FURY Beast - DDR4 3200 MHz | [About Me] | [About the AHK Foundation] | [Courses on AutoHotkey]
[ASPDM - StdLib Distribution] | [Qonsole - Quake-like console emulator] | [LibCon - Autohotkey Console Library]

Return to “Ask for Help (v1)”

Who is online

Users browsing this forum: downstairs, filipemb, Google [Bot], OrangeCat, roysubs and 163 guests