[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: Re: New metamethod for method lookup
- From: Jim Pryor <lists+lua@...>
- Date: Sun, 15 Nov 2009 13:00:20 -0500
On Sun, Nov 15, 2009 at 11:59:20AM +0200, steve donovan wrote:
> Basically, there is a need to separate method lookup from field
> lookup, but in such a way that the old method is always available.
>
> Here is the problem case: say you wish to wrap a set as an object. Say
> also that you want s[val] to _reliably and always_ indicate the
> presence of val in the set s. Currently, this cannot be be done. If
> our set has an intersection() method, then s['intersection'] will
> always be a false positive, because we look up 'intersection' in the
> set's metatable if lookup fails. So we have to write this operation
> as a method call, say s:get(val) or s:exists(val) that uses rawget
> internally, which isn't so pretty or intuitive.
A different pattern, which I like better and is achievable within Lua as
it is today, is just don't bind methods to objects you want to permit
arbitrary data keys on.
Set = {}
setmetatable(Set, {__call = function(cls,...)
local obj = {}
for i=1,select('#',...) do
obj[select(i,...)] = true
end
return setmetatable(obj, Set)
end})
function Set:intersection(other)
-- blah blah --
end
-- make some sets --
s1,s2 = Set(...), Set(...)
-- get their intersection --
Set.intersection(s1, s2)
That is, we don't ever make getmetatable(s1).__index == Set. You call
Set.intersection(s1,...) not s1:intersection(...). In the same way you call
table.remove(tbl,...) not tbl:remove(...).
If you want inheritance, you can wrap that in too. You're just giving up
the object-oriented syntax; you don't have to give up its power.
-- One way to get inheritance
local class_sentinel = {}
setmetatable(Set, {__call = function(cls, ...)
local obj = {}
for i=1,select('#',...) do
obj[select(i,...)] = true
end
obj[class_sentinel] = cls
return setmetatable(obj, Set)
end
local function _intersection(self, other)
local cls = self[class_sentinel]
-- we assume that subclasses will have their bases as __index, so
-- cls.intersection is always defined, even though it may just
-- be == Set.intersection
local meth = cls.intersection
if meth ~= _intersection then
return meth(self, other)
else
-- default Set.intersection implementation here --
end
end
Set.intersection = _intersection
--
Profjim
profjim@jimpryor.net