Alternate Export-by-Value Semantics#179
Alternate Export-by-Value Semantics#179MagmaBurnsV wants to merge 3 commits intoluau-lang:masterfrom
Conversation
|
The following cases are currently unclear in their behaviour, root causing being a lack of definition for the interaction of export statements with existing variables. I.e. you currently only explain that two exports cannot share an identifier. -- This is mentioned in alternatives, but not in body
local function foo() end
export foo -- syntax error?
local bar = 0
export bar = 1 -- syntax error?
print(bar) -- 0 or 1?
fruit = "apple" -- global
export fruit -- shadowing or reference?
fruit = "pear" -- is fruit now local?
print(fruit) -- pear?
print(_G.fruit) -- apple?
export animal
animal = "dog"
local animal = "cat" -- syntax error?
print(animal) -- cat or dog? |
|
I've clarified that the restriction only applies to exported variables, and exports follow conventional shadowing rules in regard to non-exported variables, so your example would desugar into: local _EXP = {}
local function foo() end
_EXP.foo = nil
local bar = 0
_EXP.bar = 1
print(_EXP.bar) -- 1
fruit = "apple"
_EXP.fruit = nil
_EXP.fruit = "pear"
print(_EXP.fruit) -- pear
print(_G.fruit) -- apple
_EXP.animal = nil
_EXP.animal = "dog"
local animal = "cat"
print(animal) -- cat
-- {foo = nil, bar = 1, fruit = "pear", animal = "dog"}
return table.freeze(_EXP)Or alternatively if optimized into locals by the compiler: local function foo() end
local foo
local bar = 0
local bar = 1
print(bar)
fruit = "apple"
local fruit
fruit = "pear"
print(fruit)
print(_G.fruit)
local _animal = nil
_animal = "dog"
local animal = "cat"
print(animal)
return table.freeze({foo = foo, bar = bar, fruit = fruit, animal = _animal})(Uninitialized exports looking like shorthand exports may be a good reason not to support them. The only reason I allowed them in the design was to obtain parity with how the |
While this is true, it should be really easy to detect most cases through linting because export follows existing shadowing rules. In fact the same lint would catch another common error which could result from mistypes or copy paste errors. I would propose "Uninitiated export is never assigned to" as a lint rule. local function foo() end
export foo -- Uninitiated export is never assigned to
-- The above also catches the incorrect assumption of exporting locals
-- A common lint rule "no shadowing" would also catch this error
export bar -- No lint because bar is used in an assignment
if condition then
bar = function() end
end
export f, g -- No lint because f and g is used in an assignment
function f() g() end
function g() f() end
export user -- Uninitiated export is never assigned to
User = {
}
export fruit = nil -- Uninitiated export is never assigned to
-- Although assigned nil explictly, there would never be a reason to export nilLint rules are not necessarily part of the langauge, but the possibilty of it being easily lintable helps to remove confussion for users and maintains feature parity with the local keyword as you described. |
This proposes alternative semantics for #42, which treats exports as syntax sugar for assigning keys to an export table before sealing.
This allows reassigning exported values before the module is returned, solving the original RFC's issues with mutually dependent functions and shorthand aliasing. Furthermore, it introduces export semantics that are already representable in the language, dismissing the need for introducing
const-ness to the language.Rendered