[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: Re: Alternative (better?) __index implementation
- From: "alex.mania@..." <alex.mania@...>
- Date: Sat, 01 Dec 2007 19:53:41 +0900
On Sat Dec 1 14:34 , Rici Lake <lua@ricilake.net> sent:
>
>Suppose Table2 were created with Memoize:
>
>function Memoize(func)
> return setmetatable({}, {
> __index = function(self, key)
> local rv = func(key)
> self[key] = rv
> return rv
> end,
> __call = function(self, key) return self[key] end
> }
>end
>
>This is going to act very oddly if self is wrong. (In particular,
>the __call metamethod will fail.)
>The basic problem with special-casing the __index chaining is that it
>becomes
>non-composable. The behaviour of a functable changes if it is the
>target of
>an __index metamethod, in a way which is hard to predict.
I can see this would be a problem with particular, but still rare contexts. Eg
trying to memoize a tables __index function would break, as you say. Similarly
trying to use an already memoized function as a tables __index would break.
>The putative savings (one function call)
> are just not worth the breaking of orthogonality.
There would have to be large savings, I agree, to risk breaking orthogonality. I
still think there's a case for it though - as it's only one additional function
call per method lookup per object. But then how many objects are there in a
program? How many times do each of them have their methods invoked? I'm guessing
the savings would add up quite quickly. Particularly as the goal of oo is to reduce
a program in to smaller parts, which tends to reduce methods to simple functions
that are probably no bigger then the obligatory __index metamethod would be anyway.
I guess you could use a system where the class creator uses two object approaches -
if the class being created has getters/setters to use the additional overhead way;
otherwise to use the traditional way. Just a shame that again this makes it
impossible to create functions to be called should the index not exist in the base
class.
>One backwards compatible extension which might solve some issues (such
>as
>the "redundant" function call in your string example) would be to add a
>__proxy
>metakey, and change the semantics of the get event to:
>function get(self, key)
> local rv = rawget(self, key)
> if rv == nil and getmetatable(self).__proxy then
> rv = getmetatable(self).__proxy[key]
> end
> if rv == nil and getmetatable(self).__index then
> -- to be entirely backwards compatible, we should
> -- try using __index as a table first. But we'd drop
> -- that eventually
> rv = getmetatable(self).__index(self, key)
> end
> return rv
>end
>
>Aside from being slightly more efficient in the not uncommon
>case that you have a proxy table and a metafunction, and only
>want to use the metafunction if the key is not in the proxy table,
>this has the advantage that it clearly separates the two operations:
> *indexing* the __proxy and *calling* the __index. This would make
>it possible to use, for example, userdata with __index metamethods
>as __proxy metavalues, and tables with __call metamethods as
>__index metavalues, neither of which are currently possible;
>consequently, it would improve orthogonality (imho).
>
>Rici
Again, not so sure I agree with putting redundant in demeaning quotes there ;). But
that being said, I love the solution you propose. Love it =). With your permission
would like to begin implementing that right now - it solves my problems and fixes
my anxiety about modifying existing lua behaviour in one. What in your opinion
would be the best way to handle __newindex regarding proxys?
And it is a shame that not providing the compatibility would break so many existing
apps. Maybe make it #definable, but defined in by default; similar to a few things
in the current Lua 5.1 implementation.
Anyways, thanks very much for the constructive post and your great idea!
- Alex