[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: Re: tables holding nil: another way to look at the question itself
- From: Cosmin Apreutesei <cosmin.apreutesei@...>
- Date: Tue, 4 Aug 2009 19:15:19 +0300
Sorry I missed your message completely... Please allow me to reply, if
you still remember the conversation.
On Tue, Jul 28, 2009 at 18:44, Jerome Vuarand<jerome.vuarand@gmail.com> wrote:
> 2009/7/28 Cosmin Apreutesei <cosmin.apreutesei@gazolin.ro>:
>>>> - why is there a difference between a list of nil arguments and no
>>>> argument? --> nil is acting like any other value here.
>>>
>>> That's an extra feature provided for convenience. Just like you can
>>> see tables as infinite key-value pairs, you can see argument list as
>>> infinite list of values, defaulting to nil. As a convenience you can
>>> get the number of values that was actually passed to a function call,
>>> but you can still access all elements of the argument list (just call
>>> select(42, ...) for example).
>>
>> Yea, but providing the arg. count is leaking the abstraction. If I am
>> to make an optional parameter in the middle of the arg. list, should I
>> account for the real arg. count or use an ipairs()-like iteration? In
>> other words, should I account for trailing nils? What if some other
>> function that my function wraps up is sensible about the arg. count as
>> well? I have to carry around arg. count everywhere. Whereas without
>> this leak, {...} would had been enough.
>
> Vararg lists are special kind of objects that you cannot instantiate
> or reference. They are not tables.
>
> You don't have to use or allow the vararg feature of Lua. You can
> write functions that expect a fixed number of arguments, no more and
> no less (even with forced trailing nils).
>
You don't seem to have read my arguments about this. Allow me an example:
Suppose I have to wrap function f in function g. I don't care for f's
arguments, just need to pass them along as g receives them. Can I just
ignore the feature of arg. count in this case? My own function g
surely don't need the feature, but the function f, written by someone
else, might be sensible about arg. count, forcing me to account for
this feature too if I am to implement g() correctly. That's what I
meant when I said I have to carry the abstraction leak everywhere --
after all, that's what makes it a leak.
I see pairs() as an abstraction leak too, allowing me to "see" a small
set of the invinite set of keys. Example: say I need an unordered list
of objects, any of which can have a value attached. This can be
represented in two obvious ways:
1) { {obj1, obj2, ... }, {[obj1] = obj1_value, [obj2] = obj2_value, ... }
2) { [obj1] = obj1_value, [obj2] = obj2_value, ... }
The first allows objN_value to be any value, including nil, and
doesn't require pairs() to exist. The second representation is made
available by pairs(), and that's the one I see most of the time in Lua
code, usually with nil values "fixed" with a boolean false value. When
I see code like this I understand why some people believe tables
"can't hold nil" :) Without pairs(), you couldn't have representation
#2, and so no issues with nils, but pairs() leaks hidden information
about tables, specifically the list of non-nil keys, a table which
itself can't hold the nil :) I'm not pleading ditch pairs() here, it's
probably too useful, but it all falls down on poor nil.
Sorry if this sounds too pedantic or theoretical, but the effects are
quite practical.
>>> nil is a normal value, just like 42, true, false and "Hello World!".
>>> The authors decided that the value associated to nil in a table should
>>> always be nil, that key-value pair is read-only. Sometimes you have to
>>> accept exceptions to the rule. That exception has motivations
>>> (previously discussed on that list).
>>
>> I dig it. But then __index should not be called for the nil key to
>> make sure the rule is not broken.
>
> We're talking about an exception, why should it obey another rule ?
> Also the read-only nil-key pair is a feature of tables, while index
> and newindex let you build other types of objects (eventually with
> different semantics) on top of tables. Keep in mind that index and
> newindex in tables are exceptions in the real of metamethods, because
> they let you override a behaviour that is otherwise perfectly defined
> and valid.
Metatables should allow me to override the implementation of a table,
they shouldn't let me corrupt its semantics (whatever that is) or
things will break.
> Also a side effect of the current iterator protocol is that it plays
> nicely with table indexing. For example in
> local t = {}
> for x in values(...) do t[x] = true end
> you are sure that the loop content will not throw an error (of course
> the iterator itself can). That's not a crucial feature, but that's one
> nonetheless.
>
That's looks like matching two limitations together to make a feature :)