I have this code right here, in a file named "hframe.lua".
table.insert(handler.class, framename)
local func = [[
function new(())
print("Hello Stackoverflow!")
end
]] --The function test itself.
local a="table.insert(handler.class."..framename..", "..func..")" --I broke it in two to make things easier for me to read.
print(a) --Read out the variable so I can see what it looks like.
local b=load(a) --Load it up
b() --Nil error. Great.
"framename" is a constant from another file, which is simply declared as "frame". The error is a simple nil error. Upon running b(), it throws a local nil error. With an assert, I've discovered this error.
Error: Thread error (Thread: 0x04c7d2f0)
uicode/hframe.lua:16: [string "table.insert(handler.class.frame, function ne..."]:1: '(' expected near 'new'
stack traceback:
[C]: in function 'assert'
uicode/hframe.lua:16: in main chunk
[C]: in function 'require'
uicode/handlerthread.lua:165: in main chunk
stack traceback:
[string "boot.lua"]:777: in function <[string "boot.lua"]:773>
[C]: in function 'error'
[string "boot.lua"]:631: in function <[string "boot.lua"]:630>
[string "boot.lua"]:604: in function <[string "boot.lua"]:594>
[C]: in function 'xpcall'
I'm not entirely sure WHY it's throwing this error, because clearly I define it as function new(). What I have I done wrong?
function new() end
is syntactic sugar for
new = function () end
This is an assignment. It does not resolve to a value. Hence you cannot use a function definiton where a value is expected.
In your case the code executed is
table.insert(handler.class.someFrameName,
function new () print("Hello Stackoverflow!") end
)
The only thing that would make sense for Lua with the function keyword present in a list of expressions is an anonymous function and it expects a ( after function but finds new
You have two options to fix this.
If function new() end is equivalent to new = function () end, function () end must resolve to a function value. So we can create a function value in-place:
table.insert(handler.class.someFrameName,
function () print("Hello Stackoverflow!") end
)
I'll leave it up to you to impelement this in the string you want to load.
Another option is to define the function first. You can do this both in the string or in the scope of your load call.
function new() print("Hello Stackoverflow")
table.insert(handler.class.someFrameName, new)
Lua load() function returns nil and the error if it failed to compile code. Since the passed code is invalid, b is nil. Therefore a error is raised when you attempt to call it.
The reason the code is invalid is because when a function declaration is used as an expression, the function name must be omitted, e.g.
table.insert(sometable, function() … end)
Please note that most likely you don’t need to create code dynamically, the following will work:
table.insert(handler.class[framename], function() … end)
Related
I'm still ramping up in lua, and I am not quite familiar with this syntax. What is happening when you pass in a function as a parameter like below?
Comm.setRouting(function(url)
for i = 1,4 do
local portIndex = "Path"..i
if url:match(portConfig[portIndex]) ~= nil then
return Comm.slots()[1], Comm.groups()[i]
end
end
end)
The other answers are correct, but it might help you if you wrote your own function that calls another function:
function CallAFunction(func_to_call)
func_to_call()
end
You could pass a named function (an anonymous function assigned to a variable) or an anonymous function written on the fly.
function SayHello()
print("Hello!")
end
--[[
^This is equivalent to:
SayHello = function()
print("Hello!")
end
]]--
CallAFunction(SayHello)
CallAFunction(function()
print("Goodbye!")
end)
output:
Hello!
Goodbye!
and this can be done with parameters
function CallAFunction(func)
func("Bonjour")
end
CallAFunction(function(parameter)
print(parameter)
end)
Here, func is the anonymous function, which accepts 1 parameter parameter.
When you call func("Bonjour") you are passing Bonjour as parameter, like a normal function call.
Here you're passing an argument to setRouting that is an "anonymous function"
Functions are first-class values in Lua and can be stored in local variables, global variables and table fields. Here the function is being passed anonymously on the call stack to the setRouting function.
setRouting would be called a "higher-order function" because it accepts a function as its input.
The following page has some more information about functions in Lua:
https://www.lua.org/pil/6.html
A couple of things are happening in this example:
You're passing a function as a parameter. The callee (e.g. setRouting()) can invoke that function. This is often referred to as a callback function.
You're defining the function itself, on-the-fly. This is an example of an "anonymous function", or a lambda function.
I'm playing with this lua link: http://underpop.free.fr/l/lua/docs/programming-in-lua-first-edition.pdf and get confused about the rename function. I tried it out and only to get an error message as the following.
> rename{old = "temp.lua", new = "temp1.lua"}
stdin:1: attempt to call global 'rename' (a nil value)
stack traceback:
stdin:1: in main chunk
[C]: in ?
> os.rename{old = "temp.lua", new = "temp1.lua"}
stdin:1: bad argument #1 to 'rename' (string expected, got table)
stack traceback:
[C]: in function 'rename'
stdin:1: in main chunk
[C]: in ?
Moreover, I'm learning Lua because I'm reading a piece of code, which is written in Lua. I do not think the link provided above, programming in lua first edition, is a good tutorial. I've also found a reference menu, but do not quite like it also. Can anyone please provide a good tutorial based on your opinion?
EDIT: I tried it again with the following code:
> function rename(arg)
>> return os.rename(arg.old, arg.new)
>> end
>
> rename{old = "temp.lua", new = "temp1.lua"}
It works this time.
stdin:1: attempt to call global 'rename' (a nil value)
This error message tells you exactly what the problem is.
It is caused by this line:
rename{old = "temp.lua", new = "temp1.lua"}
rename is a nil value. Hence Lua does not know what to do if you call it. In order to avoid this error you have to define rename as a callable variable like.
Let me just quote the tutorial you do not find good.
rename{old="temp.lua", new="temp1.lua"}
Accordingly, we define rename with only one parameter and get the
actual arguments from this parameter:
function rename (arg)
return os.rename(arg.old, arg.new)
end
What is the syntax to create the function, but then add it's implementation further down in code?
So roughly like this:
Define function doX
Call doX (further down in the code)
doX implemention (i.e. all functions down at the bottom of the file)
You only need to have a variable to reference. local funcName is sufficient for your purposes with one caveat. This will work:
local funcName
function callIt()
print(funcName())
end
function defineIt()
funcName = function() return "My Function" end
end
defineIt()
callIt()
As long as you define it (defineIt) before you call it (callIt), it should work as expected. You can't do something like this though (and this is the caveat):
local funcName
print(funcName())
funcName = function() return "My Function" end
You will get an error: attempt to call local 'funcName' (a nil value).
oh...so there's really no way to call funcName prior to having actually defined the function then? i.e. you still need to make sure defineIt is called before your first call to funcName itself?
I wanted to clarify this point, and I felt that an answer would be the better way than a comment.
Lua is a much simpler language than C or C++. It is built on some simple foundations, with some syntactic sugar to make parts of it easier to swallow.
There is no such thing as a "function definition" in Lua. Functions are first-class objects. They are values in Lua, just like the number 28 or the string literal "foo" are values. A "function definition" simply sets a value (namely, the function) into a variable. Variables can contain any kind of value, including a function value.
All a "function call" is is taking the value from a variable and attempting to call it. If that value is a function, then the function gets called with the given parameters. If that value is not a function (or a table/userdata with a __call metamethod), then you get a runtime error.
You can no more call a function that hasn't been set in a variable yet than you can do this:
local number = nil
local addition = number + 5
number = 20
And expect addition to have 25 in it. That's not going to happen. And thus, for the same reason, you can't do this:
local func = nil
func(50)
func = function() ... end
As Paul pointed out, you can call a function from within another function you define. But you cannot execute the function that calls it until you've filled in that variable with what it needs to contain.
As others have written, you cannot call a function at runtime that has not been assigned prior to the call. You have to understand that:
function myFunc() print('Something') end
Is just a syntax sugar for this:
myFunc = function() print('Something') end
Now, it makes sense that this kind of code would not work the way you want it to:
print(greeter(io.read())) -- attempt to call global 'greeter' (a nil value)
function greeter(name) return 'Hello '..name end
When you use the greeter variable, its value is nil, because its value is set only on the next line.
But if you want to have your "main" program on the top and the functions at the bottom, there is simple way to achieve this: create a "main" function and call it as the last thing on the bottom. By the time the function is called, all the functions will be set to the corresponding global variables:
-- start of program, your main code at the top of source code
function main()
local name = readName()
local message = greeter(name)
print(message)
end
-- define the functions below main, but main is not called yet,
-- so there will be no errors
function readName() io.write('Your name? '); return io.read() end
function greeter(name) return 'Hello, ' .. name end
-- call main here, all the functions have been assigned,
-- so this will run without any problems
main()
how can one get the caller of function within a function in lua?
Specifically I'm looking (for debug purposes in the command output, e.g. print) the ability to log when a common function is called, with an indication of where it was called from.
This could be just the filename of where it was called from for example
i.e.
File 1 - Has commonly used function
File 2 - Calls of the the file one functions
PS Mud - I actually get a nil when doing this - is this normal then? No way to get more info in this case:
Called file;
SceneBase = {}
function SceneBase:new(options)
end
return SceneBase
Calling file:
require("views.scenes.scene_base")
local scene = SceneBase.new()
debug.getinfo(2).name will give you the name of the calling function if it has one and it is a string. If it's an anonymous function, you'll get nil, if it's stored in a table using something other than a string key, you'll get ?.
function foo() print(debug.getinfo(2).name) end
-- _G["foo"] => function name is a string
function bar() foo() end
bar() --> 'bar'
-- _G[123] => function name is a number
_G[123] = function() foo() end
_G[123]() --> '?'
-- function has no name
(function() foo() end)() --> 'nil'
The Lua debug library lets you get the stack trace.
The code says it all:
#tryModA.lua:
f,err=loadfile("tryModB.lua")
if not f then
print("F is nil!!! Err:"..err)
else
f.fn=function (x)
print("x="..x)
end
f()
end
And here is the loaded file:
#tryModB.lua:
fn("hello")
Error:
lua: tryModA.lua:7: attempt to index global 'f' (a function value)
stack traceback:
tryModA.lua:7: in main chunk
[C]: ?
Question: why does it happen ?
Isn't it true that loadfile() returns a function object and I can attach another function to it? PS. I come from a JavaScript programming background where it has prototype-based objects. I assume Lua has the same prototype based objects.
In Lua, loadfile() returns a function (not a function object) and functions can only be called. "Attaching" whatever to a function like you are trying is not possible.
Now, Lua tables are completely different story and the prototype-based concepts from JavaScript probably apply to them (I'm not very familiar with JS). The simplest way to make your code work at this point is to make fn global i.e. replace f.fn = function... with fn = function... although this might not be what you want.