mirror of
https://github.com/luau-lang/luau.git
synced 2024-12-14 22:21:10 +00:00
45 lines
3.2 KiB
Markdown
45 lines
3.2 KiB
Markdown
# Support `__len` metamethod for tables and `rawlen` function
|
|
|
|
**Status**: Implemented
|
|
|
|
## 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).
|
|
|
|
`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.
|
|
|
|
This is technically changing semantics of `#` when called on tables with an existing `__len` metamethod, and as such has a potential to change behavior of an existing valid program.
|
|
That said, it's unlikely that any table would have a metatable with `__len` metamethod as outside of userdata it would not anything, and this drawback is not feasible to resolve with any alternate version of the proposal.
|
|
|
|
## Alternatives
|
|
|
|
Do not implement `__len`.
|