lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


On Sat, Nov 26, 2011 at 5:34 PM, Mark Hamburg <mark@grubmah.com> wrote:
> On Nov 26, 2011, at 5:52 AM, Roberto Ierusalimschy wrote:
>
>> It is more tricky. We think that 'n' should be something explicit.
>> (But we may remove the extra return, as it is redundant with the 'n'
>> field.)
>
> Or return table, 1, n.
>
> But how many use cases are there for passing the results of table.pack directly to table.unpack?
>
> So, I think it comes down to whether the utility of having easy access to the length value is worth the confusion caused by passing the results from table.pack to table.unpack.

I can envision two use-cases for table.pack:
1) Efficient access to every element of a vararg list (as
single-element access can be done efficiently with select(i, ...), and
inefficient access to every element can be done using {n=select('#',
...), ...}).
2) Saving a vararg list, so that it can be restored (using
table.unpack) by a different function at some point in the future.

For the first case, I don't think it really matters how the length is
reported. It looks acceptable as a return value:

function print_funny(...)
  local args, n = table.pack(...)
  for i = 1, n do print(">", args[i], "<") end
end

It also looks acceptable as an explicit field:

function print_funny(...)
  local args = table.pack(...)
  for i = 1, args.n do print(">", args[i], "<") end
end

Finally, it also looks acceptable as the table length:

function print_funny(...)
  local args = table.pack(...)
  for i = 1, #args do print(">", args[i], "<") end
end

That said, if you do have it as the table length, then you can write
the following, which is horribly inefficient, but looks interesting:

function print_funny(...)
  for i, arg in ipairs(table.pack(...)) do print(">", arg, "<") end
end

On the other hand, I'd argue that the third of the following looks
significantly neater than the previous two:

function tuple(...)
  local args, n = table.pack(...)
  return function() return table.unpack(args, 1, n) end
end

function tuple(...)
  local args = table.pack(...)
  return function() return table.unpack(args, 1, args.n) end
end

function tuple(...)
  local args = table.pack(...)
  return function() return table.unpack(args) end
end

If this last example were to work, then you would get that
table.unpack(table.pack(...)) gives you back ..., which is of no
practical use, but the symmetry is pretty, and it serves as a very
quick example of what table.unpack and table.pack do.

>From these two use-cases, I think that using the table length looks
nicest, as it avoids having to deal with multiple return values, it
avoids the slightly magic-looking n field, and it interfaces nicely
with other standard functions. At least to me, the apparent downside
of using table length would be the memory inefficiency, as a naive
implementation of using the table length (in Lua) would be:

function table.pack(...)
  local n = select('#', ...)
  return setmetatable({...}, {__len = function() return n end})
end

This has two tables being created, along with a closure. If the
semantics for __len were adjusted to allow a number as well as a
function (like __index allows tables as well as functions), then the
memory cost could be the same as an explicit named field:

function table.pack(...)
  local t = {__len = select('#', ...), ...}
  return setmetatable(t, t)
end

With this implementation, you could even use __len as an explicit
named field if you were unhappy with using table lengths, and at least
to me, saying args.__len feels slightly less magic than saying args.n.

Perhaps these arguments have been made before, and perhaps there are
other use-cases which I'm not seeing, but I felt like giving my 2c.