Lua: colon notation, 'self' and function definition vs. call - function

I'm getting terribly confused by the colon notation used when defining/calling Lua functions.
I thought I'd got my head round it until I saw this piece of code:
function string.PatternSafe( str )
return ( str:gsub( ".", pattern_escape_replacements ) );
end
function string.Trim( s, char )
if char then char = char:PatternSafe() else char = "%s" end
return string.match( s, "^" .. char .. "*(.-)" .. char .. "*$" ) or s
end
What's confusing me here is that string.PatternSafe() doesn't reference 'self' anywhere, yet the code seems to work.
I've also seen some scripts that use colon notation when defining the function, for example:
function foo:bar( param1 ) ... end
After several hours of googling I've still not managed to work out what precisely is happening in these two contexts. My current assumptions are as follows:
If a function is defined using colon notation, it gets an invisible 'self' parameter inserted as first parameter
If a function is called using colon notation, the object preceding ':' is inserted in to the arguments (so becomes the first parameter of the function)
If a function is called using dot notation, then even if it was defined using colon notation it will not get the object inserted as first argument/parameter
If my assumptions are correct, that raises an additional question: What is the best way to ensure that the function was called properly?

Your assumptions are all correct.
Assumption 1 from the manual:
The colon syntax is used for defining methods, that is, functions
that have an implicit extra parameter self. Thus, the statement
function t.a.b.c:f (params) body end
is syntactic sugar for
t.a.b.c.f = function (self, params) body end
Assumption 2 from the manual:
A call v:name(args) is syntactic sugar for v.name(v,args), except that v is evaluated only once.
Assumption 3 doesn't have a direct manual section since that's just normal function call syntax.
Here's the thing though. self is just the auto-magic name given in the syntax sugar used as part of the colon assignment. It isn't a necessary name. The first argument is the first argument whatever the name happens to be.
So in your example:
function string.PatternSafe( str )
return ( str:gsub( ".", pattern_escape_replacements ) );
end
the first argument is str so when the function is called as char:PatternSafe() is de-sugars (via assumption 2) to char.PatternSafe(char) which is just passing char to the function as the first argument (which, as I already said, is str).

Related

Understanding the difference between object.function(argument) and object:function(argument) in Lua

obj ={x=30}
function obj:printpos()
print(self.x)
end
other = {x=15}
obj.printpos(other)
The obj.printpos(other) gives expected output viz. 15. However the call obj:printpos(other) doesn't give expected output.It still prints 30. Why the call obj:printpos(other) is not taking other as its argument? Basically what is the difference between object.function(argument) and object:function(argument)? Is object:function(argument) same as object:function() i.e. whether argument is ignored?
obj:printpos(other) is equivalent to obj.printpos(obj, other).
function obj:printpos() end is equivalent to function obj.printpos(self) end.
From Lua 5.4 Reference Manual - §3.4.11 – Function Definitions (formatting mine):
The colon syntax is used to emulate methods, adding an implicit extra parameter self to the function. Thus, the statement
function t.a.b.c:f (params) body end
is syntactic sugar for
t.a.b.c.f = function (self, params) body end
From this, we can see the colon syntax implicitly adds the self parameter to the function scope.
Inversely, calling a function defined with the colon syntax using the dot syntax will cause the first argument passed to it to be assigned to the self parameter.
Thus, with
local thing = {}
function thing:func()
print(self)
end
the calls
thing:func()
-- and
thing.func(thing)
have the same result of assigning thing to self, and
thing.func(other_thing)
will assign other_thing to self.
The problem with
thing:func(other_thing)
is that, as previously seen, thing is assigned to self. other_thing is assigned to no parameter, as no other parameters were defined.

What's the difference these two function calling conventions?

