-
Notifications
You must be signed in to change notification settings - Fork 383
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
RFC: Support __len
metamethod for tables and rawlen
function
#536
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
# Support `__len` metamethod for tables and `rawlen` function | ||
|
||
## Summary | ||
|
||
`__len` metamethod will be called by `#` operator on tables, matching Lua 5.2 | ||
|
||
## Motivation | ||
|
||
Lua 5.1 invokes `__len` only on userdata objects, whereas Lua 5.2 extends this to tables. In addition to making `__len` metamethod more uniform and making Luau | ||
more compatible with later versions of Lua, this has the important advantage which is that it makes it possible to implement an index based container. | ||
|
||
Before `__iter` and `__len` it was possible to implement a custom container using `__index`/`__newindex`, but to iterate through the container a custom function was | ||
necessary, because Luau didn't support generalized iteration, `__pairs`/`__ipairs` from Lua 5.2, or `#` override. | ||
|
||
With generalized iteration, a custom container can implement its own iteration behavior so as long as code uses `for k,v in obj` iteration style, the container can | ||
be interfaced with the same way as a table. However, when the container uses integer indices, manual iteration via `#` would still not work - which is required for some | ||
more complicated algorithms, or even to simply iterate through the container backwards. | ||
|
||
Supporting `__len` would make it possible to implement a custom integer based container that exposes the same interface as a table does. | ||
|
||
## Design | ||
|
||
`#v` will call `__len` metamethod if the object is a table and the metamethod exists; the result of the metamethod will be returned if it's a number (an error will be raised otherwise). | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This seems a bit inconsistent with other metamethods.
On the other side of the argument, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think
I agree with However, not every type can be converted to a number, so Essentially the general rule would be that if it makes sense semantically to return an arbitrary type (like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The path of least resistance / maximum performance would be to preserve the number as is (which would allow the full range of IEEE values). I'm not sure truncating the number silently is very useful, and we don't really have a precedent of erroring on inexact conversions I believe. |
||
|
||
`table.` functions that implicitly compute table length, such as `table.getn`, `table.insert`, will continue using the actual table length. This is consistent with the | ||
general policy that Luau doesn't support metamethods in `table.` functions. | ||
|
||
A new function, `rawlen(v)`, will be added to the standard library; given a string or a table, it will return the length of the object without calling any metamethods. | ||
The new function has the previous behavior of `#` operator with the exception of not supporting userdata inputs, as userdata doesn't have an inherent definition of length. | ||
|
||
## Drawbacks | ||
|
||
`#` is an operator that is used frequently and as such an extra metatable check here may impact performance. However, `#` is usually called on tables without metatables, | ||
and even when it is, using the existing metamethod-absence-caching approach we use for many other metamethods a test version of the change to support `__len` shows no | ||
statistically significant difference on existing benchmark suite. This does complicate the `#` computation a little more which may affect JIT as well, but even if the | ||
table doesn't have a metatable the process of computing `#` involves a series of condition checks and as such will likely require slow paths anyway. | ||
|
||
## Alternatives | ||
|
||
Do not implement `__len`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am sure that Lua 5.1 invokes the
__len
metamethod on everything except for tables and strings, not just userdata only, akin to__add
metamethod invoking on everything except numbers and strings that can be casted to numbers.So did I miss something then with the statement "Lua 5.1 invokes
__len
only on userdata objects", but#number
errorsattempt to get length of a number value
so__len
ought to be able to invoke on numbers as this is a sign that no raw operation is performed so it falls backs to metamethod (with the exception of__tostring
which invokes on strings!)?Should
__len
also invoke on strings as well? To add to that, should__add
invoke on numbers and should__concat
invoke on strings? Should we check metamethod first then perform raw operation later? (though vanilla Lua never had this and this is likely a feature too esoteric to be added). e.g.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While you're technically correct in that a host program, or
debug.setmetatable
, can change metatable on other object types, Luau doesn't supportdebug.setmetatable
so this is written assuming a user-space program that doesn't usedebug.
API.