Lua: When can I use the colon syntax?

So far I understand the basic difference between . and : I did not fully understand when Lua allows the use of colon syntax. For example, something like this works:

 s = "test" -- type(s) is string. -- so I can write a colon function for that type function string:myFunc() return #self end -- and colon function calls are possible s:myFunc() 

However, the same pattern does not work for other types. For example, when I have table instead of string :

 t = {} -- type(t) is table. -- so I can write a colon function for that type function table:myFunc() return #self end -- Surprisingly, a colon function call is not not possible! t:myFunc() -- error: attempt to call method 'myFunc' (a nil value) -- But the verbose dot call works table.myFunc(t) 

Switching to another type:

 x = 1 -- type(x) is number. -- So I was expecting that I can write a colon function -- for that type as well. However, in this case even this -- fails: function number:myFunc() return self end -- error: attempt to index global 'number' (a nil value) 

I'm currently trying to figure this out. Is it right to conclude that

  • certain types, such as string , allow you to define both colon-functions and colon-function-calls.
  • other types, such as table , allow only colon function definitions, but not colon-function-calls.
  • other types, such as number , do not allow.

What exactly is the reason for these differences? Is there a list of all types showing what type of colon syntax it supports? Maybe a workaround for the case of number , allowing you to write, for example. x:abs() ?

+3
source share
3 answers

The first example in string works because all rows have the same meta tag and are stored in a table called string .

From string :

The string library provides all its functions inside the string table. It also sets the meta for rows, where the __index field points to the string table. Therefore, you can use string functions in an object-oriented style. For example, string.byte(s,i) can be written as s:byte(i) .


The second example in table does not work, because each table has its own meta-furniture, a table called table is just a collection of all the functions of the table library.


Types like numbers do not support the default meta-furniture.

+5
source

As a complete Lua newbie, it took me a while to figure out @Yu Hao's answer, so I will try to add some details for other newbies. Please correct me if something is wrong.

As far as I can see, a call like x:someFunc() works if [*]:

  • x has metal
  • and metatable has an __index field
  • which points to a table containing someFunc function.

As Yu Hao pointed out, strings automatically get a meta, pointing to a string table, for example:

 th> s = 'test' th> getmetatable(s) { __mod : function: 0x40c3cd30 __index : { upper : function: builtin#82 rep : function: builtin#79 split : function: 0x40ffe888 gfind : function: builtin#87 find : function: builtin#84 reverse : function: builtin#80 lower : function: builtin#81 len : function: 0x40af0b30 tosymbol : function: 0x40ffe8a8 myFunc : function: 0x41d82be0 -- note: this comes from our custom function string:myFunc() dump : function: builtin#83 byte : function: builtin#76 char : function: builtin#77 gmatch : function: builtin#87 match : function: builtin#85 sub : function: builtin#78 gsub : function: builtin#88 format : function: builtin#89 } } 

So, in this case s:myFunc() works automatically. To use the colon syntax for table , we can manually set its metatable:

 th> function enableColonForTable(t) ..> meta = {__index = table} ..> setmetatable(t, meta) ..> end th> t = {} th> enableColonForTable(t) th> t:insert(1) -- works now! 

Another note is that it doesn't really matter if __index to a table with exactly the same name as the type. Instead of meta = {__index = table} we could also do:

 th> arbitraryScope = {} th> function arbitraryScope:test() return "something" end th> t = {} th> setmetatable(t, {__index = arbitraryScope}) {} th> t:test() something 

This is also the key difference from the number case. Although there are existing tables named string and table , there is no existing table named number . That is why even the definition, for example, function number:abs() failed. But we can still make it work:

 th> number = {} th> function number:abs() return math.abs(self) end th> x = -123 th> debug.setmetatable(x, {__index = number}) -123 th> x:abs() 123 

Note that we had to use debug.setmetatable instead of setmetatable here. The difference between the two seems to be that setmetatable sets the metatable only for this instance, and debug.setmetatable sets the metatable for the whole type. Obviously, setting an individual metatet for numbers is prohibited (and does not make any sense in any case). This means that (unlike tables), new numbers now have a default meta-furniture, so this works:

 th> y = -42 th> y:abs() 42 

[*] Update

As Tom Blodget noted, x:someFunc() also works if x itself serves as a namespace, i.e. This is a table with the someFunc method field. For example, you can do table:insert(1) . But now the namespace (a table called table ) is passed as self , and you would add data to the namespace:

 th> print(getmetatable(table)) -- note: "table" does not have a metatable nil th> table:insert(1) -- yet a colon syntax call works th> table { prune : function: 0x4156bde0 getn : function: 0x41eb0720 maxn : function: builtin#90 remove : function: 0x41eb08c8 foreachi : function: 0x41eb05b8 sort : function: builtin#93 concat : function: builtin#92 unpack : function: builtin#16 splice : function: 0x4156bdc0 foreach : function: 0x41eb0688 1 : 1 pack : function: builtin#94 insert : function: builtin#91 } 
+2
source

Additional answer:

First, note that a function is a value (aka "first-class citizen" ).

: is one of three indexing operators in Lua. The index operator returns the value of the "field" from an object that can be indexed, whether it be a function or any other type.

Indexing operators in order of generality:

  • expression [ expression2 ]
  • expression . identifier
  • expression : identifier ( parameter list )

The last two are simply “syntactic sugar” and can be rewritten as either of them.

You would use the second if the expression2 would always be the same string as the valid Lua identifier, and you want to hard code it.

You would use the third if the value returned by indexing "identifier" into "expression" will always be the function you want to call, with the value returned by "expression" as the implicit first parameter. Such a function call is called a "method call".

Also, note that the language / compiler does not care / does not know whether the field value is a function or not (you will receive an error at runtime if you try to call a value that is not a function). It also does not care / does not know whether the function is a method or not (the function probably will not behave as you planned if you do not pass the appropriate parameters to it).

Now the value type of the expression should be any type that can be indexed. Note that expressions do not have compile-time types, so if the value of an expression has a type that cannot be indexed, this is also a run-time error. Indexable types: table and any object with __index __index . Other answers contain detailed information about this.

+2
source

Source: https://habr.com/ru/post/1415091/


All Articles