lua-users home
lua-l archive

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



mnicolet escribió:

> The whole question could be simplified to
>  a) follow your guidelines for ´assignment´, setting ´special variables´
>     and methods
>  could that be done from C code ?

Sure. The point of the page is to show how to do it, but it could
easily be implemented in C. If I have time, I'll hack something
together.

>  b) hack a bit the lvm.

I think your suggestion for __lt is a reasonable compromise. However,
there is a slight issue with what "the same metamethod" means.

Consider the following code:

--------------------------------
Code snippet 1
--------------------------------

function compare_by_id(self)
  local function lt(a, b)
       return a.id < b.id
  end

  local meta = getmetatable(self)
  if meta then meta.__lt = lt
          else setmetatable(self, {__lt = lt}
  end
  return self
end


a = compare_by_id {id = 2}
b = compare_by_id {id = 3}

if a < b then print "a is less"
         else print "b is less"
end

----------------------------------

Now, 'a' and 'b' have different metatables. But that should be
ok because they have the same __lt metamethod, right? Wrong!
Every time compare_by_id runs, it returns a different "closure".
(A closure is a compiled function with a list of upvalues. Even
though there are no upvalues in the above functions, you get
a new closure every time you execute the function statement.

To make that clearer, suppose the function which creates the
__lt method looked like this:

--------------------------------
Code snippet 2
--------------------------------

function compare_by_named_key(key, self)
  local function lt(a, b)
    return a[key] < b[key]
  end

  local meta = getmetatable(self)
  if meta then meta.__lt = lt
          else setmetatable(self, {__lt = lt}
  end
  return self
end

----------------------------------

Now 'key' is a "closed variable" in 'lt' so the two closure
instances are obviously different:

----------------------------------
Example 2.1
----------------------------------

a = compare_by_named_key("apples", {apples = 2})
b = compare_by_named_key("oranges", {oranges = 3})

----------------------------------

Then the comparison 'a < b' is comparing apples with oranges. :)
But in the following case, I would like it to work:

----------------------------------
Example 2.2
----------------------------------

a = compare_by_named_key("apples", {apples = 2})
b = compare_by_named_key("apples", {apples = 3})

----------------------------------

It won't, though.

Now, the first example could be fixed by lifting the definition
of lt out of compare_by_id:

----------------------------------
Code snippet 3
----------------------------------

do
  local function lt(a, b)
    return a.id < b.id
  end

  function compare_by_id(self)
    local meta = getmetatable(self)
    if meta then meta.__lt = lt
            else setmetatable(self, {__lt = lt})
    end
    return self
  end
end

----------------------------------

In this case, there is only one closure of 'lt' and
so 'a' and 'b' will end up with different metatables but
the same __lt metamethod.

That won't help with the second example, though. There
is a workaround, but it is more complicated; I have to
put the key name into the metatable, too, something
like this:

----------------------------------
Code snippet 4
----------------------------------

do
  local function lt(a, b)
    local akey, bkey = getmetatable(a).sortkey,
                       getmetatable(b).sortkey
    return akey == bkey and a[akey] < b[bkey]
  end

  function compare_by_named_key(key, self)
    local meta = getmetatable(self)
    if meta then meta.sortkey, meta.__lt = key, lt
            else setmetatable(self, {sortkey = key,
                                     __lt = lt})
    end
    return self
  end
end

----------------------------------

However, that is a lot more complicated (and slower), and it
is just a simple example.

In Lua 4, (and with c-closures in Lua 5), we could say that
two closures are the same if there functions are the same
and the list of upvalues in the closures are element by
element equal. That won't work in Lua 5, though, because
Lua 5 has full lexical binding and no longer has constant
upvalues (in Lua functions). So going back to Example 2.2,
the two different instances of 'lt' have *different* 'key'
upvalue objects which happen to have the same value.

It is clear from a syntactic analysis of the code in Snippet 2
that 'key' is a constant, but Lua does not (currently) do this
analysis. Otherwise, it could create a constant upvalue vector
instead of a variable upvalue vector, and the element-by-element
comparison would still work.

Regardless, we would end up with a fairly time-consuming test
for metamethod comparison. It still might be faster than the
multiple function calls and hash lookups of Code Snippet 4,
though.

Furthermore, Code Snippet 2 is (at least theoretically) better
because it makes it clear that the comparison key is fixed. In
Code Snippet 4, I could legally change the name of the
comparison key at any time (unless the metatable itself is
locked, but even that cannot easily be worked out by
syntactic analysis.)

> What is ´binary dispatch´ ? may it be something like the simple code I´m
> thinking about ?

It is where you consult the types of both operands at the same time.
A few languages implement this (Dylan, for example, and I believe there
is a Java dialect which does it.) In the case of non-inherited types, it
is easy; you just have a hash table of type pairs. But inheritance makes
it trickier and multiple inheritance makes it even trickier still.
Automatic
type coercion is also complicated.

I'll see if I can dig up some web references rather than doing a long
essay.