Closures
Is it possible to have a function called inside another keep access to the local variable scoping of the function it was called inside [...]
Yes.
[...] without passing data, the same way as a nested function does?
The "nested function" is in that case called a closure and the variables that enter the closure's scope are called "upvalues" of the function. Example:
local function outer()
local var = "hello world"
local function closure()
print(var)
end
closure() -- will print "hello world"
end
note that these upvalues are references to the variables, not copies of them! So modifying the variable even after creating the closure works just fine:
local function outer()
local var = "hello world"
local function closure()
print(var)
end
closure() -- will print "hello world"
var = "new value"
closure() -- will print "new value"
end
So my first question would be: Why can't you use a closure here? Are you afraid of the cost of creating a new closure each function call? The alternatives - aside from passing the variables - won't be much cheaper:
Shared upvalue
You could make var a shared upvalue, setting it before calling the "inner" function:
local var
local function callee()
print(var)
end
local function caller()
var = "hello world"
callee() -- will print "hello world"
end
The disadvantage of this is that var bleeds into the enclosing scope of the two functions. You could use a do-end block to prevent this.
Shared (usually global) environment
You don't even need to use an upvalue for this - you can just use the (usually global) environment both functions reside in:
local function callee()
print(var)
end
local function caller()
var = "hello world"
callee() -- will print "hello world"
end
I would heavily recommend against this kind of so-called "global pollution" as the global environment will bleed into the scope of everything; in a constrained function environment this might be fine, but still you probably won't want to constantly index a table.
Function environments & debug hacks
Finally it is even possible to implement exactly the kind of scoping you're looking for by abusing the debug library - specifically its getlocal & setlocal functions as well as the getinfo function to get the function currently on the stack; you'll have to furthermore set the environment to one that leverages this by setting an __[new]index metatable which accesses the appropriate locals (or filling the environment table) _ENV of your function (you'd have to use setfenv for this on Lua 5.1, LuaJIT and older).
I am not providing the code for this as it is both (1) horribly inefficient and (2) a major code smell, but making the below work in Lua is possible:
-- implementing inherit_local_var_env is left as an exercise to the reader
local function callee()
_ENV = inherit_local_var_env()
print(var) -- accesses local var from the calling function
end
local function caller()
local var = "hello world"
callee() -- will print "hello world"
end
local function another_caller()
local var = "another message"
callee() -- will print "another message"
end