Parsing varargs to a function

89 views
Skip to first unread message

Sean Conner

unread,
Apr 27, 2026, 4:31:18 PM (6 days ago) Apr 27
to lua-l

I know that to parse varargs, one uses select():

function f(...)
for i = 1 , select('#',...) do
local x = select(i,...)
print(x)
end
end

What was the thinking behind this? Why not?

function f(...)
for i = 1 , #... do
local x = ...[i]
print(x)
end
end

That is, treat ... as an array and use existing syntax? I know this will
not work with ipairs(), but it seems to be a net win. Currently, with an
empty Lua state, there is no way for code to interate through the varargs
without the select() function (or at least, not easily).

-spc (Just thinking out loud here ... )



Sainan

unread,
Apr 27, 2026, 4:42:45 PM (6 days ago) Apr 27
to lu...@googlegroups.com
May I introduce you to Lua 5.5?

function f(...t)
for i = 1, #t do
print(t[i])
end
end

-- Sainan

Lars Müller

unread,
Apr 27, 2026, 4:57:35 PM (6 days ago) Apr 27
to lua-l
Sainan: To be precise, it's actually t.n rather than #t. This makes some sense because #t generally may stop at any boundary.

See also https://lua.org/manual/5.5/manual.html#3.4.10.
Worth noting that vararg tables, in 5.5, are guaranteed to have the actual "table.pack" optimized away if you only index them and take their count t.n (directly, not in a closure),
because then Lua can just peek at the stack to get the values.

Personally I would have preferred a (peephole-ish) optimization that makes "x = select(i, ...)" constant time, as found in LuaJIT.
Then the "select"-loop would be fine as idiomatic vararg iteration construct.
(In its current state, it runs in quadratic time on PUC Lua, as varargs with (average) linear length are copied on the stack in every invocation.
For older versions of PUC Lua, I thus recommend iterating tables by first packing them up using table.pack and then iterating up to t.n instead.)

- Lars

Martin Eden

unread,
Apr 27, 2026, 7:02:26 PM (6 days ago) Apr 27
to lu...@googlegroups.com

On 2026-04-27 22:57, Lars Müller wrote:
> Personally I would have preferred a (peephole-ish) optimization that makes
> "x = select(i, ...)" constant time, as found in LuaJIT.
> Then the "select"-loop would be fine as idiomatic vararg iteration
> construct.
> (In its current state, it runs in quadratic time on PUC Lua, as varargs
> with (average) linear length are copied on the stack in every invocation.
> For older versions of PUC Lua, I thus recommend iterating tables by first
> packing them up using table.pack and then iterating up to t.n instead.)
Hello Lars,

I don't care about performance of PacMan's bait "...".

I use "..." mostly to wrap results of function passed as argument.
So there is unlikely be 20 M values from function. And O(N)
get_by_index(index, ...) is okay.

-- Martin

Lars Müller

unread,
Apr 28, 2026, 5:57:11 AM (5 days ago) Apr 28
to lu...@googlegroups.com, Sean Conner
Hehe, I have a WIP blog post about this :-)

I used to think that the "select loop" is the preferred way to iterate varargs too. It has the desired semantics: It iterates nils, and it's simple enough.
Unfortunately, it's only fast (linear time) on LuaJIT.
On PUC Lua, it's quadratic time in the length of the vararg, because a whole lot of copying happens on the stack.

That is, the "proper" way to iterate a vararg in PUC Lua is to pack it up in a table t with table.pack, and then iterate that up to t.n using numeric for.
(On 5.1, you need to polyfill table.pack using select("#", ...) but that's okay.)

Lua 5.5 finally adds a proper, more efficient way with vararg tables, see https://lua.org/manual/5.5/manual.html#3.4.10 for details.
Effectively your example becomes:

function f(...t)
  for i = 1, t.n do

    print(t[i])
  end
end


This is guaranteed to be optimized to just indexing the stack (and recording the number of items on the stack at calltime), so no garbage table is actually created and populated, unless t is used in a manner that makes this optimization inapplicable (e.g. it may escape as an upvalue, or further keys are added to it, etc).

Personally I think I would have preferred select + truncation being peephole optimized as in LuaJIT in order to avoid the need for an additional language feature, but at least there's now a "canonical" way to iterate varargs in PUC Lua :)

- Lars


On Mon, Apr 27 2026 at 16:31:12 -04:00:00, Sean Conner <se...@conman.org> wrote:
I know that to parse varargs, one uses select(): function f(...) for i = 1 , select('#',...) do local x = select(i,...) print(x) end end What was the thinking behind this? Why not? function f(...) for i = 1 , #... do local x = ...[i] print(x) end end That is, treat ... as an array and use existing syntax? I know this will not work with ipairs(), but it seems to be a net win. Currently, with an empty Lua state, there is no way for code to interate through the varargs without the select() function (or at least, not easily). -spc (Just thinking out loud here ... )
--
You received this message because you are subscribed to the Google Groups "lua-l" group. To unsubscribe from this group and stop receiving emails from it, send an email to lua-l+un...@googlegroups.com. To view this discussion visit https://groups.google.com/d/msgid/lua-l/20260427203112.GA31146%40brevard.conman.org.

Lars Müller

unread,
Apr 28, 2026, 9:53:30 AM (5 days ago) Apr 28
to lu...@googlegroups.com
Hi Martin,

Yes, you're right that most of the time, it doesn't matter much as you have something like a couple dozen arguments at most.
(And if you expect to have more, you should strongly consider a table.)

But this is a "you can have your cake and eat it too" situation. There is no real need for a tradeoff here.
From a language design perspective, there should be a canonical construct which lets you correctly and efficiently iterate varargs.
I want to be able to tell programmers "just use this way of iterating varargs" in a style guide.
The programmer should not need to think "hmm, this vararg will probably always be small enough that quadratic time is okay",
which generally can't be ensured when you're writing, say, library code.

And so I find it nice to see that at long last, this "canonical iteration" has been made a thing :)

- Lars
--
You received this message because you are subscribed to the Google Groups "lua-l" group. To unsubscribe from this group and stop receiving emails from it, send an email to lua-l+un...@googlegroups.com. To view this discussion visit https://groups.google.com/d/msgid/lua-l/5e68c2c2-aa8d-4f01-b67b-65d8332b3246%40disroot.org.

Roberto Ierusalimschy

unread,
Apr 28, 2026, 12:14:18 PM (5 days ago) Apr 28
to 'Lars Müller' via lua-l, Sean Conner
> Personally I think I would have preferred select + truncation being peephole
> optimized as in LuaJIT in order to avoid the need for an additional language
> feature, but at least there's now a "canonical" way to iterate varargs in
> PUC Lua :)

Optimizing 'select' is hardly a peephole optimization, as there is no
way to know ahead of time whether the function that will be called is
the "real" 'select'.

-- Roberto
Reply all
Reply to author
Forward
0 new messages