Functions can be called in a couple ways:
say(1, 2, 3) # 123
say: 1, 2, 3 # (1, 2, 3)
The latter seems to pass a Positional, but apart from that I don't know how else they differ. Are there any differences that are important to know? What types of situations would you use one over the other?
#jjmerelo's answer covers the basics. This complementary answer, which aims at being somewhat exhaustive but hopefully not exhausting, covers traps, rare cases, and advice.
foo: valuea, valueb, ...
Surprisingly perhaps, this is not a call of a sub or method called foo.
Instead it's a statement that begins with a label, foo:.
The say: line in your question won't work in an ordinary program:
say: <a b c>; # Useless use of constant value a b c ...
The "Useless use" warning means the <a b c> doesn't get used in a useful way. The say: isn't doing anything with the list of values. It's just a label that doesn't do anything.
Presumably you are using something like the Perl 6 REPL. The REPL automatically says the last value in a line if it isn't otherwise used, thus making the line appear to work without a warning.
.a-method:
If a postfix method call using the form .a-method has no arguments other than the invocant (the argument to the left of the ., or the current topic if there isn't an explicit invocant) then you can just write it in the form:
42.say ;
You can optionally append a colon:
42.say: ;
There's no good reason to, but it's consistent with:
.a-method: arg2, arg3, ...
If you want to supply one or more arguments (other than the invocant) to a postfix .a-method call, then you have to pick one of two ways to introduce them.
One way is to write a colon immediately after the method name, before the argument(s). There must be no space between the method name and colon, and there must be space after the colon before the method argument(s).1
For example, the following uses a colon before the Numeric argument in the following method call:
say <abc 2 def ghi> .first: Numeric ; # 2
In the above line the method call expression (.first: Numeric) ends at the statement terminator (;). If there's an enclosing sub-expression such as an array subscript then the method call expression ends at the end of that sub-expression:
say .[1 + .first: Numeric] given <abc 2 def ghi> ; # ghi
The argument list of a colon form method call is also brought to a close by a valid statement modifier like given:
say .first: Numeric given <abc 2 def ghi> ; # 2
a-sub arg1, arg2, ...
This is the corresponding form for subroutine calls. The only format differences are that the sub has no invocant or . before the sub name and you must omit the colon after the sub name.
.a-method( arg2, arg3, ... )
a-sub( arg1, arg2, ... )
The other common form used for both method and sub calls is to immediately follow the method or sub name with parens to delimit arguments. The opening paren must immediately follow, without any space between the routine name and (.
Here's parens used with the .first method:
say 1 + .first(Numeric) given <abc 2 def ghi> ; # 3
This has the advantage that it's arguably prettier than the alternative of using outer parens:
say 1 + (.first: Numeric) given <abc 2 def ghi> ; # 3
If you want to put a sub call directly inside a double quoted string, you need to prefix the sub name with an & sigil and use the postfix parens form:
my #array = <abc 2 def ghi> ;
say "first number is &first(Numeric,#array)" ; # first number is 2
To put in a method call, you again have to use the postfix parens form, and you must also provide an explicit invocant (you can't just write "Some text .a-method()"):
my #array = <abc 2 def ghi> ;
say "first number is #array.first(Numeric)" ; # first number is 2
If there are no arguments (other than the invocant for a method call) you still need to use this form with empty parens if you want to interpolate a sub or method call in a string:
my #array = <abc 2 def ghi> ;
say "no method call #array[3].uc" ; # no method call ghi.uc
say "with method call #array[3].uc()" ; # with method call GHI
say "&rand"; # &rand
say "&rand()"; # 0.929123203371282
.a-method ( arrgh, arrgh, ... ) ;
This won't work.
Because the .a-method isn't followed by a colon, the method call is considered complete.
That means the next thing must be either an expression/statement ender like ;, or a postfix operator that will operate on the result of the method call, or an infix operator that will operate on the result and some following argument.
But ( arrgh, arrgh, ... ) is none of these. So you get a "Two terms in a row" compilation error.
.a-method:( arrgh, arrgh, ... ) ;
.a-method: ( arrgh, arrgh, ... ) ;
In general, DO NOT MIX use of a : with use of parens around arguments as part of a method call. There is no good reason to do so because it will either not work, or work only by accident, or work but very likely confuse readers.
Doing so without a space between the colon and opening paren yields a cryptic compilation error:
This type (QAST::WVal) does not support positional operations
Leaving a space appears to work -- but typically only by luck:
say .first: (Numeric) given <abc 2 def ghi> ; # 2
The (Numeric) is a single value in parens which yields Numeric so this line is the same as:
say .first: Numeric given <abc 2 def ghi> ; # 2
But if there are two or more arguments in parens, things will go awry. Use one of these forms:
say .first: Numeric, :k given <abc 2 def ghi> ; # 1
say .first(Numeric, :k) given <abc 2 def ghi> ; # 1
which correctly yield the array index ("key") of the 2 element rather than:
say .first: (Numeric, :k) given <abc 2 def ghi> ; # Nil
which yields Nil because the .first method doesn't do anything useful with a single argument that's a list of the form (Numeric, :k).
Of course, you may occasionally want to pass a single argument that's a list of values in parens. But you can do so without using a colon. For the sake of clarity, it's my advice that you instead write this as:
invocant.a-method(( valuea, valueb, ... ));
a-sub ( arrgh1, arrgh2, ... ) ;
As just explained for method calls, this passes ONE argument to a-sub, namely the single list ( arrgh1, arrgh2, ... ) which will seldom be what the writer means.
Again, my advice is to instead write this as:
`a-sub( valuea, valueb, ... ) ;`
or:
`a-sub valuea, valueb, ... ;`
if you mean to pass multiple arguments, or if you wish to pass a list as a single argument, then:
`a-sub(( valuea, valueb, ... )) ;`
.a-method : arrgha, arrghb, ...
a-sub : arrgha, arrghb, ...
For the method form this will net you a "Confused" compilation error.
The same is true for the sub form if a-sub takes no arguments. If a-sub takes arguments you'll get a "Preceding context expects a term, but found infix : instead" compilation error.
.&a-sub
There's a call form which lets you call a routine declared as a sub -- but use the .method call syntax. The following feeds the "invocant" qux on the left of the dot as the first argument to a sub called a-sub:
qux.&a-sub
Use a : or parentheses as usual to pass additional arguments to a-sub:
sub a-sub ($a, $b) { $a == $b }
say 42.&a-sub(42), 42.&a-sub(43); # TrueFalse
say 42.&a-sub: 42; # True
(In my original version of this section I wrote that one can not pass additional arguments. I had tested this and thought one could not. But I must have just gotten confused by something. #Enheh's comment led me to retest and discover that one can pass additional arguments just as with ordinary method calls. Thank you #Enheh. :))
a-method( invocant: arg2, arg3, ... )
a-method invocant: arg2, arg3, ...
Called "Indirect object notation" in the design docs, these formats are an undocumented and very rarely seen form of method call in which the call mimics the method declaration -- the method name comes first and then the invocant followed by a colon:
say first <abc 2 def ghi>: Numeric ; # 2
Note that say is a sub call because the next token, first, isn't followed by a colon. In contrast first is a method call because the token after it is followed by a colon.
Footnotes
1 All comments about spaces/spacing in this answer ignore unspacing.
As Raiph tells you above, say: is a label. So you didn't say anything (even though you thought you did) and -- outside use of the REPL -- the compiler will complain that your use of <a b c> was useless:
say: <a b c>; # OUTPUT: «WARNINGS for <tmp>:␤Useless use of constant value a b c in sink context (lines 1, 1, 1, 1, 1, 1)␤»
However, you often can use a : notation instead of parentheses in method calls. Consider the four routine calls below (two subroutine calls then two method calls):
my #numbers = (33, 77, 49, 11, 34);
say map *.is-prime, #numbers ; # simplest subroutine call syntax
say map( *.is-prime, #numbers ); # same meaning, but delimiting args
say #numbers.map( *.is-prime ) ; # similar, but using .map *method*
say #numbers.map: *.is-prime ; # same, but using : instead of parens
These sentences will all return the same (False False False True False).
In general, as you see above with map, you can use () in method calls wherever you would use :, but the opposite is not true; : can be used only in method calls.
Use () if the arguments need to be delimited precisely, as Raiph comments below.
This answer focuses on the basics. See Raiph's answer for more exhaustive coverage of the precise details of routine call syntax. (As an important example, the meaning of these calls normally changes if there's any spaces between the routine name and the colon (:) or opening parenthesis (()).

How to implement a basic Lua function in Conky?

I am trying to add a function to my Conky which prints the length of a string for debug purposes. The code, inside a file called test.lua, is pretty trivial:
function test(word)
return string.len(word)
end
...and I load it like this. In my conky.config section I have:
lua_load = '/home/xvlaze/test.lua',
lua_draw_hook_pre = 'test'
...in the conky.text section I have:
${lua test "fooo"}
...where test is the name of the function and fooo the string to test.
The expected result should be a printed 4 in Conky, but instead of that I get:
conky: llua_do_call: function conky_test execution failed: /home/xvlaze/test.lua:2: attempt to index a nil value (local 'string')
conky: llua_getstring: function conky_test didn't return a string, result discarded
I have browsed through the documentation, but I can't find anything. Does anybody know where the failure is?
Several guidances on how to implement functions in Conky:
First of all: YOU MUST USE conky_ BEFORE YOUR FUNCTION'S NAME.
Otherwise, you will get the following error when running your Conky:
attempt to call a nil value
Secondly: YOU MUST ALWAYS RETURN A VALUE.
I don't mind repeating it - it is crucial. Otherwise, you will get:
function foobar didn't return a string, result discarded
function_result
...in your terminal, and your Conky will be left empty of values related to your extra code. Nothing will be printed regarding your function.
Last but not least: YOU MUST ALWAYS CALL YOUR FUNCTION LIKE:
lua_load = '/path/to/function.lua',
-- Whatever content...
${lua function_name function_parameter1 function_parameterN} -- In case you use more than one parameter.
In summary, a dummy function template could be:
MAIN FILE (conky.conf):
conky.config = {
-- Whatever content... Lua styled comments.
lua_load = '/path/to/function.lua',
}
conky.text = [[
# Whatever content... In this section comments are started with '#'!
${lua function_name parameter}
]]
FUNCTION FILE:
function conky_function_name(parameter)
-- Whatever content... Remember this is Lua, not conky.text syntax. Always use '--' comments!
return whatever -- No return, no party. A function MUST always return something!
end

Not being able to use function on progress4gl

I've been trying to create a simple function that would accumulate some strings and then I would call it an it would return it, but for some reason I'm getting:
Could not understand line 1 (198)
Which is too vague, I've been looking in forums for examples to compare mine to, but it seems all right, May someone provide me an explanation of what I might be doing wrong?
Code:
put unformatted fcustomer(). /*line one*/
function fcustomer returns char():
define variable vgatherer as character.
define variable i as integer no-undo.
do i = 1 to 10:
assign vgatherer = vgatherer + "thing(s)".
end.
return vgatherer.
end function.
Functions need to be declared prior to use or be forward declared.
You might also want to have an input parameter.
function fcustomer returns character ( input p1 as character ) forward.
put unformatted fcustomer( "some text" ). /*line one*/
function fcustomer returns character ( input p1 as character ):
define variable vgatherer as character.
define variable i as integer no-undo.
do i = 1 to 10:
assign vgatherer = vgatherer + p1.
end.
return vgatherer.
end function.
The ABL uses a single-pass compiler, so functions have to be declared before they're used. If you change the code like so, it'll work:
function fcustomer returns char():
define variable vgatherer as character.
define variable i as integer no-undo.
do i = 1 to 10:
assign vgatherer = vgatherer + "thing(s)".
end.
return vgatherer.
end function.
put unformatted fcustomer(). /*line one*/
You can also forward-define your functions with the FORWARD phrase. Check your ABL docs for details.

Is there a way to determine which optional arguments were given in a VBA function call?

Say I have a VBA function with an optional argument. Is there a way to tell, from within that function, whether the calling code has supplied the optional argument or not?
Public Function SomeFunction(optional argument as Integer = 0) As Integer
End Function
i.e. Is there a way to tell the difference between the following calls?
x = SomeFunction()
x = SomeFunction(0)
As far as I am aware it is not possible. If no argument is passed then the argument is initiliazed to its default value (0 in this case).
One way around this is to change the variable type to Variant and use the IsMissing function to check whether an argument is passed or not. Example:
Public Function SomeFunction(Optional argument As Variant) As Integer
If IsMissing(argument) Then
argument = 0
Else
// Code if argument not = 0....
End If
End Function
The IsMissing function works only with the Variant data type as any other data type will always have a default initialization value.
No, there is not.
The same problem exists in other language, such as C#, as expressed clearly in "C# In Depth", Chapter 13.1.1 (I read this part 15mins ago, it's normal I remember this!)
What I suggest you is not to set the default value in the function declaration. In the function, if argument is null, you set it to 0 and you know it wasn't supplied in the function call.
Edit : Just like #Remnant said in his answer, to be able to do this, the parameter type needs to be a variant.