5

Just like how we can do this:

a = 3
print(_G['a']) -- 3

I want to be able to do something like this:

local a = 3
print(_L['a']) -- 3

I basically want to be able to access local variables using their names as strings. Is there a table that can do this, perhaps one that can be passed as a function argument? It would be like the this keyword in ActionScript.

Kyle Delaney
  • 11,616
  • 6
  • 39
  • 66
  • 1
    Please avoid ever doing this. I know it may seem harsh, but there is usually a much better way to do what you want to do in Lua, than to use the Debug Library. Please avoid the [XY Problem](http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem) – ATaco Nov 28 '16 at 22:23
  • http://hawwashsoft.proboards.com/thread/64/locals – warspyking Nov 29 '16 at 19:36

1 Answers1

7

This is possible by way of the debug library - namely the getlocal and setlocal functions. If you can't use this library (or access the C API), then you're out of luck.

You can extend your global environment with a specially crafted _L table, that when accessed performs linear lookups of the current set of locals.

Reading a local variable simply finds a matching variable name, and returns its value. Writing to a local variable requires you to discover its index in the stack frame, and then update the value accordingly. Note that you cannot create new locals.

Here's a simple example that works with Lua 5.1 (but not Lua 5.2+).

local function find_local (key)
    local n = 1
    local name, sname, sn, value

    repeat
        name, value = debug.getlocal(3, n)

        if name == key then
            sname = name
            sn = n
        end

        n = n + 1
    until not name

    return sname, sn
end

_G._L = setmetatable({}, {
    metatable = false,
    __newindex = function (self, key, value)
        local _, index = find_local(key)

        if not index then
            error(('local %q does not exist.'):format(key))
        end

        debug.setlocal(2, index, value)
    end,
    __index = function (_, key)
        return find_local(key)
    end
})

In use:

local foo = 'bar'

print(_L['foo']) --> 'bar'
_L['foo'] = 'qux'
print(_L['foo']) --> 'qux'

local function alter_inside (key)
    local a, b, c = 5, 6, 7
    _L[key] = 11
    print(a, b, c)
end

alter_inside('a') --> 11     6     7            
alter_inside('b') -->  5    11     7            
alter_inside('c') -->  5     6    11

You could write this in a different manner, using plain functions instead of the table combined with read / write operations (__index, __newindex).


See §2.4 – Metatables and Metamethods if the above use of metatables is a brand new topic for you.


In Lua 5.2+, you can use the special _ENV tables to adjust your current chunk's environment, but note that this is not the same as using local variables.

local function clone (t)
    local o = {}

    for k, v in pairs(t) do o[k] = v end

    return o
end

local function alter_inside (key)
    local _ENV = clone(_ENV)

    a = 5
    b = 6
    c = 7

    _ENV[key] = 11

    print(a, b, c)
end

alter_inside('a') --> 11     6     7            
alter_inside('b') -->  5    11     7            
alter_inside('c') -->  5     6    11

As a final note, also consider that this (ab)use of locals might not be the best approach.

You could simply store your variables in a table, when appropriate, to achieve the same results with far less overhead. This approach is highly recommended.

local function alter_inside (key)
    -- `ls` is an arbitrary name, always use smart variable names.
    local ls = { a = 5, b = 6, c = 7 }

    ls[key] = 11

    print(ls.a, ls.b, ls.c)    
end

alter_inside('a') --> 11     6     7            
alter_inside('b') -->  5    11     7            
alter_inside('c') -->  5     6    11

Don't dig yourself into a hole trying to solve unnecessary problems.

Oka
  • 23,367
  • 6
  • 42
  • 53
  • Your code doesn't work. `local a = 1; print(a, _L['a']) --> 1 nil` – Egor Skriptunoff Nov 28 '16 at 07:50
  • Seems like a discrepancy of sorts between Lua 5.1 and 5.2+, presumably due to the difference in how local environments work. I've linked the 5.3 docs throughout this answer, but admitted only tested this code in 5.1. I don't have the energy to make this code portable between versions, if its even possible, considering it was only meant as a simple example of how this is theoretically possible. Again, it's not advised to use this kind of introspective programming in production. The manual warns against the use of the debug library for anything other than debugging, as it is slow as snails. – Oka Nov 28 '16 at 23:41
  • Can you please warnings above the solution. It is not just bad practice - it is horrible for too many reasons, as you likely know yourself. – Oleg V. Volkov Nov 29 '16 at 02:54
  • @OlegV.Volkov The warnings are clear as day in the preface of the [`debug`](http://www.lua.org/manual/5.3/manual.html#6.10) library, which I linked first and foremost. It's not _exactly_ relevant to the body of the answer that this information be present, and the onus is on the learner to properly read all appropriate documentation. – Oka Nov 29 '16 at 02:58