As stated in the basic examples, you can have variable bound arguments and the variable argument list (
...). You can use this fact to recursively pull apart a list as you would in other languages (like Haskell). Below is an implementation of
foldr() that takes advantage of that. Each recursive call binds the head of the vararg list to
x, and passes the rest of the list to a recursive call. This destructures the list until there is only one argument (
select('#', ...) == 0). After that, each value is applied to the function argument
f with the previously computed result.
function foldr(f, ...) if select('#', ...) < 2 then return ... end local function helper(x, ...) if select('#', ...) == 0 then return x end return f(x, helper(...)) end return helper(...) end function sum(a, b) return a + b end foldr(sum, 1, 2, 3, 4) --> 10
You can find other function definitions that leverage this programming style here in Issue #3 through Issue #8.
Lua's sole idiomatic data structure is the table. The table length operator is undefined if there are
nils located anywhere in a sequence. Unlike tables, the vararg list respects explicit
nils as stated in the basic examples and the remarks section (please read that section if you haven't yet). With little work the vararg list can perform every operation a table can besides mutation. This makes the vararg list a good candidate for implementing immutable tuples.
function tuple(...) -- packages a vararg list into an easily passable value local co = coroutine.wrap(function(...) coroutine.yield() while true do coroutine.yield(...) end end) co(...) return co end local t = tuple((function() return 1, 2, nil, 4, 5 end)()) print(t()) --> 1 2 nil 4 5 | easily unpack for multiple args local a, b, d = t() --> a = 1, b = 2, c = nil | destructure the tuple print((select(4, t()))) --> 4 | index the tuple print(select('#', t())) --> 5 | find the tuple arity (nil respecting) local function change_index(tpl, i, v) -- sets a value at an index in a tuple (non-mutating) local function helper(n, x, ...) if select('#', ...) == 0 then if n == i then return v else return x end else if n == i then return v, helper(n+1, ...) else return x, helper(n+1, ...) end end end return tuple(helper(1, tpl())) end local n = change_index(t, 3, 3) print(t()) --> 1 2 nil 4 5 print(n()) --> 1 2 3 4 5
The main difference between what's above and tables is that tables are mutable and have pointer semantics, where the tuple does not have those properties. Additionally, tuples can hold explicit
nils and have a never-undefined length operation.