Function Reference

Get help with using AutoHotkey and its commands and hotkeys
samardac
Posts: 212
Joined: 30 Nov 2014, 13:36

Function Reference

08 Jul 2015, 03:09

Hay I just discover Function Reference, looks like I understood how it works, but what is for?
In witch situation we have to use it?
http://ahkscript.org/docs/Objects.htm#F ... References
User avatar
Blackholyman
Posts: 1293
Joined: 29 Sep 2013, 22:57
Facebook: socialjsz
Google: +Jszapp
Location: Denmark
Contact:

Re: Function Reference

08 Jul 2015, 03:34

Did you also read http://ahkscript.org/docs/objects/Func.htm

I have not used it much yet

one example

Code: Select all

MyFunc := Func("MyTimer")
SetTimer, % MyFunc, 1000
return

MyTimer()
{
	static Count
	Count++
	ToolTip % Count
	if (count > 20)
		ExitApp
}
Also check out:
Courses on AutoHotkey

My Autohotkey Blog
:dance:
Coco-guest

Re: Function Reference

08 Jul 2015, 08:22

I find it useful for cases like this:

Code: Select all

MsgBox % _Ord("A")

_Ord(s)
{
	static Ord := Func(A_AhkVersion<"1.1.21" ? "Asc" : "Ord")
	return %Ord%(s)
}
samardac
Posts: 212
Joined: 30 Nov 2014, 13:36

Re: Function Reference

08 Jul 2015, 09:01

I thought the main aim of it was set methods for objects when they defined this way:

Code: Select all

MyObj := {Key: Func("MyMsgBox")}
MyObj.Key.()
MyMsgBox()
{
	MsgBox !!!
}
But looks like there are the other implementation of this, interesting example with timer, where you use function instead of label.

Example with A_AhkVersion I could not understand. :D
User avatar
trismarck
Posts: 506
Joined: 30 Sep 2013, 01:48
Location: Poland

Re: Function Reference

09 Jul 2015, 12:09

samardac wrote:Hay I just discover Function Reference, looks like I understood how it works, but what is for?
The function reference seems to be the standard way of referring to any function. Referring to the function by name is the old way to do the same. The advantages of using the function reference would be:
  • type information - the function object vs a string, which (the string) could be a function name or not
  • speed - calling the function by reference vs searching for the function name in the list of all functions every time
  • access to information about the function, like .IsBuiltIn or .MaxParams()
  • not really an advantage, but just to point it out - various operators work differently with function names/function references, so consistently using function references helps to make the program more 'consistent'
To clarify, Blackholyman's example was to depict that function references _can_ be used i.e. with SetTimer, to trigger a function. At the same time, to trigger a function, in addition to passing the reference to a function, one can also pass the name of the function:
SetTimer wrote:[v1.1.20+]: If not a valid label name, this parameter can be the name of a function, or a single variable reference containing a function object. For example, SetTimer %funcobj%, 1000 or SetTimer % funcobj, 1000. Other expressions which return objects are currently unsupported.
The same thing is true for Coco's example, so I guess I'm just misinterpreting it. Func() can be used to call either Ord or Asc, but doesn't have to be - %Ord%() could just as well call the fn by name.
samardac wrote:In witch situation we have to use it?
http://ahkscript.org/docs/Objects.htm#F ... References
I'd say in wherever we can - the question should actually be asked backwards - when should we _not_ use function references. And I guess the answer would be almost never - even if one needs to evaluate the name of the function to be called dynamically, i.e. by doing something like f := "fun" . 1, %f%(), it would be a good practice? to obtain a function reference in that case anyway (vs call the fn by name).

Here are some notes on how various operators differ in behaviour when the value is a function name / function reference:

