We learned in the previous section that variables consist of both a name and a value, and that after a variable has been declared, it is possible to "re-assign" values to the name. The important detail here is that each variable requires a unique name.

Let's now take a look at an important aspect of variables, scope.

Global Scope

In the early days of programming, all variables were what we now call global variables, or variables that have (or exist in the) "global scope". The global scope can be thought of as a single bucket of uniquely-named variables, that are accessible from anywhere. Programmers quickly discovered that this created a variety of problems, ranging from simple annoyances such as the need to define (and keep track of) many similarly-named variables to avoid "name collisions", to accidentally-created and difficult-to-find bugs related to different parts of the code operating on the same variables.

Although global variables are generally discouraged today, they are supported by most languages, including Lua. In fact, variables created in Lua are global by default. While global variables might seem convenient, they lead to code that can be difficult to understand, debug, and maintain, which led the concept of scope and local scopes.

Local Scope

While we have described the global scope as a big bucket of variables, local variables can be thought of as smaller buckets of variables that are placed into that big global bucket.

Local variables have their scope limited by the "location" in which they are defined:

  • local variables defined in a file have their scope limited to that file. This allows the same variable names to be used in different files without risk of collision.

  • local variables defined within a function are local to that function, which allows functions to be easily used across different files.

  • Within a file or function, local variables defined within a block (such as a control structure are local to that block.

Let's take a look at a few examples:

local x = "file scope"
print(x) -- file scope

-- start an "if-then" block
if true then
    -- this block has its own scope
    local x = "block scope"
    print(x) -- block scope

-- back in the file scope
print(x) -- file scope

local fn = function()
    -- this function has its own scope
    local x = "function scope"
    return x

-- although we are back in the file scope
-- this prints the function's return value
print(fn()) -- function scope

-- this prints x from the file scope
print(x) -- file scope