[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: Re: Quest: real world "Lua array with holes" usage
- From: Hisham <h@...>
- Date: Mon, 25 Jul 2016 19:19:24 -0300
On 24 July 2016 at 23:48, Tim Hill <drtimhill@gmail.com> wrote:
>
>> On Jul 24, 2016, at 11:34 AM, Roberto Ierusalimschy <roberto@inf.puc-rio.br> wrote:
>>
>> Of course any function can return nil among its returns, but maybe
>> that might be considered a bad practice.
>>
>> My point is that, if nil is representing the absence of a value, it
>> is weird not to have a third value but to have a fourth one. If nil
>> is representing something else, probably it shouldn't be (as pointed
>> out by others already).
>>
>> ("is weird" ~ "it might be considered weird”)
>
>
> It’s weird *only* if the values are a strict list. If the values are (conceptually) a tuple, then of course an intermediate may be nil. While it could be argued that tuples should be returned as tables, you have the same problem; you can’t put a nil mid-way (and, as others have noted, returning a tuple directly can be faster and has less memory churn).
Yes, that's what I meant. In a function, sometimes I'll return three
unrelated things and any one of them might be nil.
function get_bla_data()
-- ...
return size, parent, color
end
-- parent might be nil, no problem
local size, parent, color = get_bla_data()
So in essence, I end up returning a tuple because that's what
multiple-returns (conceptually) are, but the order is pretty
arbitrary. I just want to return three things and get these three
things on the other side. Sure, I could return them as a table with
named keys, but that would be cumbersome/heavier/slower and then we
wouldn't need multiple-returns (or table-packing results!) at all.
function get_bla_data()
-- ...
return { size = size, parent = parent, color = color }
end
local bla_data = get_bla_data()
local size, parent, color = bla_data.size, bla_data.parent, bla_data.color
-- boring!!
Shocking thought right after writing this: if Lua had some kind of
table auto-destructuring syntax, could it get rid at once of varargs,
multiple-returns and table-packing-unpacking altogether!?
Thought experiment: here's a brave new blue-sky, half-baked "Lua 6 -
now everything-is-tables even more" world
(1) As a preliminary, needed for the stuff below, # becomes a special
valid table key (note that # ~= "#").
* #t is redefined to return t[#] if this key is set, or the Lua 5.3
semantics of #t if it is not.
* pairs(t) always skips t[#]
* if t[#] is set, ipairs(t) traverses up to t[#]
(2) Multiple returns are now actually a table
return x, y, z
becomes sugar for
return { [1] = x, [2] = y, [3] = z, [#] = 3 }
(3.1) Function arguments and varargs are now actually a table
function f(x, y, z)
-- ...
end
becomes sugar for
function f(_ARG)
local x, y, z = _ARG[1], _ARG[2], _ARG[3]
-- ...
end
(3.2) Explicit varargs become a destructuring construct for _ARG:
function(x, y, ...)
local t = { ... }
end
becomes sugar for
function f(_ARG)
local x, y, z = _ARG[1], _ARG[2]
local t = { [#] = _ARG[#] - 2, [1] = _ARG[3], --[[...]] }
end
(4) Function calls auto-structure its arguments into a table:
f(10, 20)
becomes sugar for
f({[1] = 10, [2] = 20, [#] = 2})
(5.1) Returns of function calls in expression contexts
auto-destructure the return table:
local x, y, z = f()
becomes sugar for
local _t = f()
local x, y, z = _t[1], _t[2], _t[3]
(5.2) Returns of function calls in table constructor contexts
auto-destructure the table, including the size:
local t = { f() }
becomes sugar for
local _t = f()
local t = { [1] = _t[1], [2] = _t[2], [3] = _t[3], [#] = _t[#] }
(translation of { f(), g() } with reasonable semantics and the
equivalent for (5.1) are left as an exercise (I said it was
half-baked!) )
Results:
* All functions receive a single argument, a table (_ARG), and return
a single argument, a table (which you never get to touch).
* No more table.pack() or select(), just use _ARG explicitly.
* No more table.unpack(), just do { f() }, which does the right thing
* { ... } also does the right thing
Of course, the implementation would optimize away these implicit
tables at compile-time. After all, the vast majority of functions
won't refer to _ARG explicitly and the return-values table is
essentially conceptual. Only functions using _ARG would pay for a
table, etc.
I hope you all enjoyed reading this as much as I enjoyed writing it,
and now have fun shooting it down by pointing out all the obvious
flaws that I evidently haven't spent any time pondering about! :-D
-- Hisham