Calling a key of an object, whose value (key's value) contains fn name/fn ref
Objects wrote:When thing.test() is called, thing is automatically inserted at the beginning of the parameter list. However, for backward-compatibility, this does not occur when a function is stored by name (rather than by reference) directly in the object (rather than being inherited from a base object).

Code: Select all

thing := {foo: "123"}

thing.test := Func("thing_test")
thing.test()
	; called
	
thing.test := "thing_test"
thing.test()
	; not called bc 'this' was not inserted into param array, which in turn caused
	; too few parameters being passed to function under 'thing.test()'
	; and thus Autohotkey aborted calling the function
	; 

thing_test(this) {
   MsgBox % this.foo
}
The bolded part of quote was already mentioned here. As of why 'this' was inserted even if the object didn't have a base object, that would I guess be a topic for another discussion. In the context of fn name/fn reference, we can see that thing.test() works differently depending on whether the value of thing.test is a fn name or a fn reference.

%f%() vs f.() vs f.Call()
Here is a table that sums up all the three, when either %f%(), f.() or f.Call() is called and f contains a given value:

By what actually happens (by behaviour):
  • function exists and by reference
    • %f%() - function called
    • f.() - function called
    • f.Call() - function called
  • function exists and by name
    • %f%() - function called
    • f.() - default base object called before, function called after
    • f.Call() - default base object called 'before', function not called after
  • function doesn't exist
    • %f%() - default base object called, second param ""
    • f.() - default base object called, second param ""
    • f.Call() - default base object called, second param "Call"
By features added to happen after primary 'function' of the operator:
  • function exists and by reference
    • %f%() - function called
    • f.() - function called
    • f.Call() - function called
  • function exists and by name
    • %f%() - function called
    • f.() - default base object called before, function called after
    • f.Call() - default base object called 'before', function _not_ called after
  • function doesn't exist
    • %f%() - default base object called, second param ""
    • f.() - default base object called, second param ""
    • f.Call() - default base object called, second param "Call"
By looking at the 'feature' table, it looks like the feature to call the function by name added to f.() is no more there in case of f.Call() - f.Call() won't call a function by name.
When function doesn't exist, all three methods resort to default base object - ok and desired.

By looking at the 'by behaviour' table, we can see that calling the function by name is discouraged - the default base object triggers in case of f.() and f.Call() (so that we can catch that i.e. by using the default base object for debugging purposes) and in case of f.Call(), the function isn't called even if it exists.

For calling functions, the .Call() operator is the newest, so I guess should be used whenever possible.

An example for the tables above:

Code: Select all

"".base.__Call := Func("__Call")

f := Func("chicken2")
f.()

chicken(aP*) {
	msgbox % "chicken" "`n" ObjToString(aP)
}
__Call(aP*) {
	msgbox % "base.__Call" "`n" ObjToString(aP)
}
TODO: documentation quotes for above behaviour.
  • %f%()
    Functions wrote:In v1.1.07.00+, Var in %Var%() can contain a function name, function reference or object imitating a function. If the function does not exist, the default base object's __Call meta-function is invoked instead.
  • f.()
    Objects wrote:If the variable func contains a function name, the function can be called one of two ways: %func%() or func.().
    Objects wrote:When a non-object value is used with object syntax, the default base object is invoked. This can be used for debugging or to globally define object-like behaviour for strings, numbers and/or variables.



By looking at the above, one could wonder, is it be faster to do something like: %fun%abc() or fun2 := Func(fun "abc"), fun2.Call() - the first case always searches for the fn and the second case could only search for the fn one time. But this seems out of context for this thread.

------------
All of the above is about [references to] the Func Object. But besides the Func Object, there are also other types of 'function' objects, like the BoundFunc object, a COM object a javascript object returned by a COM object or other. One could create a reference to each of those and then a call like f.Call() could also work if 'f' contained a COM object.

-----------
samardac wrote:I thought the main aim of it was set methods for objects when they defined this way:
Yes, values of keys of the class object do contain function references (vs function names):

Code: Select all

class cx {
	fun() {
	}
}
msgbox % IsObject(cx.fun)
Last edited by trismarck on 11 Jul 2015, 07:35, edited 1 time in total.
samardac
Posts: 212
Joined: 30 Nov 2014, 13:36

Re: Function Reference

10 Jul 2015, 02:15

Thanks trismarck, exhaustive answer, must be added to help file.
User avatar
trismarck
Posts: 506
Joined: 30 Sep 2013, 01:48
Location: Poland

Re: Function Reference

11 Jul 2015, 08:08

Actually, after rethinking:
trismarck wrote:
  • type information - the function object vs a string, which (the string) could be a function name or not
In v1.1, type() will just return "Object" and objects can be of multiple types, so this is somewhat not accurate. But I guess in general, having a separate object of given type _is_ beneficial.
trismarck wrote:
  • speed - calling the function by reference vs searching for the function name in the list of all functions every time
It's not actually speed, but rather just the caching ability. I.e. if one would like to dynamically construct the name of the function before calling the function, one could do one of the following:
  • abc%def%() or
  • f := Func("abc" def), f.Call()
. If abc%def%() is just going to be called one time in the entire script or if the script needs to dynamically construct the name of the function every time, then writing abc%def%() (vs f := Func("abc" def), f.Call()) is just more convenient and intuitive and faster (I think).
trismarck wrote:The same thing is true for Coco's example, so I guess I'm just misinterpreting it. Func() can be used to call either Ord or Asc, but doesn't have to be - %Ord%() could just as well call the fn by name.
The misinterpretation is about: although %Ord% can call the function by name or by reference (vs has to call fn by reference), one _has to_ use a function reference (vs the fn name) to _cache_ the function reference in the static variable, so that a call to _Ord is faster. And that was Coco's point - to cache, one _has to_ use fn reference.
trismarck wrote:By looking at the 'by behaviour' table, we can see that calling the function by name is discouraged - the default base object triggers in case of f.() and f.Call() (so that we can catch that i.e. by using the default base object for debugging purposes) and in case of f.Call(), the function isn't called even if it exists.
Ok, so calling a function by name _isn't_ always discouraged. Even though, for a function name, the default base object triggers for f.() and for f.Call(), it _doesn't_ trigger for %f%(). And actually, %f%() is just a special case of abc%def%(), which, as described above in this post, is really a shortcut for writing: f := Func("abc" def), f.Call(). %f%() (vs abc%def%()) just this feature so that it can call the function _by reference_ if f happens to contain a fn reference.

So _every_ function call operator is useful, it all just depends on what the user wants to do.
---------------------
For completeness: f.() is equivalent to f[""]().
trismarck wrote:
  • function exists and by name
    • %f%() - function called
    • f.() - default base object called before, function called after
    • f.Call() - default base object called 'before', function not called after
For completeness, this should be differentiated by the second parameter passed to the default base object too ("", "", "Call") (which I forgot to write), but lets now say it was omitted for clarity.

As for adding this to the help file - perhaps it could be placed as sort of a 'second line' documentation where one just interprets what the documentation is saying in particular contexts (and thus not being explicit and making mistakes). But then, that second line documentation would have to be something more than just a forum post (~a consistent part of a bigger picture), which this post isn't. Thus it's just 'there' on the forum...
lexikos
Posts: 7651
Joined: 30 Sep 2013, 04:07
GitHub: Lexikos

Re: Function Reference

11 Jul 2015, 19:44

trismarck wrote:Even though, for a function name, the default base object triggers for f.() and for f.Call(), it _doesn't_ trigger for %f%().
Yes it does.

Code: Select all

"".base.__Call := "__Call"
__Call(n) {
    MsgBox "%n%" is not a function.
}
f := "function_name"
%f%()
So _every_ function call operator is useful, it all just depends on what the user wants to do.
No; f.() is more limited than %f%() and provides no benefit other than the very subjective benefit of being more readable.

Method calls, and therefore f.() or f[""](), do not support named parameters in variadic calls:

Code: Select all

f := "fn"
f.({b:1}*)   ; 0,0
%f%({b:2}*)  ; 0,2
fn(a:=0,b:=0) {
    MsgBox %a%,%b%
}
Currently, f.() is illegal in v2-alpha.

f[]() is also valid for objects. For COM objects it indicates that the default method (DISPID_VALUE) should be invoked. For instance, if f is a JScript Function object, f[]() calls it whereas f.() tries to invoke a method called "", which doesn't exist. JScript Functions have a "call" method, but the first parameter is actually the context this, even if the function isn't being used like a method.

f[]() invokes __Call with the "method name" parameter omitted:

Code: Select all

class MyFn {
    __Call(m:="omitted", p:="") {
        MsgBox "%m%", %p%
    }
}
fn := new MyFn
fn[](42)

"".base.__Call := "__Call"
__Call(p1:="", p2:="omitted", p3:="") {
    ListVars
    MsgBox
}
fn := "Foo"
fn[](42)
If you define the parameter (m/p2) without a default value, f[](42) won't call the meta-function (and in v2, an exception will be thrown because you've omitted a required parameter).

Return to “Ask For Help”

Who is online

Users browsing this forum: Bing [Bot], blad4, Google [Bot] and 37 guests