Functions

As variables

In lua functions are first class variables, this means that they have the same rights as conventional values like numbers and strings. Functions can be stored in variables and in tables, can be passed as arguments, and can be returned by other functions. So one can assign the print_array function (see Tables) to a variable print_list and then use print_list to execute print_array; like this

print_list = print_array
print_list(array)

This means that one can do things like use a table to cut short a long "if elseif .... else end" structure for instance when processing menu choices.

So this if-then-else structure

> function do_choice(choice)
>         if choice == 1 then
>                 print("Your choice was: 1")
>         elseif choice == 2 then
>                 print("Your choice was: 2")
>         elseif choice == 3 then
>                 print("Your choice was: 3")
>         end
> end
> do_choice(1)
Your choice was: 1

could be replaced with this structure using a table

> choices = {}
> choices[1] = function() print("Your choice was: 1") end
> choices[2] = function() print("Your choice was: 2") end
> choices[3] = function() print("Your choice was: 3") end
> function do_choice(choice)

> choices[choice]()

> end
> do_choice(2)

Your choice was: 2

Namespaces

Being able to add functions to tables also means that tables can be used to create "namespaces". I won't elaborate on the meaning of the term, just give an example. If we have a number of functions doing things for one specific Celestia object type and the same number of functions doing the same things for other object types, we would end up with a list of functions sort of like this:

A lot of names that have to be structured, typed, remembered, maintained and the lot. What one cóuld do is create a table per object type and put the various functions in there, possibly also adding type dependend data; like this:

Instead of starOrbit("Sol") we now call star.orbit("Sol") or planet.orbit("Earth"), etc. With this you have effectively created separate namespaces for stars, planets and other Celestia types; each with their own functions, data, etc. The function and data names can remain short and descriptive. For each namespace the same names can be used for similar functions or data. This is not less work, but in the end it is simpler to maintain and work with.

Syntax variant

With regards to functions lua recognizes a syntax variant that allows a function call like this:

  • table.print(table)

to be written as:

  • table:print()

! Notice the colon that takes the place of the period.

Both syntaxes have exactly the same result. Whenever lua encounters a table:function(argument) construct, it will convert it to table.function(table, argument) before executing it, moving all possible other parameters one position to the right. This variant is part of some syntactic sugar intended to give lua a more 'object oriented' (oo) look and feel.

There is also a counterpart for function definitions, that allows a function definition statement like:

  • function table.print(table)
  • ..........
  • end

to be written as:

  • function table:print()
  • ..........
  • end

! Notice the colon that again takes the place of the period.

Also here a table:function(parameter) construct gets converted to table.function(table, parameter) before executing it, moving all other parameters one position to the right.

Furthermore inside the functions the keyword "self" can be used, that then refers to the function's first parameter: normally the "table" parameter. So the next complete example:

tableEt = {}                                -- the extention table
function tableEt.print(table)
        for key, value in pairs(table) do
                print(string.format("\n%-15s = %s", tostring(key), tostring(value)))
        end
end
tableMt = {}                                -- the metatable
tableMt.__index = tableEt                   -- link the extention table

-- -------------------------------------------------------------------------------------------------

function main()

seasons = { "Spring", "Summer", "Autumn", "Winter" }

setmetatable(seasons, tableMt)

seasons.print(seasons)

end

Can be written as:

tableMt = {}                                -- the metatable
tableMt.__index = tableMt                   -- use metatable as it's ow extention table (save's 1 table)

function tableMt:print()

        for key, value in pairs(self) do

print(string.format("\n%-15s = %s", tostring(key), tostring(value)))

        end

end

-- -------------------------------------------------------------------------------------------------

function main()

seasons = { "Spring", "Summer", "Autumn", "Winter" }

setmetatable(seasons, tableMt)

seasons:print()

end

This saves the typing of (duplicate) tablenames - which is allways a good thing - and gives the whole a cleaner look. Also in the example the print() method has been moved from a separate extention table to the metatable itself. Since the extention table holds function data, no user data, it is no problem that it has elements in it like: __index = tableMt. This saves a table, which also allways is a good thing; specifically when dealing with interpretive languages like lua.

Summarizing what happens:

    1. the function print() is not found in the seasons table
    2. seasons is found to have a metatable attached: tableMt
    3. tableMt is found to have an entry for the __index event
    4. the __index event refers to an extention table: tableMt
    5. tableMt is found to contain a print() function
    6. that print function is executed as: tableMt.print(seasons).

! Notice that the metatable/__index mechanism sees to it, that the original table "seasons" is correctly passed to the print function as the table argument

  • tableMt.print(seasons)

and not the metatable tableMt or even a possible separate extention table.