I'd like to accomplish one thing with metatablitz, but i don't understand how to do it right. More precisely, i implemented it, but i miss one little thing - so that in the right place i can use a colon.
The code below is simple and speaks for itself. How do i make sure that Z:Entity.Create() does not cause an error?
Z = {
Entity = {
Create = function(name)
print ('Entity name:', name)
end,
}
}
setmetatable(Z.Entity, {__call = function(self, ...)
print (...)
end})
local p1 = Z:Entity('New entity') -- table: 0xb8f730 New entity
p1 = Z.Entity.Create('test') -- Entity name: test
p1 = Z:Entity.Create('test') -- lua: [string "<eval>"]:16: function arguments expected near '.'
Any advice or help would be welcome!
a:name() is syntactic sugar for a.name(a)
So if you write Z:Entity.Create('test'), the interpreter expects Entity to be a function value. But instead of the expected function arguments in parenthesis there is a dot.
Your code doesn't make too much sense though. Why do you assign three different things to p1?
Your code isn't really self explanatory, you need to explain what you are trying to do and why you want to use a colon. I recommend reading up on the syntax, maybe with this question. The colon basically is a shortcut for calling a function on an object and passing the object as the first parameter. When defining a function using the colon syntax it lets you skip declaring the 'self' parameter and declares one behind the scenes.
Let's look at your three sample statements:
First statement:
local p1 = Z:Entity('New entity')
That is equivalent to this:
local p1 = Z.Entity(Z, 'New entity')
Since you set a metatable for Z.Entity with __call, you can use call the Entity table as a function. Since you use the colon syntax, it passes Z as the first parameter when calling the function so you are really passing two parameters. The way it is declared then, self will be Z.Entity and ... will expand to the two arguments, Z and 'New entity'.
Second statement:
p1 = Z.Entity.Create('test')
Here you are simply calling the function 'Create' and passing it the string 'test' prints out Entity name: test. You are not using colon syntax and the metatable __call function isn't being executed because you are not calling Entity like a function.
Third statement:
p1 = Z:Entity.Create('test')
-- lua: [string "<eval>"]:16: function arguments expected near '.'
When you write Z:Entity, it expects you to be calling it as a function and it will pass Z as the first argument to that function. It doesn't make any sense to have a period following Entity.
Possible assumption
From the name Entity.Create, it sounds like you want to create a new object with this function and return it. This is where you would normally return a new table and call setmetatable and specify __index which will look for the the passed object to find functions that can be called. You should read up on object oriented programming in LUA, but here's a quick example of what I think you're trying to do:
Z = {
Entity = {
SayHello = function(self)
print(self.name .. " says 'Hello'!")
end,
Create = function(name)
local obj = {}
obj.name = name
setmetatable(obj, {__index = Z.Entity})
-- now you can call obj:SayHello()
return obj
end
}
}
local p1 = Z.Entity.Create('Player 1')
-- here p1 is passed as the first argument to SayHello because
-- a colon is used
p1:SayHello() -- Player 1 says 'Hello'!
Related
Continuing to learn Lua.
I have wrote a function that removes the first sentence from each line and returns the result as a table of modified lines, where the first sentence was removed. Strangely, table.insert behaves weird in such function.
function mypackage.remove_first(table_of_lines)
local lns = table_of_lines
local new_lns = {}
for i=1,#lns do
table.insert(new_lns,string.gsub(lns[i],"^[^.]+. ","",1))
end
return new_lns
end
Unexpectedly, this gave me the following error.
[string "function mypackage.remove_first(table_of_lines)..."]:5: bad argument #2 to 'insert' (number expected, got string)
Why is "number expected" in the first place?
From table.insert docs
Inserts element value at position pos in list, shifting up the
elements list[pos], list[pos+1], ···, list[#list]. The default value
for pos is #list+1, so that a call table.insert(t,x) inserts x at the
end of list t.
Nothing is said about type requirements for table.insert. Ok, I decided to modify the example.
function mypackage.remove_first(table_of_lines)
local lns = table_of_lines
local new_lns = {}
for i=1,#lns do
local nofirst = string.gsub(lns[i],"^[^.]+. ","",1)
table.insert(new_lns,nofirst)
end
return new_lns
end
And now everything works. Can you explain what is going on here?
The problem is a bit complicated. It's a collision of three factors:
string.gsub returns two parameters; the second parameter is the number of matches.
table.insert can take 3 parameters. When it is given 3 parameters, the second parameter is expected to be an integer offset defining where to insert the object.
When you do this: func1(func2()), all of the return values of func2 are passed to func1, so long as you don't pass arguments after func2 in func1's argument list. So func1(func2(), something_else) will get only 2 arguments.
Therefore, when you do table.insert(ins, string.gsub(...)), this will invoke the 3-argument version, which expects the second argument to be the index to insert the object into. Hence the problem.
If you want to ensure discarding, then you can wrap the expression in parenthesis:
table.insert(new_lns, (string.gsub(lns[i], "^[^.]+. ", "", 1)))
I want to know how to get the table hex id. I know that doing:
local some_var = {}
print (some_var)
the result is (for instance):
table: 0x21581c0
I want the hex without the table: string. I know that maybe some of you suggest me to make a regular expression (or something similar) to remove those chars, but I want to avoid that, and just get the 0x21581c0
Thanks
This is simpler and works for all types that are associated with pointers:
local function getId(t)
return string.format("%p", t)
end
print("string:", getId("hi"))
print("table:", getId({}))
print("userdata:", getId(io.stdin))
print("function:", getId(print))
print("number:", getId(1))
print("boolean:", getId(false))
print("nil:", getId(nil))
Result:
string: 0x0109f04638
table: 0x0109f0a270
userdata: 0x01098076c8
function: 0x0109806018
number: NULL
boolean: NULL
nil: NULL
In the standard implementation, there is the global 'print' variable that refers to a standard function that calls, through the global variable 'tostring', a standard function described here. The stanard 'tostring' function is the only way to retrieve the hexadecimal number it shows for a table.
Unfortunately, there is no configuration for either of the functions to do anything differently for all tables.
Nonetheless, there are several points for modification. You can create you own function and call that every time instead, or point either of the the global variables print or tostring to you own functions. Or, set a __tostring metamethod on each table you need tostring to return a different answer for. The advantage to this is it gets you the format you want with only one setup step. The disadvantage is that you have to set up each table.
local function simplifyTableToString(t)
local answer = tostring(t):gsub("table: ", "", 1)
local mt = getmetatable(t)
if not mt then
mt = {}
setmetatable(t, mt)
end
mt.__tostring = function() return answer end
end
local a = {}
local b = {}
print(a, b)
simplifyTableToString(a)
print(a, b)
Without complex patterns, you can just search for the first space, and grab the substring of what follows.
function get_mem_addr (object)
local str = tostring(object)
return str:sub(str:find(' ') + 1)
end
print(get_mem_addr({})) -- 0x109638
print(get_mem_addr(function () end)) -- 0x108cf8
This function will work with tables and functions, but expect errors if you pass it anything else.
Or you can use a little type checking:
function get_mem_addr (o)
return tostring(o):sub(type(o):len() + 3)
end
The table id stated by the OP is invalid in the version of Lua I am using (5.1 in Roblox). A valid ID is length 8, not 9 as in your example. Either way, just use string.sub to get the sub-string you are after.
string.sub(tostring({}), 8)
The reason is, 'table: ' is 7 characters long, so we take from index 8 through the end of the string which returns the hex value.
While I understand the basic difference between . and :, I haven't fully figured out when Lua allows to use the colon syntax. For instance, something like this does work:
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 seem to work for other types. For instance, when I have a table instead of a 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)
Moving on 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 make sense of this. Is it correct to conclude that
certain types like string allow both colon-function-definitions and colon-function-calls.
other types like table allow only colon-function-definitions but not colon-function-calls.
yet other types like number allow neither.
What exactly is the reason for these differences? Is there a list of all types, showing which type of colon-syntax they support? Is there maybe a workaround for the number case, allowing to write e.g. x:abs()?
The first example on string works because, all strings share the same metatable, and it's stored in the table named string.
From string:
The string library provides all its functions inside the table string. It also sets a metatable for strings where the __index field points to the string table. Therefore, you can use the string functions in object-oriented style. For instance, string.byte(s,i) can be written as s:byte(i).
The second example on table doesn't work because, every table has its own metatable, the table named table is just a collection of all the functions of table library.
Types like numbers don't support metatable by default.
As a total Lua newbie, it took me a while to understand #Yu Hao's answer, so I'll try to add some details for other beginners. Please correct me if anything is wrong.
As far as I can see, a call like x:someFunc() works if [*]:
x has a metatable
and the metatable has a field __index
which points to a table containing a function someFunc.
As Yu Hao has pointed out, strings automatically get a metatable pointing to the table string, e.g.:
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. In order to use the colon syntax for a 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 observation is that it actually does not matter whether __index points to a table with exactly the same name as the type. Instead of meta = {__index = table}, we also could do:
th> arbitraryScope = {}
th> function arbitraryScope:test() return "something" end
th> t = {}
th> setmetatable(t, {__index = arbitraryScope})
{}
th> t:test()
something
That is also the key difference to the case of a number. While there are existing tables called string and table, there is no existing table called number. This is why even defining e.g. function number:abs() has failed before. 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 the given instance, while debug.setmetatable sets the metatable for the whole type. Apparently setting an individual metatable for numbers is forbidden (and would not make much sense anyways). This means that (in contrast to tables) newly constructed numbers now have the given metatable by default, so this works:
th> y = -42
th> y:abs()
42
[*] Update
As pointed out by Tom Blodget, x:someFunc() also works if x itself serves as a namespace, i.e., it is a table with a method field someFunc. For instance you could do table:insert(1). But now the namespace (the table called table) is passed as self and you would have added 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
}
Supplementary answer:
First, note that a function is a value (aka "first-class citizen").
: is one of three indexing operators in Lua. An indexing operator returns the value of a "field" from an object that can be indexed—be it a function or any other type.
The indexing operators are, in order of generality:
expression [ expression2 ]
expression . identifier
expression : identifier ( parameter-list )
The latter two are just "syntactic sugar" and can be rewritten in the form of any above it.
You would use the second if "expression2" would always the same string that is a valid Lua identifier and you want to hardcode it.
You would use the third if the value returned by indexing "identifier" against "expression" will always be a function that you want to call with the value returned by "expression" as an implicit first parameter. Such a function call is called a "method call."
Also, note that the language/compiler doesn't care/know if the field value is a function or not (you'd get an error at runtime if you try to call a value that isn't a function). Nor does it care/know if the function is a method or not (the function probably won't behave as you intended if you don't pass it appropriate parameters).
Now, the type of the expression value must be any type that can be indexed. Note that expressions don't have compile-time types so if the expression value is of a type that cannot be indexed, that is a runtime error, too. Indexable types are: table and any object with an __index metamethod. Other answers provide details on these.
While learning Lua, I borrowed some code from here
to use string indexing, which is exactly this:
getmetatable("").__index = function(str, i) return string.sub(str, i, i) end
After that, I wrote a function to reverse a string as practice.
function reverse_string(str)
local s = ""
for i = string.len(str), 1, -1 do s = s .. str[i] end
return s
end
That works fine, until I change the string.len(str) to str:len(), then I get this error:
reverse.lua:9: bad argument #2 to 'sub' (number expected, got string)
Debugging print()'s tell me that the __index function is being called on str:len(), and that the i argument is becoming the string "len". I know that str:len() works without the metatable there, but as soon as I add it this happens, why?
From Lua 5.2 Refernce Manual:String Manipulation
The string library provides all its functions inside the table string. It also sets a metatable for strings where the __index field points to the string table. Therefore, you can use the string functions in object-oriented style. For instance, string.byte(s,i) can be written as s:byte(i).
So, the object oriented style like str:len() comes from the default metamethod __index, which you have modified.
The index function is passed the table, and the key that is being indexed. so 'str' should be the string and 'i' should be key in your case. Because "len" is not in the meta table it calls __index and passes the string as the first argument and the key ("len") as the second argument. it looks as though you need to check to the type of 'i' to see what to do for better handling of strings
getmetatable("").__index = function(str, key)
if type(key) == "string" then
return string[key]
else
return string.sub(str, key, key)
end
end
str = "hello, world!"
print(str:len())
print(str[5])
see here for more info
I'm just getting started with Lua. In the example I'm learning from (the Ghosts & Monsters Corona open source), I see this pattern repeatedly.
local director = require("director")
local mainGroup = display.newGroup()
local function main()
mainGroup:insert(director.directorView)
openfeint = require ("openfeint")
openfeint.init( "App Key Here", "App Secret Here", "Ghosts vs. Monsters", "App ID Here" )
director:changeScene( "loadmainmenu" )
return true
end
main()
Is this some sort of convention experienced Lua programmers recommend or are there genuine advantages to doing it this way? Why wouldn't you just skip the function all together and do this:
local director = require("director")
local mainGroup = display.newGroup()
mainGroup:insert(director.directorView)
local openfeint = require ("openfeint")
openfeint.init( "App Key Here", "App Secret Here", "Ghosts vs. Monsters", "App ID Here" )
director:changeScene( "loadmainmenu" )
Is there some implicit benefit to the first style over the second? Thanks!
Is this some sort of convention experienced Lua programmers recommend or are there genuine advantages to doing it this way?
It's not typical. The advantage is that object state is private, but that's not enough of an advantage to recommend it.
I see this pattern repeatedly.
I've never seen it before, and it happens only once in the source you posted.
EDIT: Adding a response to a question asked in the comments below this post.
A function which accesses external local variables binds to those variables and is called a 'closure'. Lua (for historical reasons) refers to those bound variables as 'upvalues'. For example:
local function counter()
local i = 1
return function()
print(i)
i = i + 1
end
end
local a, b = counter(), counter()
a() a() a() b() --> 1 2 3 1
a and b are closures bound to different copies of i, as you can see from the output. In other words, you can think of a closure as function with it's own private state. You can use this to simulate objects:
function Point(x,y)
local p = {}
function p.getX() -- syntax sugar for p.getX = function()
return x
end
function p.setX(x_)
x = x_
end
-- for brevity, not implementing a setter/getter for y
return p
end
p1 = Point(10,20)
p1.setX(50)
print(p1.getX())
Point returns a table of closures, each bound to the locals x and y. The table doesn't contain the point's state, the closures themselves do, via their upvalues. An important point is that each time Point is called it creates new closures, which is not very efficient if you have large quantities of objects.
Another way of creating classes in Lua is to create functions that take a table as the first argument, with state being stored in the table:
function Point(x,y)
local p = {x=x,y=y}
function p:getX() -- syntax sugar for p.getX = function(self)
return self.x
end
function p:setX(x)
self.x = x
end
return p
end
p1 = Point(10,20)
p1:setX(50) -- syntax sugar for p1.setX(p1, 50)
print(p1:getX()) -- syntax sugar for p1.getX(p1)
So far, we're still creating new copies of each method, but now that we're not relying on upvalues for state, we can fix that:
PointClass = {}
function PointClass:getX() return self.x end
function PointClass:setX(x) self.x = x end
function Point(x,y)
return {
x = x,
y = y,
getX = PointClass.getX,
setX = PointClass.getY,
}
end
Now the methods are created once, and all Point instances share the same closures. An even better way of doing this is to use Lua's metaprogramming facility to make new Point instances automatically look in PointClass for methods not found in the instance itself:
PointClass = {}
PointClass.__index = PointClass -- metamethod
function PointClass:getX() return self.x end
function PointClass:setX(x) self.x = x end
function Point(x,y)
return setmetatable({x=x,y=y}, PointClass)
end
p1 = Point(10,20)
-- the p1 table does not itself contain a setX member, but p1 has a metatable, so
-- when an indexing operation fails, Lua will look in the metatable for an __index
-- metamethod. If that metamethod is a table, Lua will look for getX in that table,
-- resolving p1.setX to PointClass.setX.
p1:setX(50)
This is a more idiomatic way of creating classes in Lua. It's more memory efficient and more flexible (in particular, it makes it easy to implement inheritance).
I frequently write my own Lua scripts this way because it improves readability in this case:
function main()
helper1( helper2( arg[1] ) )
helper3()
end
function helper1( foo )
print( foo )
end
function helper2( bar )
return bar*bar
end
function helper3()
print( 'hello world!' )
end
main()
This way the "main" code is at the top, but I can still define the necessary global functions before it executes.
A simple trick, really. I can't think of any reason to do this besides readability.
The first style could be used too improove readability, but I would rather give the function some meaningful name instead of main or just go without the function.
By the way, I think it's always a good practice to name blocks of code, i.e. put them into functions or methods. It helps explain your intend with that piece of code, and encourages reuse.
I don't see much point to the first style as you've shown it. But if it said something like if arg then main() end at the bottom, the script might (just might) be useful as a loadable "library" in addition to being a standalone script. That said, having a main() like that smacks of C, not Lua; I think you're right to question it.