Yuescript
Yuescript copied to clipboard
'local'-keyword has unexpected behavior
Currently, local behaves in an unexpected way. For example, this YueScript code ...
local x = "y"
... compiles to the following Lua code:
local x
x = "y"
I would appreciate it if this was changed to behave the same as it does in Lua. If someone is going out of their way to explicitly declare the variable in the same statement, then there's likely a reason for it. If someone really wanted to pre-declare the variable, they could just do that instead.
This should be partially addressed by commit 28bae6517f43c384a828df62b727517e26b3af9b. Currently, only specific cases of assignments are transpiled into the expected one-liner form.
For example, the following cases are handled correctly:
-- supported cases
local x = 1
local y, z = "sds", false
local n, m = func!
Which transpile to:
local x = 1
local y, z = "sds", false
local n, m = func()
However, there are still cases where this behavior is not yet supported, such as:
-- unsupported cases
local a = tb?\func!
local b = item\end 123
local c = if true then 1
These are transpiled into the following Lua code:
local a
do
local _obj_0 = tb
if _obj_0 ~= nil then
a = _obj_0:func()
end
end
local b
local _call_0 = item
b = _call_0["end"](_call_0, 123)
local c
if true then
c = 1
end
In these unsupported cases, the variable declaration and assignment are still split due to the complexity of the expressions.
This is a sensible solution. However, for the sake of consistency, how about doing the following:
Given this YueScript code:
local x = if x then "Yes" else "No"
Generate this:
local x = x
if x then
x = "Yes"
else
x = "No"
end
The compiler could observe if the expression that's being assigned to x is itself mentioning x, and if it does (like the if x then ... above), it inserts local x = x instead of just local x.
local b = item\end 123
Still not clear why this is unsupported, and why it ever needs extra _call_0 variable.
local b = item\end 123
Still not clear why this is unsupported, and why it ever needs extra _call_0 variable.
In this code, the compiler interprets item as a global variable. Accessing a global variable can have side effects, so the extra _call_0 is introduced to ensure the access happens only once.
So that the following code should be handled as one-liner, while it is not yet done.
item = {}
local b = item\end 123
@SkyyySi local x = x is not acceptable because the x in the right may also trigger a unintended global accessing. If you happen to alter a global environment with some like this, a function will be called unintentionally.
-- You can try the code in YueScript 0.27.1
_ENV = <index>: (key) => print "accessing a global named #{key}"
local x = x
But if your expression explicitly asks to accesses a variable x (by using its name somewhere), how could it be an accidental global read? The only cases in which a programmer didn't mean to do that are:
- The code is bugged and wasn't meant to reference
xin the first place. - They meant to just use
nilinstead ofx(since, right now,xcan only ever benilbefore the expression is completely evaluated and assigned tox).
I don't think either of those are compelling arguments.
There are also genuine reasons why someone might want this to happen. Primarily, that would be caching global variables. It's a common thing in Lua to write something like this:
local assert = assert
local pcall = pcall
local string = string
--- You get the idea
Besides that: If someone actually does something as horrible as triggering side-effects whenever the global environment is accessed, then they deserve whatever happens to them ;)
I think the argument is that you wouldn't expect the side-effect twice when you only see it once in your code. YueScript caches it so the side effect is only executed once.
I think the argument is that you wouldn't expect the side-effect twice when you only see it once in your code. YueScript caches it so the side effect is only executed once.
- The side effect isn't triggered twice when doing
local x = xin Lua. It runs once, as one would expect. - It currently doesn't get trigged at all in YueScript, since it is compiling to
local x x = x - YueScript doesn't do any caching in this recard. It does pre-declare the variable, but that's also the very thing that I was hoping to see changed when opening this issue.
I know how assignments work. I'm talking about a case where the variable gets cached, like the case posted earlier in this very thread:
local b
local _call_0 = item
b = _call_0["end"](_call_0, 123)
As long as it's compiled this way, it has to cache item to avoid double side effects
In those case, falling back to an IIFE is always an option:
local b = ((function(_call_0)
return _call_0["end"](_call_0, 123)
end)(item))
Or, alternatively, an additional temporary variable can be generated by the compiler:
local _b_temp_0
local _call_0 = item
_b_temp_0 = _call_0["end"](_call_0, 123)
local b = _b_temp_0
Both would preserve the intended behaviour with function environments.