Table Index Matching in Luau
tracked
Ruby Maelstrom
Currently, when scripting in Luau, indexing of tables does not match between Luau functions and ll. functions.
For example, the following return the same values:
str = ll.List2String(List,2)
and
str = List[3]
This isn't exactly a bug, both are functioning 'correctly', but it's going to cause some issues. Is there a reasonable solution to this?
Log In
H
Harold Linden
We've thought a little about this, and we think we can get a nice compromise by making versions of the functions that add
1
to indices >=0
in the binding layer, and put functions that take the traditional 0-based indices in llcompat.
or something similar. Passing an index of 0
to the new functions would either be an error or equivalent to passing 1
, not sure which makes the most sense yet. Does that sound reasonable?We've gone through and tagged all parameters / return values that are "index-like" such as the parameters for
llDetected*()
and llList2*()
so that we can easily do those fixups in the binding layer, but we'll wait until the integer
mess is cleaned up to actually make those changes.WolfGang Senizen
Harold Linden
Another option instead of error or 1, it could return nil, like doing [0] would in lua?
That way the only difference to lua, is the added support of negative indexes.
Would still possibly be a bit of a footgun for new users, but i think less than an error, and less "magic" than
0 = 1 ????
In general dissuading use of the list funcs is probably the move though.
H
Harold Linden
WolfGang Senizen:
Makes sense, I don't feel great about silently doing nothing, but that appears to be idiomatic Lua behavior.
My concern's mainly around the non-list functions that take indices like
llDetected*()
and llSubStringIndex()
, but I suppose it's fine for those to also return nil
.WolfGang Senizen
Harold Linden
I just had a thought though, maybe nil is the wrong move for ll functions, this would generally imply returning nil for anything that is out of bounds...
No LL function currently returns multiple types
so maybe rather than nil,
ll.DetectedKey(0)
should return NULL_KEY and ll.DetectedName(0)
should return ""
etc... same for list funcs probably?Sorry xD
WolfGang Senizen
Harold Linden
Do you have a good list of all functions for LSL that are index based?
Or would you like one written up for you?
Because there are Several outside of llDetected
and llList2
H
Harold Linden
WolfGang Senizen
Yep, we have one. I attached the generated list of functions' args and returns that we think handle index-like values, but let us know if you think there are any incorrect or missing!
Do note that face numbers will still be zero-based, since that's how the viewer reports them, and we don't want to create too much confusion there. Still on the fence about whether to change
llJson(Get/Set)Value()
's array index specifiers, that's a bit of a weird case.WolfGang Senizen
Harold Linden
Seems mostly complete only these I can find missing.
llInsertString
llListInsertList
llListReplaceList
llOrd
SuzannaLinn Resident
Harold Linden
Other functions with 0-based parameters:
llListSortStrided
: stride_indexllLinksetDataFindKeys
: startllLinksetDataListKeys
: startAbout the naming of the adapted functions, I would prefer functions coming from LSL to have the same name and behaviour, and adapted functions to have a different name, perhaps in another library (or even in another library with the same name).
I think that "if it has the same name, it works the same" would be easier for beginner scripters moving from LSL and for scripters using both languages.
H
Harold Linden
WolfGang Senizen
Thanks, we've added these to the list
H
Harold Linden
SuzannaLinn Resident
These were added to the list too, thanks!
On balance, we think that consistently using the same indexing scheme everywhere is less confusing for newer scripters and existing scripters alike, as otherwise you need to maintain the context of "ah yes, this function is from LSL so it behaves differently than other functions", and I have to write my index-iterating loops differently. This would get doubly-confusing when mixing
ll.*()
calls and Lua-native calls when operating on the same data.If you really want 0-based indices everywhere in your script's
ll.*()
function calls, it should be as easy as doing ll = llcompat
near the top of your script once we've got this rolled out.SuzannaLinn Resident
Harold Linden
Ah, it's good, so
llcompat
will have all the LSL functions as they are, and ll
will be the library evolving into lualizationH
Harold Linden
WolfGang Senizen
We've done some thinking about this internally. One thing we're concerned about is making these 1-indexed APIs where people aren't aware they're not 0-indexed and accidentally write buggy code. That code sort of works because we return a default value, but it's silently doing something that they don't expect it to.
An error is an obvious early warning of "hey, you're misusing this API and you should fix your code" and might present less of a footgun.
One thing we considered was whether these functions actually take indices from untrusted sources, and I think in the general case (
llListStatistics()
, ll.Detected*()
, etc.) the answer is no, so you shouldn't have to worry about wrapping them in pcall()
in the general case.Is there a case we might be missing here that would tilt things in favor of using a default value?
Signal Linden
tracked
This one's tricky. We'd like to keep most of these functions the way they are, because it makes it easier to do mechanical LSL source-> SLua source translation so people can rewrite their scripts as idiomatic SLua piecemeal. We have such a source->source translator in the works, it'll likely be released sometime after the alpha phase when we open-source the compiler and VM.
I think for most of these functions (like
llList2Key()
, etc, the answer is there's no good reason to use them in SLua, because their auto-casting behavior was always flaky, and explicit casting like uuid(some_tab[idx])
is less ugly.For functions that have no direct Luau equivalent, like
llListStatistics()
we'll eventually write proper Luau equivalents. Same goes for llDetected*()
and things that expect 0-based indices.Wulfie Reanimator
Can confirm. Mixing LSL calls and Lua loops is sure to cause headaches for years to come like this. As an example, here's a script that simply outputs keys of avatars in the region.
local AGENT_LIST_REGION = 4
local agents = ll.GetAgentList(AGENT_LIST_REGION, {})
ll.OwnerSay("Loop from 0 to list length")
for i=0, #agents, 1 do
ll.OwnerSay(`{i} {ll.List2Key(agents, i)}`)
end
-- 0 779e1d56-5500-4e22-940a-cd7b5adddbe0
-- 1
ll.OwnerSay("Loop by key-value pairs:")
for k,v in pairs(agents) do
ll.OwnerSay(`{k} {ll.List2Key(agents, k)}`)
end
-- 1
ll.OwnerSay("Loop by key-value pairs with Lua indexing:")
for k,v in pairs(agents) do
ll.OwnerSay(`{k} {agents[k]}`)
end
-- 1 779e1d56-5500-4e22-940a-cd7b5adddbe0