Godot: Immutable variables

Created on 14 Nov 2018  路  24Comments  路  Source: godotengine/godot

A middle term between a normal var, and a const, that once set, would remain forever with the given value. A good syntax could be using a word short as "var", like "let":

let var_name # Starts 'null', can still have something assigned to it.
let var_name = value # A value has been given, now its value cannot be changed anymore.

One of the main uses of immutable variables would be pairing it together with onready, when creating variables to keep stuff like nodes and certain references:
onready let var_name = $Node/SubNode

archived feature proposal gdscript

Most helpful comment

I'm in favor of let, as it's harder to confuse compared to val (which looks close to var at a quick glance). Some languages like Nim already use let for immutable variables. However, there's the downside that JavaScript users may be confused by this keyword, since let variables are mutable there.

Edit: Swift also uses the let keyword for immutable variables (and var for mutable ones).

All 24 comments

#set once with whatever is in the inspector
export let var_name = 1.0

Though personally, I prefer val

@somnivore val is too close to var, the syntax for something like this should be very different, so you can see it immediately and avoid silly mistakes.

C# has it as readonly Type name, which can then be assigned only from the constructor. Maybe it can be an annotation (#20318)?

what about using ref ?

@blain1972 Most languages that have a declaration of 'ref' use 'ref' as the mutable declaration where the default is immutable. Using it opposite in Godot sounds rife for confusion.

Most commonly let/val/const are used for immutable declarations among many languages, however those don't allow rebinding from an 'empty' state, most commonly such a thing would also be marked as lazy (such as let lazy blah = ...) with first access populating the value.

I am in favor of a let/val keyword. This is a pretty standard language feature. I know gdscript is similar to python, but protecting data is important, especially in use-cases like creating plugins where a developer might want to restrict a user from accessing something that could break functionality.

I'm in favor of val as well.

@YeldhamDev var/val is used in Kotlin, no real issue.

C# has a keyword called readonly to do something similar. Despite the name, it can be written once, but only either inline (like const) or during constructors, so it's not write once from anywhere. Java's final keyword does the same thing.

val seems too close to var, and I don't like let personally. Aside from let, here's some ideas:

  • What if const thing with no value was allowed to be written once? I don't know if GDScript constants are evaluated compile-time like C# constants are (it doesn't say), but this could be an easy way to solve the problem with no additional keywords.

  • Re-use a keyword from another language, such as readonly or final.

  • Make it a modifier to var, such as writeonce var.

Why don't you like let? By "personally", do you mean that it's a personal dislike and it shouldn't affect whether it's chosen as the keyword?

I'm in favor of let, as it's harder to confuse compared to val (which looks close to var at a quick glance). Some languages like Nim already use let for immutable variables. However, there's the downside that JavaScript users may be confused by this keyword, since let variables are mutable there.

Edit: Swift also uses the let keyword for immutable variables (and var for mutable ones).

I also support let it's hard to confuse it with anything else. It would also solve my issue of initiating some variables in parent class and allowing children to edit them This would allow for autocomplete of functions in parent class that use those variables and assign value to them in children only so the actual objects in the game that use them will have them working fine.

Coming to GDScript after doing a coding session in Dart, which has a similar concept of final variables, I really miss them. lets or vals are really useful writing data classes that are often desired immutable. Would like to have immutable vars featured in GDScript as well!

I programmed in Scala for quite some time and there are zero practical issues with var + val. If you try reassigning val compiler (IDE) will quickly tell you so. I find using let for constants unfortunate, because many people coming from JS/TS will find it confusing. Sure, there are languages like Haskell where it's used as an immutable binding, but let's be honest, how many people are coming from these significantly less popular languages?

Honestly, I find const in GDS confusing, because despite being it a dynamically typed language, it behaves like a macro or similar from "lower" statically typed languages. I would be for upgrading const to work same as const in JS - it can be filled at runtime (e.g. onready const a:= $A or const a:= A.new()), but cannot be re-assigned. Please note that its value is not necessarily immutable, only (I don't know how it's called in GDS) the reference/binding/pointer is.

The value is immutable. Godot has non-pointer (I don't like calling them references because it means something different in C++) types like int and float, where their values aren't pointers. It's just that for "array values" or "dict values" the value is a pointer, not the array or dict itself.

What's the difference between let and const? They seem like they're supposed to accomplish the same thing (preventing modification of a variable's value once it's created), so I don't really understand why there'd be two keywords for them.

I speak from a position of very little programming experience, so sorry if this seems like a stupid question!

@WARIO-MDMA const needs a value immediately when created. The idea is for let to allow giving it a value later, and then lock changes.

@YeldhamDev I see. Thank you for clearing that up.

@YeldhamDev This may not necessarily be the case. We could still require let variables to be initialized on assignment, which should make the GDScript parsing logic significantly simpler.

An immutable variable declared via let would have to be initialized immediately and then would never change within that scope. The difference from const is that it can be created within a scope at runtime. It's not constant (ie always the same value) - just immutable (ie it's not able to be reassigned after declaration).

initialized on assignment

How would that work with the usage example I gave on the first post, about using it with onready?

It wouldn't work. Maybe modifying let with onready should be a special case that allows it to start as null and change, since it's clearly visible from the syntax to the user that it will be modified on ready (also easily enforceable because there's no separate assignment statement to check the correctness of).

I don't think it's too much of a problem UX-wise to allow later definition for other let declarations, since you can look at the declaration and tell that it may change from null because it is declared without a definition. It is however impossible at compile / semantic checking time to determine whether a variable will ever be assigned to multiple times. I think the feature will be much more useful for avoiding bugs if single assignment can be checked statically.

Maybe you could use static checks for lets with definition or with onready, and dynamic checks for let statements in function bodies with no definition, but this would create an inconsistency that might trip up users. It is possible also to try to conservatively detect safe situations with multiple assignment statements (for example, in separate branches of a conditional statement), but depending on how complex this criteria is, it might not be clear to users when it is allowed.

When GDScript becomes more performant it may also be a performance concern that you have to dynamically perform a null check, but at the moment it is not an issue because of the high overhead interpreting bytecode. Just make sure the dynamic check and the assignment are part of the same bytecode instruction so that you don't double that overhead.

Closing in favor of https://github.com/godotengine/godot-proposals/issues/820, as feature proposals are now tracked in the Godot proposals repository.

Closing in favor of #23695, as feature proposals are now tracked in the Godot proposals repository.

But ... #23695 is this issue? 馃え

@mnn Sorry, I fixed it.

Was this page helpful?
0 / 5 - 0 ratings