Lua: colon notation, 'self' and function definition vs. call
Asked Answered
L

1

12

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:

  1. If a function is defined using colon notation, it gets an invisible 'self' parameter inserted as first parameter
  2. 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)
  3. 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?

Ladawnladd answered 18/8, 2015 at 19:23 Comment(1)
Possible duplicate of Difference between . and : in LuaWashin
W
10

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).

Woodprint answered 18/8, 2015 at 19:32 Comment(6)
What's the best way to determine, from within the called function, whether it was called using dot or colon notation? Edit: In particular, if called with dot notation by accident, the 'self' will swallow the next parameter (so in foo:bar example, self param would swallow param1 if I called foo.bar("test"), and param1 would be nil).Ladawnladd
You cannot tell in the function how you were called. They aren't different. This is all sugar. You can tell how many arguments you have and what "types" they are though.Woodprint
@Etan Well one possible solution is to check how many arguments were passed. 1 extra? Colon syntax.Epicalyx
@Epicalyx Unless the caller just happened to pass you the output from a function that they know produces the output you need and then output you don't care about figuring you'll just ignore the extra input and/or lua will drop it, etc. (i.e. table.remove(tab, index_from_value(tab, value)) where index_from_value returns ind, val for convenience). This is why I used to routinely tell people writing C modules not to assert on the value of lua_gettop at the start of their functions. But yes, if you don't care about that you can "guess" that way (and I hinted at that with "how many").Woodprint
@Etan I've done this with wrappers actually, when wrapping a piece of userdata I'd have to determine if they used dot or colon syntax. I had to check that way :PEpicalyx
@Epicalyx Right. You can approximate it (especially if your argument type values help) but it is a heuristic at best.Woodprint

© 2022 - 2024 — McMap. All rights reserved.