Conversation
This document introduces the `const` keyword for local variable bindings, explaining its syntax, semantics, and potential drawbacks.
|
The motivation is very weak and the drawbacks of breaking backwards compatibility are... bad. The motivation boils down into preventing accidental reassignment, and enabling unspecified implementation optimizations. Accidental reassignments by and large just don't happen. As for the unspecified optimizations, luau today knows if a variable is constant or not, and if it is, it applies optimizations. Checking if a binding is ever reassigned is trivial. I assume the suggestion of breaking backwards compatibility comes with the idea of a tool to allow users to migrate code. This is bad because such a tool is infeasible for the largest user of the language, roblox. The migration tool cannot be run at user's own leisure because roblox forces the latest version of luau, and the tool cannot be run automatically effectively because many users do not use roblox as a source of truth for their code. All in all, this feature opens too many difficult questions for the lack of utility it provides. |
Expand on the motivation for introducing the `const` keyword, emphasizing its role in ensuring stability for exported variables and preventing accidental reassignment. Provide examples illustrating the implications of mutable bindings in module exports.
Clarify the behavior and implications of the 'const' keyword in local declarations, including its contextual usage and potential drawbacks.
I've added more details about this to the RFC.
Instead of reserving I've covered this in the RFC also. |
|
|
||
| ```luau | ||
| const x = 1 | ||
| x = 2 -- error |
There was a problem hiding this comment.
What kind of error? Static(Parsing Error) or dynamic?
Difference is more obvious inside a function definition.
const a = 42
function foo() -- 1
a = 43
end
foo() -- 2
Should we see an error at parsing of 1 or at the call of 2?
JavaScript picks option 2, but honestly I'm not aware of advantage of it. So I would prefer option 1.
There was a problem hiding this comment.
I think it can be implemented as a parsing error but we may want a runtime error as a backstop for cases like loadstring. I would expect the error to be on the line of the actual assignment, in this case a = 43.
There was a problem hiding this comment.
As an aside JavaScript likely picks (2) because it's the line that leads to a being mutated and without it the value remains constant. For sanity, I think we should stick to erroring on a = 43 even if the code is unreachable.
There was a problem hiding this comment.
JS goes crazier here:
const a = 42;
function bar(overrides) {
with (overrides) {
let foo = function() {
console.log(a);
a = 43;
console.log(a);
}
foo();
}
}
bar({ a: 50 }); // prints 50, 43
bar({ b: 50 }); // prints 42 and throws TypeError: Assignment to constant variable.
docs/const-keyword.md
Outdated
|
|
||
| ## Drawbacks | ||
|
|
||
| - Keyword and compatibility surface: if `const` is currently used as an identifier in existing code, reserving it as a keyword can be source-breaking in some contexts. |
There was a problem hiding this comment.
As I mentioned above it is not an issue right now.
But it can be in conflict with developing proposals, like destructive assignment.
Theoretically you can have something like that in you code now:
const {a, b}
and it sould be treated as a call to const with a single param.
Assuming destructive assignment will look like
const {a, b} = x
It can be an issue
There was a problem hiding this comment.
As const variables must always be initialized const {a, b} would be invalid (and therefore treated as a function call) while const {a, b} = x remains unambiguous as this is invalid syntax today. The one downside I see if that this could become a gotcha, so we might want to consider a linter warning for const {a, b}.
There was a problem hiding this comment.
@bradsharp While const {a, b] = x is not ambiguous, it is still not reasonably parsable.
There was a problem hiding this comment.
It almost makes one want for a "Do not add destructuring syntax" RFC
| const x: number = 5 | ||
| const t: { a: number } = { a = 1 } | ||
| ``` | ||
| Multi-assignment is also supported: |
There was a problem hiding this comment.
Probably worth mentioning vararg multi assignment explicitly.
it should be fine from runtime point of view to have something like
function f() return 1 end
const a, b = f()
Because f can return multiple values in last position(same with ...). In that case b should be initialized with nil. But typechecker probably should complain here.
SPY
left a comment
There was a problem hiding this comment.
I think RFC in a good shape and can be merged.
|
There's 9 thumbs down. Shouldn't we wait for more community input? A major problem with this RFC is that it's primarily motivated on fixing a hole caused by the current semantics of the export-by-value RFC. This motivation is entirely speculative and different semantics for export-by-value can and should be chosen to solve this instead. Secondary motivation for safeguarding reassignment is also rather unpopular. Anecdotal experience from myself and others says that accidental local reassignments rarely happen, and more often we want cross-module immutability, which is taken care of with |
|
I'm more concerned on the future compatibility concerns with destructuring syntax, as in, this RFC doesn't leave a path open for destructuring syntax. |
|
Something this RFC doesn't mention is that
It would be good to clarify that attempting to rebind a
It must be said that JavaScript has been living with this restriction for years now and it's not a huge problem. |
|
Merged as a dependency for #42 |
|
I must strongly object to merging this RFC. First, I think it is dishonest to say this is needed as a "dependency" for the export-by-value RFC. This is not true and even contradicts the RFC's stated motivation:
Nothing about exporting needs this, and it shouldn't be framed like that. Second, as I've already said before, this motivation is very weak as its only substantial rationale is to close a feature gap caused by the current export-by-value semantics. This can instead be solved by adopting different export semantics that don't cause feature gaps, such as my proposed alternative semantics. Even if the team is convinced that current export semantics are the best for static optimizations (I disagree, my alternative can also obtain these optimizations without introducing const-ness), that still doesn't justify implementing |
|
Rebinding |
Hi there, folks! We're back with another weekly Luau release! # Language * Adds the `const` keyword for defining constant bindings that are statically forbidden to be reassigned to. This implements [luau-lang/rfcs#166](luau-lang/rfcs#166). * Adds a collection of new math constants to Luau's `math` library per [luau-lang/rfcs#169](luau-lang/rfcs#169). # Analysis * Fixes a class of bugs where Luau would not retain reasonable upper or lower bounds on free types, resulting in types snapping to `never` or `unknown` despite having bounds. ```luau --!strict -- `lines` will be inferred to be of `{ string }` now, and prior -- was local lines = {} table.insert(lines, table.concat({}, "")) print(table.concat(lines, "\n")) ``` ```luau --!strict -- `buttons` will be inferred to be of type `{ { a: number } }` local buttons = {} table.insert(buttons, { a = 1 }) table.insert(buttons, { a = 2, b = true }) table.insert(buttons, { a = 3 }) ``` * Disables the type error from `string.format` when called with a dynamically-determined format string (i.e. a non-literal string argument with the type `string`) in response to user feedback about it being too noisy. * Resolves an ICE that could occur when type checking curried generic functions. Fixes #2061! * Fixes false positive type errors from doing equality or inequality against `nil` when indexing from a table * In #2256, adds a state parameter to the `useratom` callback for consistency with other callbacks. # Compiler - Improves the compiler's type inference for vector component access, numerical for loops, function return types and singleton type annotations, fixing #2244 #2235 and #2255. # Native Code Generation - Fixes a bug where some operations on x86_64 would produce integers that would take up more than 32-bits when a 32-bit integer is expected. We resolve these issues by properly truncating to 32-bits in these situations. - Improves dead store elimination for conditional jumps and fastcalls arguments, improving overall native codegen performance by about 2% on average in benchmarks, with some benchmarks as high as 25%. --------- Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
This document introduces the
constkeyword for local variable bindings, explaining its syntax, semantics, and potential drawbacks.Rendered