lua-users home
lua-l archive

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

Michal Kolodziejczyk wrote:
I am also missing __tonumber metamethod...

It seems like this could be a useful feature, but if it were to behave like __tostring it would be of limited value - limited to programmers calling tonumber() explicitly.

Note that although the __tostring metamethod exists, it is *not* used by the VM when it decides that it needs a string value. It is used only by the Lua base library function tostring(). And the only time tostring() is called (other than when the developer puts tostring() in his Lua code) is in the base library implementation of print().

The VM supports coercion to number and string to a limited extent, but only between those two types because it is simple and relatively fast to do so.

If you were to do a similar thing for tonumber(), it would never get used by the Lua internals. Attached is a two-line patch to lbaselib.c that makes tonumber() behave just like tostring(), honoring a __tonumber metamethod (without the optional numeric base). This allows the following:

  print(t, tonumber(t))    -- table: 0x807e060	nil
  mt.__tostring = table.concat
  mt.__tonumber = function (self) return self[1] or 0 end
  setmetatable(t, mt)
  print(t, tonumber(t))    --	0
  t[2]=" is perfect"
  print(t, tonumber(t))    -- 6 is perfect	6
  print(tonumber(t) + 8)   -- 14

But this is of limited value since, unlike tostring(), there is no place that Lua uses tonumber() itself. Still, it might be of value when the programmer uses tonumber() explicitly.

For example, I think you're looking for this sort of use case (error messages truncated):

  for k = t, 10 do print(k) end
  --  stdin:1: 'for' initial value must be a number
  print(t + 8)
  --  stdin:1: attempt to perform arithmetic on global 't'

Adding support for that kind of thing would have to be done in a way that resolves the ambiguities that will arise between them and the operator metamethods (perhaps by a precedence rule, reducing further the simplicity that is Lua).

For example, the easiest way to provide such support would be to modify luaV_tonumber() so that it calls the __tonumber metamethod if it exists (relying on the string table's __tonumber metamethod to achieve its current behavior). This would allow each of the erroneous examples above to work (untested).

Developers would then have to decide whether their objects should define __tonumber or the operator metamethods, since the former would cause the latter to be performed as normal lua_Number operations. I can think of examples where a developer would choose the former (defining __tonumber), say for a bank account object whose balance would be returned. But for most use cases I would imagine a developer choosing to redefine the binary operators instead.

Other ways are more complicated. If you have an idea, give it a spin.

Personally, I don't think it is worth the complexity. __tostring is useful for print() and possibly for serialization of objects, but I don't see the same usefulness for __tonumber.


The information contained in this email transmission may contain proprietary and business sensitive information. If you are not the intended recipient, you are hereby notified that any review, dissemination, distribution or duplication of this communication is strictly prohibited. Unauthorized interception of this e-mail is a violation of law. If you are not the intended recipient, please contact the sender by reply email and immediately destroy all copies of the original message.

Any technical data and/or information provided with or in this email may be subject to U.S. export controls law. Export, diversion or disclosure contrary to U.S. law is prohibited. Such technical data or information is not to be exported from the U.S. or given to any foreign person in the U.S. without prior written authorization of Elbit Systems of America and the appropriate U.S. Government agency.
diff -c -r lua-5.1.4-orig/src/lbaselib.c lua-5.1.4/src/lbaselib.c
*** lua-5.1.4-orig/src/lbaselib.c	2008-02-14 11:46:22.000000000 -0500
--- lua-5.1.4/src/lbaselib.c	2009-02-05 08:32:52.000000000 -0500
*** 52,57 ****
--- 52,59 ----
  static int luaB_tonumber (lua_State *L) {
    int base = luaL_optint(L, 2, 10);
+   if (luaL_callmeta(L, 1, "__tonumber"))  /* is there a metafield? */
+     return 1;  /* use its value */
    if (base == 10) {  /* standard conversion */
      luaL_checkany(L, 1);
      if (lua_isnumber(L, 1)) {