lua-users home
lua-l archive

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


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