Godot version:
24e1039
Issue description:
Dictionaries that happen to be constant will have their contents shared across all instances of a script.
Isn't that what a constant is supposed to be ? Like the same all the time ?
@groud A constant variable means that their value never changes, in this case, it means that it will always be a dictionary. But the dictionary's values can still be changed.
Arrays for example, can be made constant and their contents can be changed, and they will still be unique across scripts.
A constant variable means that their value never changes, in this case, it means that it will always be a dictionary. But the dictionary's values can still be changed.
I am not sure this is intended though. A constant is not only about the type of the variable, but also about its content. Constant dicts should not be modifiable.
I tried toying with this; const arrays can be appended to willy-nilly and all objects shared the same array, const dicts did not accept adding new key:values, but I could erase existing values (and was also shared by all objects), and a const Color did not let me modify it whatsoever. It seems that any operation which attempts to reassign the const will fail, but any operation that can modify it internally works fine.
Even C# does not have a language-level concept of constant arrays or dictionaries, at best it is declared with readonly (which means it's a member variable, different from const) or static but in any case it can still be modified.
Either we should detect in the compiler that code is calling non-const functionality of dictionaries, arrays or objects (which may require const-awareness of functions in GDScript, especially for the latter Oo), or we should introduce a wrapper like C# does such as ReadOnlyDictionary or ReadOnlyArray.
Not sure about objects though... considering that even constructors of objects can be non-deterministic they should not be allowed (unless they are already?)
@Zylann I think for objects it is acceptable. If I am not mistaking, storing an object in a variable is more like storing a reference to this object. However, I don't think this is the case for arrays and dictionaries, that why I believe their modification should be forbidden.
Just gonna say that I've used const Dictionaries as a way of storing variables in tool script classes, that way I can have globally accessible static data associated with a class at editor time (no user-defined singletons / autoloads in the editor context).
I'm not opposed to making const Dictionaries truly un-editable (if that can be done) since that makes more sense, but there is currently no legit workaround for editor-time singletons. If I add an EditorPlugin, the plugin still has to be passed as a reference to all the tools within the plugin. I can't simply add global variables to GDScript at my whim, nor is it possible to have editor-only classes that don't occupy the runtime global namespace.
...I should probably make a separate proposal for all this, but it should be noted that changing this would affect these circumstances.
@willnationsdev in the meantime, if you fear your hack will get fixed in the future, you can exploit meta properties by loading the script resource of your plugin, which does the same thing legit ;) (in any case, even with const, careful not to create a ref cycle).
@Zylann oh yeah, that's right. Man, I cannot tell you the number of times I've forgotten that meta properties are even a thing.
Using const as a way to have class variable sharing between script instances is really useful since gdscript does not syntactically support something like "static" in C#. Of course, you can use the get_script().set_meta("name", "value") and get_script().get_meta("name") to achieve the same functionality but it is more verbose than having a "const statics : Dictionary = {}" and then calling statics.get("name") and statics["name"] = value. I really do hope this issue is fixed only after gdscript starts supporting "static" members like in C#.
The issue with const dictionary is that it's a hack which is not guaranteed to remain working. I mean, just reading the code, it is a non-sense to be able to modify.
The advantage of the script meta is that it's not a hack and shows what is actually happening.
Still, ensuring what is in a const will never be modified in any way in a dynamic language is non trivial I believe. Which is probably why this issue is still open.
Or, we could accept and document the fact that const actually means non-reassignable value stored in script resource, and deal with whatever limitation this has (more akin to static readonly in C#), which then makes it a bit weird to not have the non-readonly version.
If people actually want static, mutable variables exactly like C# which remain alive even if the script is unloaded, then that would be something else with another issue.
Since SceneTree inherits from MainLoop which inherits from Object, we can already achieve "static, mutable variables exactly like C# which remain alive even if the script is unloaded" using get_tree().set_meta("className.variableName", "value") and get_tree().get_meta("className.variableName"), or if you prefer you could do get_tree().set_meta("className", {"variableName":"value"}) and get_tree().get_meta("className")["variableName"].
I don't know enough about mono or microsoft clr to say how it handles a class lifetime (instead of an object lifetime) but I think all static variable from a class not referenced from other objects have its lifetime tied to the class lifetime itself, and I think unloading just one class should not be possible on mono/clr. But I do know that on mono/clr you can create appdomains which can be loaded and unloaded, and all classes loaded under a given appdomain will be unloaded for that appdomain only when the appdomain is unloaded.
Either way both functionalities of static class variables and global variables can already be achieved using either the script meta or the scene tree meta. I think the discussion should be about supporting static variables either syntactically or with functions. We could have a method on object for get_static and set_static which would just be a wrapper on get_script().get_meta() and get_script().set_meta(), and maybe another method get_global and set_global which would just be a wrapper on get_tree().get_meta() and get_tree().set_meta().