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):
- (B) has an extra "get" operation, which can result in a __get call.
- (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.