2

I'm having trouble understanding the following. I have the following code:

awful.key({ "Mod1" }, "Tab",
    function (c)
        local grabber = awful.keygrabber.run(
            function(mod, key, event)
                if grabber == nil then
                    naughty.notify({ text="nope"})
                end
                awful.keygrabber.stop(grabber)
                return
            end)
    end)

This is supposed to grab the keyboard when I press super+Tab then release keyboard focus by calling the stop method. However the grabber variable appears to be nil. At first I thought this was a scope problem, so I removed the local, this works. However I have the feeling as if this isn't the way to solve this problem.

After messing around with it I found out this works:

awful.key({ "Mod1" }, "Tab",
    function (c)
        local grabber
        grabber = awful.keygrabber.run(
            function(mod, key, event)
                if grabber == nil then
                    naughty.notify({ text="nope"})
                end
                awful.keygrabber.stop(grabber)
                return
            end)
    end)

The only difference is that the variable grabber is defined on one line and is assigned one line later. Why can't I do this on the same line?

siebz0r
  • 18,867
  • 14
  • 64
  • 107
  • 4
    The scope of a local variable begins at the first statement **after** its declaration and lasts until the last non-void statement of the innermost block that includes the declaration. [Manual](http://www.lua.org/manual/5.2/manual.html#3.5) – Egor Skriptunoff Feb 22 '14 at 20:37
  • Pedantically, `local grabber grabber = awful.keygrabber.run(`…, because whitespace, comments and `;` are statement separators. (But, that would make it harder to read.) – Tom Blodget Feb 27 '15 at 02:27

1 Answers1

4

In the statement

local a = expr

where expr can be any Lua expression, the local a is only created after the expression has been evaluated. Before that, the local a does not exist. If the expression uses a variable called a, the variable is taken from the next "level" up. See section 2.6 of the Lua ref, it is short and provides additional insight into this. But what it means is that if you have

a = 123
local b = 456
local c = c + b

the third line will fail to execute because the c on the right side of the = does not yet exist so it is nil. The b does exist, although it is a local. Similarly, in

local a = f()

if f() uses a, Lua will look for an a that is above that line since it has not yet created a local a. If there is none above, a will be nil, regardless of how many times the function is run:

do 
    local a = 1
    function g() a=a+1 end -- modifies the above local a ("upvalue")

    local a = function() return a+1 end -- a+1 uses the first local a
    -- a is now a local function, using the first local a 

    print(a()) -- prints 2
    g() -- increases the external a
    print(a()) -- prints 3
end

So where the local is declared in relation to the functions that use them is critical, and a local doesn't exist (even a local that "hides" a previous local) until the expr is fully evaluated.

Oliver
  • 27,510
  • 9
  • 72
  • 103
  • 2
    The first `a` will still be alive even without a do-end block. Changing `g` to print `a` will show that. – lhf Feb 23 '14 at 12:01
  • Thanks for pointing that out. I have changed end of my answer to reflect this, hope it is better. – Oliver Feb 23 '14 at 16:32
  • You explained the issue rather well however I expected the example to demonstrate the problem. The example seems to work instead of demontrating the problem. – siebz0r Feb 23 '14 at 21:12