Describe the project you are working on:
The GDScript compiler.
Describe the problem or limitation you are having in your project:
The current way to define setters and getters are to use the setget syntax and name the functions to use as setter and getter.
There are a few problems with such syntax:
var x setget ,get_xexport and `onready will move to annotations which improves this but not completely).set_x() function or just set the x field with the automatic setter.Describe the feature / enhancement and how it helps to overcome the problem or limitation:
Implement a syntax for actual properties instead of setget.
Moving setget to annotations would solve (2) but not the others (although (5) is independent of syntax, we could fix it while keeping the keyword).
With properties we can keep a cohese block with variable declaration together with setter and getter, without exposing a extra functions.
Describe how your proposal will work, with code, pseudocode, mockups, and/or diagrams:
This is one possible way of declaring a property:
var milliseconds: int = 0
var seconds: int:
get:
return milliseconds / 1000
set(value):
milliseconds = value * 1000
So the getter and setter are included in the variable declaration as extra blocks, so no function declaration needed. Type specifiers are not needed also since the variable is typed and the setter/getter will enforce the same type.
Alternatively, we could use a new property keyword, but I don't feel it's needed. It's visually clear that the blocks belong to the variable.
You can also set only the getter for a read-only property:
var start: int = 0
var end: int = 0
var interval: int:
get:
return end - start
To avoid creating external variables for this, you can use a generated member variable inside the setter and getter:
signal changed(new_value)
var warns_when_changed = "some value":
get:
return warns_when_changed
set(value):
changed.emit(value)
warns_when_changed = value
Edit: I think the proposal I made in https://github.com/godotengine/godot-proposals/issues/844#issuecomment-630269749 could be a compromise for people who still wants actual functions as setter/getter:
So, I guess we can provide a special syntax if you still want a separate function:
var my_prop:
get = get_my_prop, set = set_my_prop
func get_my_prop():
return my_prop
func set_my_prop(value):
my_prop = value
If this enhancement will not be used often, can it be worked around with a few lines of script?:
It will be used often and the current way makes this more complex to write and maintain.
Is there a reason why this should be core and not an add-on in the asset library?:
It cannot be implemented as an addon.
You have to separate the functions from the variables
I should disagree. This is absolutely not a problem. It allows keep code in separate blocks: first variables and then functions. Which is great as it keeps code clean and as readable as possible.
But suggested way to declare properties also looks good...
I should disagree. This is absolutely not a problem. It allows keep code in separate blocks: first variables and then functions. Which is great as it keeps code clean and as readable as possible.
But suggested way to declare properties also looks good...
Note that I mean specifically the getters and setters. If you see a setget in a variable, you need to scroll down and find where the function is defined to see what it does.
With the syntax proposed here, I would split the blocks as: variables, properties, then functions.
Why not use annotations for that ? I think I would prefer something like:
@getter(get_myproperty)
@setter(set_myproperty)
var myproperty
Why not use annotations for that ? I think I would prefer something like:
@getter(get_myproperty) @setter(set_myproperty) var myproperty
I mentioned annotation as an alternative, but it doesn't solve points (3) and (4). It also would make difficult to solve (5) since the setter/getter needs to access the variable without the setter/getter, but if it's a regular function then the conditions for direct access get more complex.
For me, (3) is not really a problem as in most cases, the setter and getter will have very name very close to the variable name, so I don't think it is that confusing. For point (4), I am not sure there is way to solve this problem, unless by adding more explicit keywords maybe, but that would be weird.
Regarding point (5), I think we can call the getter/setter using self.myproperty no ? This kind of make sense to me I believe (though it may be cause problems when you use self to bypass a scope-limited variable).
Regarding point (5), I think we can call the getter/setter using
self.mypropertyno ? This kind of make sense to me I believe (though it may be cause problems when you use self to bypass a scope-limited variable).
Yes, it is possible to use self.myproperty, but then you have to be conscious of it all the time to avoid mistakes. If you refactor the code to a getter or setter, you'll need to check every place to add the self. notation. And if you are removing them, you need to remove the self. as well, otherwise you are losing in performance for no gain.
I've dealt with many Godot beginners who were baffled by this behavior and found it confusing. So no matter what syntax we pick, I believe (5) must be resolved.
For annotation or other syntax, it's important to keep in mind the whole context when evaluating. Of course two/three lines of notation may seem cleaner but you need to take in account that those are defined somewhere.
So an annotation syntax would look more like this:
var milliseconds: int = 0
@getter(get_seconds)
@setter(set_seconds)
var seconds: int
# [...] a bunch of other code here
func get_seconds():
return milliseconds / 1000
func set_seconds(value):
milliseconds = value * 1000
Which in my view requires more boilerplate code than it needs, by exposing two functions that most likely won't be used, because why would you need to use them if using the property is simpler? A similar happen with the engine native getters and setters when properties were introduced (though they still work, they're not exposed in the documentation).
This example code also can illustrate the issue (5). Anywhere in the class if you use self.seconds it has a different meaning than simply seconds. If you forget this detail you can be surprised by a subtle bug that you can easily miss. Especially because it's more common to not use self for this.
And you also now have a member variable that you don't ever use (which could be stripped out by a property syntax, recognizing it's not used).
var start: int = 0
var end: int = 0
var interval: int:
get:
return end - start
If that is a read-only property then how to define a variable with custom getter but not setter, that allows other scripts to do a direct assignment like it is currently possible?
otherNode.interval = 42
Above code does direct assignment if no setter is provided for interval.
Why have set(value):? There will always be one argument. Why not make value implicit, such that the line looks like set: and value is still defined? This is how C# does it.
if setget is seperated into set: and get: I would really like to be able to use one liners...
export(float) var my_value set: set_my_value, get: get_my_value
it would also be nice to beable to switch order of set and get
export(float) var my_value get: get_my_value,set: set_my_value
Why not make
valueimplicit, such that the line looks likeset:andvalueis still defined?
As for me I always name argument in set functions in different ways. Not always value.
it would also be nice to beable to switch order of set and get
Yes, I agree with it.
Why have
set(value):? There will always be one argument. Why not makevalueimplicit, such that the line looks likeset:andvalueis still defined? This is how C# does it.
Because
Explicit is better than implicit.
What C# does should not matter for GDScript design given it was added for people who didn't want to learn/use GDScript. I'm for explicit value since that's what Python does.
@hilfazer
If that is a read-only property then how to define a variable with custom getter but not setter, that allows other scripts to do a direct assignment like it is currently possible?
otherNode.interval = 42
Above code does direct assignment if no setter is provided forinterval.
That's something to discuss. We can either make it read-only if there's no setter, or use direct assignment (then use a @readonly annotation for this).
@aaronfranke
Why have
set(value):? There will always be one argument. Why not makevalueimplicit, such that the line looks likeset:andvalueis still defined? This is how C# does it.
Because I despise implicit identifiers in scope. Say your class has a property named value, how do you define a setter for it?
var value:
set:
self.value = value
In this case, self.value will call the setter recursively, which is bad. Sure we can detect this pattern and compile it correctly, but I prefer to simply error out.
Also, naming it something else can make your code clearer to read.
Explicit is better than implicit.
I think this saying is overused. But I also think that removing magic is better for understanding.
@Shadowblitz16
if setget is seperated into
set:andget:I would really like to be able to use one liners...export(float) var my_value set: set_my_value, get: get_my_value
This is a different idea. In my proposal you won't need to define the functions elsewhere, so this is notation is not really useful. I'm not "splitting setget", I'm defining a new way of doing setters and getters.
If we went that route I'd prefer the annotation syntax. Otherwise it wouldn't solve point (2) in my original post.
it would also be nice to beable to switch order of set and get
export(float) var my_value get: get_my_value,set: set_my_value
As said, this notation won't work, but in my proposal order doesn't matter.
@vnen I really don't know about this.
I dislike the idea of having to explicitly define value and I think functions make the code look alot nicer.
If this has to do with people calling the getter and setter functions then I think some sort of equivalent of the private keyword should be used.
With this change, if I want to directly set a variable inside the class that defined the variable (without calling its setter function), would I still be able to do that?
(Like using my_var = value instead of self.my_var = value)
If not, it would cause a stack overflow in situations where 2 variables have setters which set the values of each other.
For example, say I have a turn-based game.
Whenever I change the current player, I also need to change the current opponent.
By _not_ using self when setting the variable, I can do it like this with no problem:
const PlayerName := {A = "Player A", B = "Player B"}
const Key := {Player = "Player", Opponent = "Opponent"}
var c_player_name: String = PlayerName.A setget set_c_player_name
var c_opponent_name: String = PlayerName.B setget set_c_opponent_name
func set_c_player_name(__player_name: String):
set_c_player_or_c_opponent_name(Key.Player, __player_name)
func set_c_opponent_name(__opponent_name: String):
set_c_player_or_c_opponent_name(Key.Opponent, __opponent_name)
func set_c_player_or_c_opponent_name(__key: String, __player_name: String):
assert(PlayerName.values() == [PlayerName.A, PlayerName.B])
assert(PlayerName.values().has(__player_name))
assert(Key.values() == [Key.Player, Key.Opponent])
assert(Key.values().has(__key))
var __player_name_index: int = PlayerName.values().find(__player_name)
var __other_name_index: int = abs(__player_name_index - 1)
var __other_player_name: String = PlayerName.values()[__other_name_index]
if __key == Key.Player:
c_player_name = __player_name
c_opponent_name = __other_player_name
else:
c_player_name = __other_player_name
c_opponent_name = __player_name
assert(c_player_name != c_opponent_name)
If not, maybe a new function/annotation or something would be needed, like:
setd(<var name>, value)
(In this case, setd would stand for "_set directly_", and would not call the setter function.)
@vnen Because I despise implicit identifiers in scope. Say your class has a property named
value, how do you define a setter for it?
Simple, you disallow having a property named value. The name is too generic to be clear, and you already can't declare properties named var or func or extends.
@Shadowblitz16
I dislike the idea of having to explicitly define value and I think functions make the code look alot nicer.
I don't understand because if you are using functions, then you definitely need to define thevalueparameter.
- annotation should not be used for everything, it makes things more confusing and code less readable
I agree they shouldn't be used for everything (and they aren't) but in this case I find much more confusing and less readable to have all in the same line. Longer lines are always harder to read. And annotations can go in the same line if you so prefer.
- defining logic inside the property declaration is fine but I would still like to be able to use functions for long setters and getter logic.
Well, with my proposal it's not impossible to do it:
var my_prop:
get: return get_my_prop()
set(value): set_my_prop(value)
The only thing is that in this you would need a backing field for use inside the setter and getter, to avoid an infinite loop.
So, I guess we can provide a special syntax if you still want a separate function:
var my_prop:
get = get_my_prop, set = set_my_prop
Order doesn't matter, and both are optional. With this we can detect the pattern and allow direct access inside the functions (though I still don't like providing direct access far from the declaration, but it's a compromise I could accept).
@Error7Studios
With this change, if I want to directly set a variable inside the class that defined the variable (without calling its setter function), would I still be able to do that?
(Like usingmy_var = valueinstead ofself.my_var = value)
No. That's the problem (5) that I mentioned in the original post and it is a problem I intend to fix. If you need direct access in this case, you can use a backing field.
If not, maybe a new function/annotation or something would be needed, like:
setd(<var name>, value)
I guess we could have a way to edit the field directly, but I'm not convinced yet that it's really needed.
@aaronfranke
@vnen Because I despise implicit identifiers in scope. Say your class has a property named
value, how do you define a setter for it?Simple, you disallow having a property named
value. The name is too generic to be clear, and you already can't declare properties namedvarorfuncorextends.
That is a super bad name to disallow. Is very unlikely you'll name something var or func (even extends. And if we are to allow using actual functions as setter, you're likely to use value as the parameter.
What we could do, to settle on a compromise, is make the setter parameter optional and assume it's value when not present. I still don't like implicit identifiers, but I guess in this case is passable.
I'd really prefer it if you don't introduce two ways of doing the same thing unless really necessary, if they want the C#-ite semantics just use C#, being explicit is good and building in exceptions _already_ seems like a poor idea.
Since it functionally fulfills everyones requirements with the original (explicit, simple) proposal and people can just call their function in the setter/getter if they want I don't see the problem. (i think $ in gdscript already confuses people enough though, so i may be extreme)
... Just my two cents anyways :eyes:
Problem (4) is not significant because:

x.property is most often equivalent to x.get_property(), and x.property = value to x.set_property(value). GDScript design must be consistent with Godot design.
@dalexeev honestly I would remove that from the docs. I would even make those methods inaccessible from scripting. They are not needed, exactly because you can simply set those as properties. I don't see one reason to use the method instead of the property.
I don't see one reason to use the method instead of the property.
Non-obvious use cases are possible. For example, using funcref. Something like:
var action = funcref(npc, "set_position")
Non-obvious use cases are possible. For example, using
funcref. Something like:var action = funcref(npc, "set_position")
Well, you can create your function if you really need this. Especially when lambdas are added it will likely be simple to do it in the same line.
(5) Setters and getters aren't called if you use the variables inside the same class
(5) isn't an issue and should not be touched, instead, tutorials should notify that setters and getters are triggered _when attributes are accessed by scripts outside of the class_. It is very common to access a variable from inside without wanting to trigger a method for outside access. The very-known objective of setters and getters is hiding the complexity from outside of the class, not from inside. This "difficulty" beginners might experience is part of understanding scripting fundamentals, the tutorial should explain it, we do not need to reinvent a new weird behavior that would make people even more confused.
Yes, it is possible to use self.myproperty (to access without calling the getter), but then you have to be conscious of it all the time to avoid mistakes.
Yeah, you have to be conscious of what you're typing, there are two ways of accessing, and you should know which one of those two you want to use. People need to understand that self has a meaning, and it is a _feature_, you shouldn't write self without being conscious of what it means.
Besides that, I like this change 👍, easy to parse and looks like a very useful shortcut for typing setters and getters. If this is to be implemented, we should also discuss setget's future and usefulness.
You have to separate the functions from the variables, so they look disjoint while they should be a cohesive block.
I totally agree with this, there's no point in separating variables from it's setters and getters.
@vnen
I still don't think value should be explicit.
we all know it passes a value and only 1
var my_prop:
get: return get_my_prop()
set(value): set_my_prop(value)
should be..
var my_prop:
get: return get_my_prop()
set: set_my_prop(value)
also i like the idea of doing something like...
var my_prop:
get := get_my_prop
set := set_my_prop
or something similar.
its basically C# linq which looks nice
@vnen
I still don't think value should be explicit.
we all know it passes a value and only 1
With all the respect, this is a very bad idea, click on this comment it already has 6 dislikes.
There are three main reasons for this reaction:
value implicit would add another unnecessary reserved word, and nobody wants that._process requires us to type delta or rename the variable, delta is not a reserved word!).In order to expand on "C# does it" I did a quick research (please correct me where i'm wrong) about setters in other languages. I've checked languages that were considered to use for scripting in Godot plus Haxe. My results:
Explicit setter's value: Lua, Python, Squirrel, JavaScript, ActionScript, Haxe
Implicit setter's value:
Personally i consider implicitness of setter's value a small issue which does not justify reserving a word.
@marcospb19
implicit is better for this case because again there is only one thing your passing a value.
if this needs to be a option go for it I just hate defining something everyone knows what it is
I, personally, believe the setter's value argument should be made explicit. It's the simplest and clearest solution compared to the cost going down the road of implicitness would incur.
value would only be a new keyword inside a setter it wouldn't even be highlighted outside of a setter
That would require the tokenizer to class value as either a reserved keyword or a regular identifier depending on whether it's inside or outside a setter block. That would make the tokenizer needlessly more complex for little gain.
there is no need to customize the name of the value because its just that a value
In certain cases it may be beneficial to do. A value passed to the setter may not be a value _in itself_. If a setter deals with some kind of data, then yes we can call it a value but let's assume we pass an integer into a setter that identifies an action of some kind. That integer is a value but in the code it represents or designates an action, therefore we think of it as an action and not a value. Therefore, the name action or new_action is better suited for that setter's value.
(or I may just be fighting the semantics battle here :) )
- value would only be a new keyword inside a setter it wouldn't even be highlighted outside of a setter
Reserved word and I cannot call a global variable value anymore :disappointed: (also more unnecessary complexity for the parser).
- there is no need to customize the name of the value because its just that a value.
This argument does not make sense, values and variables have their meaning and we use "customize" their names to specify it.
I did not understood your third point, I'm saying that this feature should support static typing (like lambdas probably will).
I just hate defining something everyone knows what it is
Again, note that we have to type delta when we write the _process function, even if it is redundant and "everybody knows what it is", but we don't make it implicit because there's no reason to make things more complicated.
Agreeing with @hilfazer
Personally i consider implicitness of setter's value a small issue which does not justify reserving a word.
@katoneko
I, personally, believe the setter's value argument should be made explicit. It's the simplest and clearest solution compared to the cost going down the road of implicitness would incur.
its only clearer in the way you see the value.
but it is extremely ugly and makes code less readable.
a implicit value keyword is more readable and clear because once you understand it you don't need to worry about the name of the value you already know the value keyword contains it
That would require the tokenizer to class value as either a reserved keyword or a regular identifier depending on whether it's inside or outside a setter block. That would make the tokenizer needlessly more complex for little gain.
it would yes but I disagree with the little gain part
my way you could do something like this..
#alot nicer for with a some extra work
export(float) var my_var:
get := my_var_getter()
set := my_var_setter(value%2)
vs
#alot uglier for no good reason
export(float) var my_var:
get := return my_var_getter()
set(value) := my_var_setter(value)
In certain cases it may be beneficial to do. A value passed to the setter may not be a value in itself. If a setter deals with some kind of data, then yes we can call it a value but let's assume we pass an integer into a setter that identifies an action of some kind. That integer is a value but in the code it represents or designates an action, therefore we think of it as an action and not a value. Therefore, the name action or new_action is better suited for that setter's value.
like I said we all know it passes a value to the setter it doesn't matter its type its still a value so there is no need to rename it
you don't say I am going to set my property's name's values name
you say I am going to set my property's name's value
@marcospb19
Reserved word and I cannot call a global variable value anymore 😞 (also more unnecessary complexity for the parser).
thats why It would be a local keyword or variable inside the setter.
btw you shouldn't be naming your variables value anyways
This argument does not make sense, values and variables have their meaning and we use "customize" their names to specify it.
you customize the variables name not its value's name
Again, note that we have to type delta when we write the _process function, even if it is redundant and "everybody knows what it is", but we don't make it implicit because there's no reason to make things more complicated.
delta time could easily be made static like in unity or other game engines Time.get_delta()
so your argument doesn't make sense
Agreeing with @hilfazer
gdscript was made to be user friendly so that new users could easily learn it.
if this wasn't the case then it would have probably been scrapped for a more developed programming language like C# or python
if we don't keep the language both easy read and easy to use then the supposed target audience is isn't going to want to learn gdscript and there was no point in having gdscript as the primary language in the first place
but it is extremely ugly and makes code less readable.
I wouldn't think of it as ugly at all, let alone _extremely_ ugly. It's just an identifier enclosed by parentheses, it doesn't make it much uglier.
a implicit value keyword is more readable and clear because once you understand it you don't need to worry about the name of the value you already know the value keyword contains it
If the variable's name is always specified, you don't need any of that :)
btw you shouldn't be naming your variables value anyways
As vnen said, valueis really bad to disallow. In my project I have a Token class, which has a variable value, which is very natural for a Token class to have. If value is disallowed, I'd have to name it like token_value, which, when accessed, would end up being token.token_value and that feels redundant.
It's very easy to ban certain words from a language and so it's very easy to abuse as well. I, personally, think reserving a word is only justifiable if it's a keyword like if, whileor func, which is used to signal the parser that a corresponding definition is ahead. There, it's expected to reserve a keyword just for that, however now not only value is really undesirable to ban but also unnecessary as we can just make the setter's value explicit and dodge the problem altogether.
if we don't keep the language both easy read and easy to use then the supposed target audience is isn't going to want to learn gdscript and there was no point in having gdscript as the primary language in the first place
I just don't see how adding (value) would render GDScript code incomprehensible or substantially harder to read and understand.
if we don't keep the language both easy read and easy to use then the supposed target audience is isn't going to want to learn gdscript and there was no point in having gdscript as the primary language in the first place
I just don't see how adding
(value)would render GDScript code incomprehensible or substantially harder to read and understand.
Yes, I completely agree with @katoneko, I started learning game programming with GDScript in 2017, before that I only had some limited experience with HTML, CSS, javascript and a bit of PHP (mainly for customizing wordpress themes) but I'd hardly consider myself a programmer.
So I've been learning Godot and GDScript, and Unity and C# from 2018 onwards, and I still find GDScript a LOT friendlier to learn and use. As other people are arguing here, I find that explicit is much better for me to learn and understand and read. So much so that I still avoid using abbreviated variables, or one line ifs
export(float) var my_var:
get: my_var_getter()
set: my_var_setter(value%2)
This actually makes me wonder: where value is coming from? what the hell is that %2 supposed to be? and why do I need it?
while:
export(float) var my_var:
get: return my_var_getter()
set(value): my_var_setter(value)
Is pretty simple and immediately understandable, like "Oh, okay, so when setting it receives a value and passes to the function my_var_setter()"
gdscript was made to be user friendly so that new users could easily learn it.
I think the explicit declaration keeps the language easier to read an use, and I just wanted to add some two cents (a wall of text actually, sorry) from someone who actually is one of the target users of why gdscript was created, and why to this date, GDScript still makes a lot more sense to me than C#.
That idea of delta being a static and having to do Time.get_delta() instead of simply using a parameter that is explicitly declared is exactly the kind of stuff that trips me up frequently in unity, as it makes me question things like
While _process(delta) is explicitly declared so I just know where it comes from, and a simple explanation in the documentation of the _process(delta) is all I need. In fact just declaring the process function will already tell me I have to undertand "What the fuck is delta?" before using it and help me learn a game developing concept of frames and time between frames or at least make me look up _process(delta) in the documentation. So at least I know where to start researching.
I'm studying C++ now, and sincerly, even with all C++ complexity, I still think it makes more sense than C#. You have to type A LOT, but everything I'm typing has a meaning and serves a purpose and as I'm slowly learning the meaning of each thing, everything starts to fall in place together.
Either that or the instructor of the C++ course is much better than the instructor of the C# course, or unity really fucks up and makes everything it touches more complex (which I tend to suspect is the case).
I also think this from @hilfazer is really important:
Explicit setter's value: Lua, Python, Squirrel, JavaScript, ActionScript, Haxe
Implicit setter's value:
Even if we add C# as an example of Implicit setter's value, it's still only one against many others, and more important than that even, it will still be more similar to Python and Lua which to my understanding are the closer comparatives to GDScript, at least much closer than C#.
So if we have to take any other language as an example, I suppose it would be preferable to go for closer looking languages than far ones.
And we already have C# as a supported language anyways, so in some sense that "sintax" (hope I'm using this word correctly here) is already available in a way.
With this change, if I want to directly set a variable inside the class that defined the variable (without calling its setter function), would I still be able to do that?
(Like usingmy_var = valueinstead ofself.my_var = value)No. That's the problem (5) that I mentioned in the original post and it is a problem I intend to fix. If you need direct access in this case, you can use a backing field.
Jus a noob question though, what is a backing field? Or what would it look like in GDScript?
Tried to search google and I found this example about backing fields in Kotlin but it's not clear to me how this would work or look like in GDScript
deltatime could easily be made static like inunityor other game enginesTime.get_delta()
so your argument doesn't make sense
Godot is not Unity or any "other game engine", things in a different in each one, delta is explicit and that was a nice design choice made by Godot developers, you just discarded my whole argument because "Unity did that", we should consider Godot's context when responding to issues here.
delta time could easily be made static
But it is not going to, we are not changing _process(delta), so you cannot consider this is a valid argument to also make the setters parameter implicit, actually, we will maintain delta explicit because of the benefits it provides.
:disappointed:
Agreeing with @hilfazer
gdscript was made to be user friendly so that new users could easily learn it.
@Shadowblitz16, you changed my quote to something else, please, don't do that!
Here is what I actually said:
Agreeing with @hilfazer
Personally i consider implicitness of setter's value a small issue which does not justify reserving a word.
It's true.
if this wasn't the case then it would have probably been scrapped for a more developed programming language like C# or python
We can be inspired by Python design's, but It is a huge mistake to compare the capabilities of GDScript and Python (or C#). The paradigm, the age, the purpose, it's ALL different.
I can even say that GDScript should never be as "developed" as Python, there are obvious reasons for me saying that, but this is a long discussion that is very out of the scope of this issue.
@Shadowblitz16
Your arguments make sense, but the syntax you're proposing is not worth the downsides, a reserved keyword, no static typing support, and a type of implicitness that is not part of GDScript for valid reasons.
EDIT: About explicitness, again, I like to remember about the self keyword in any Python method, it has to be present even if it isn't a passable parameter, this leads to clearer functions that manipulate only what you see, there's nothing implicit there.
Jus a noob question though, what is a backing field? Or what would it look like in GDScript?
It's a term used in C#: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/properties#properties-with-backing-fields
Essentially you have your property named x with its getter and setter, which manipulates a field named _x ("field" is just a class variable without setter or getter). This way if you need direct access to the value you use _x. If you need to access the setter or getter, you use x.
I feel like my original proposal still covers all cases. Explicit value will be barely noticed if we have good completion. Though people are now discussing with this := syntax that isn't in any of my proposals (and doesn't make sense to me, as this is the syntax for type inference).
The only possible addition is what I mentioned before in https://github.com/godotengine/godot-proposals/issues/844#issuecomment-630269749, but that no one seemed to notice...
Jus a noob question though, what is a backing field? Or what would it look like in GDScript?
It's a term used in C#: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/properties#properties-with-backing-fields
Essentially you have your property named
xwith its getter and setter, which manipulates a field named_x("field" is just a class variable without setter or getter). This way if you need direct access to the value you use_x. If you need to access the setter or getter, you usex.
Thanks, your explanation and the link made it clearer than the example I had found!
The only possible addition is what I mentioned before in #844 (comment), but that no one seemed to notice...
Yes! I did think that was a good solution when I first read it, but completely forgot about by the time I finished reading the other comments and was writting my own!
I think this solves the issue well to keep using dedicated functions, using any order and not having to type (value).
I personally wouldn't use it as I still prefer to write longer code that is more explicit because of what I said in my long comment, but it appears to me as a good solution that adds an option instead of excluding one which sounds better to me in this case.
I just wanted to chime in and voice my support for @vnen 's original proposal with the addition of assigning functions optionally.
Well, actually I'm not a fan of how var seconds: int: looks aesthetically (the two colons so close to each other), but I couldn't come up with an alternative so I'm fine with it.
My 2 cents:
If you only want to use a getter, you need to have a weird dangling comma, which is easy to miss:
Make it optional.
Together with type and default value, the declaration line is already too long (export and `onready will move to annotations which improves this but not completely).
You have to separate the functions from the variables, so they look disjoint while they should be a cohese block.
You can use the \ to start a new line. Regarding the second point:
export(float, 0.1, 1.0) var my_var : int setget set_var, get_var
export(float, 0.1, 1.0) var my_var : int \
setget set_var, get_var
export(float, 0.1, 1.0) var my_var : int set_get \
set_var, \
get_var
You have an extra function in your script that's not always clear, from the user of the script, whether they should call the set_x() function or just set the x field with the automatic setter.
You can make it optional also.
Setters and getters aren't called if you use the variables inside the same class, which gives confusing semantics and makes it hard to refactor.
I agree, but it is not a syntax related problem
I just want to say that the current syntax is not perfect, but you can improve it and make it better without changing all of it. Meaning make things optional and I think it is better to have a backward compatible syntax, than having a new syntax to learn every time I want to switch between Godot's major versions.
Sidenote
By breaking the backward compatibility you are also asking many users to maintain 2 versions of their plugin. I don't think it is worth.
@xsellier
Make it optional.
I think the sole purpose of that comma is signalling the parser that there's only the getter. If we make it optional, we might not be able to tell if we're seeing a setter or a getter:
var a setget some_func # is it a setter or is it a getter?
var b setget ,some_func # ah, it's a getter with no setter!
var c setget some_func, some_func2 # it's a setter followed by a getter!
At which point we can't avoid changing the syntax in some form.
@katoneko You can improve the parser by parsing the function prototype for example.
No parameter = getter
One parameter or more = setter
var a setget some_func
var b setget ,some_func
var c setget some_func, some_func2
func some_func():
# Im a getter
pass
func some_func2(value):
# Im a setter
pass
I agree it is harder to implement, but I think it is better that way.
Another solution would be having a keyword set and another keyword get plus the setget keyword, that way you don't break compatibility.
@xsellier
You have an extra function in your script that's not always clear, from the user of the script, whether they should call the set_x() function or just set the x field with the automatic setter.
You can make it optional also.
Make what optional?
You can improve the parser by parsing the function prototype for example.
No parameter = getter
One parameter or more = settervar a setget some_func var b setget ,some_func var c setget some_func, some_func2
The parser could handle it but you also now have to handle it when reading the code.
Another solution would be having a keyword
setand another keywordgetplus thesetgetkeyword, that way you don't break compatibility.
If you cut the fluff that's pretty much what my proposal is... Except that I think adding code to still handle setget not worth just to not break compatibility, when other things will be broken anyway. I'm trying to take this opportunity to fix the small weirdness in GDScript syntax and behavior.
than having a new syntax to learn every time I want to switch between Godot's major versions.
Most users don't switch back and forth between versions. You learn the new syntax, which is a small burden I agree, but then you're set. It's like switching between Python 2 and 3.
By breaking the backward compatibility you are also asking many users to maintain 2 versions of their plugin. I don't think it is worth.
Well, setget will be the lesser issue when trying to maintain a plugin between both major versions, because the API will change a lot. I had a plugin for Godot 2.1 and just created a new version for Godot 3 because everything changed, even if GDScript syntax wasn't changed. The same is expected between Godot 3 and 4: you will need a new version of your plugin.
For instance:
Setters and getters aren't called if you use the variables inside the same class, which gives confusing semantics and makes it hard to refactor.
I agree, but it is not a syntax related problem
Fixing this is also breaking compatibility. Having a new syntax forces users to learn the new behavior.
I think that we always should avoid breaking backward compatibility when you can. For me, this is a case where you can avoid breaking the backward compatibility.
I think that we always should avoid breaking backward compatibility when you can.
I tend to agree in general but I also think we shouldn't be stuck with a lesser feature just because we made a mistake in the past.
Your call, you are the guy who is going to implement it, I cannot argue with that. But if you can keep the old way working, it'd be great too
My opinion is to do it exactly as you've described in your https://github.com/godotengine/godot-proposals/issues/844#issuecomment-630269749 vnen.
Another related thought to that though. Definitely wouldn't need to do this, but it could make writing GDScript a lot more convient, especially with lambdas added in. If GDScript were to introduce partial application from FP languages, you'd more or less get your suggestion out-of-the-box.
func add(a, b):
return a + b
func addFive(a):
add(5) # stores a lambda
func _ready():
print(addFive(10)) # prints "15"
At that point, you'd be able to define a getter or setter even more naturally:
var my_prop:
get: get_my_prop
set: set_my_prop
The fact that the (value) parameter is omitted would just be an optional thing should one not wish to detail that information.
Edit: Well, in retrospect, I'm seeing how it'd be difficult to resolve these versus straight method calls. You can't just do return add(5) or something. So, I guess, nevermind then. Unless someone can actually think of a viable implementation of the idea. XD
Just adding my support for Vnen’s original post here which for me seems much cleaner/better than having to scroll down to find the functions.
Ok, I try to keep myself short:
var milliseconds: int = 0
var seconds: int:
get:
return milliseconds / 1000
set(value):
milliseconds = value * 1000
```
This is the perfect syntax for it. (And coincidentally it's exactly the same syntax as proposed by vnen ;) )
Don't make value implicit, that would only be more confusing. This way it's really readable what it does and uses familiar concepts. (Like functions having parameters). Maybe I would even go for it and add empty parentheses for the getter as well. ( But don't worry, I did throw that idea away ).
Also in my opinion don't directly assign if theres no setter. In this case it should be readonly in my honest opinion.
@vnen, will static typing be supported?
var milliseconds: int:
set(seconds: int):
milliseconds = seconds * 1000
@marcospb19 That is not how properties are supposed to work. If you want to set using seconds, the property should also get using seconds. The type you set/get with necessarily must match the variable's type, so if the property is an int, you set it using ints, and get ints.
@aaronfranke I mean, the example is all kinds of strange, but it's totally fine to convert a value to some internal format, as long as accessing this property converts it back to seconds (and rounded as int, and not a float).
I don't think that type of value need to match the variable type, setters can be giant functions and if you don't give static type support, the function will fail at an unexpected place instead of failing at the call moment (unless the code checks at the first line).
What if you want the setter and getter to interface with strings while the variable is a list of nodes? Maybe because the string is a .yaml or .json, idk.
@marcospb19
will static typing be supported?
var milliseconds: int: set(seconds: int): milliseconds = seconds * 1000
I mentioned this already on the original proposal:
Type specifiers are not needed also since the variable is typed and the setter/getter will enforce the same type.
About this:
I don't think that type of
valueneed to match the variable type, setters can be giant functions and if you don't give static type support, the function will fail at an unexpected place instead of failing at the call moment (unless the code checks at the first line).
They must be though, because it doesn't make sense otherwise. If your property is int doing prop = x needs x to be int. Same for getter: x = prop will fail if x cannot be an int.
The parameter will have the type set, which will be the same as the property itself, so static checks are still applicable.
This is already the case with setget BTW: if you use different types for the setter parameter and the variable, the parser will give you an error. So far I haven't seen anyone needing a different behavior.
What if you want the setter and getter to interface with strings while the variable is a list of nodes? Maybe because the string is a .yaml or .json, idk.
Then don't use properties, or use a backing field for the actual data.
EDIT: i'm editing this message because I made a huge mistake :laughing: .
This is already the case with setget BTW: if you use different types for the setter parameter and the variable, the parser will give you an error.
IMHO there is no reason to block static typing (why add a barrier?), it should be optional.
We should be able to type this

Or
func set_sum(math_expression: String):
And call it with: set_sum('10 + 25 + 100')
With no errors while also having sum to be an int, it looks very reasonable.
It's not static typing if setter accepts a differently typed argument from the property it is a setter for. Use regular methods if you desire to break typing. Also, your example lacks type information in the property declaration, otherwise you'd have an error.
@pycbouh
Sorry for the confusion, I edited the last message.
Maybe somethig like this:
var _some_var
@set some_var
func _set_some_var(value):
_some_var = value
@get some_var
func _get_some_var():
return _some_var
@jebedaia360 That seems pretty good as an optional alternative syntax for it.
func set_sum(math_expression: String):And call it with:
set_sum('10 + 25 + 100')With no errors while also having
sumto be anint, it looks very reasonable.
If you are calling it as a method you don't need it to be a setter. If you need to use different types then don't use static types.
Maybe somethig like this:
var _some_var @set some_var func _set_some_var(value): _some_var = value @get some_var func _get_some_var(): return _some_var
My problem with this is that it makes my point (3) in the original post even worse: now looking at a variable you don't even know if it has a setter/getter or not.
Imagine a few months later you doing obj.some_var = 3 and then we get the value it's different: print(obj.some_var) prints 30, and you don't understand why. How long until you realize this have a setter? Looking at the declaration won't help. If you have the idea about the setter you have now to scan the file to find what function it is.
My problem with this is that it makes my point (3) in the original post even worse: now looking at a variable you don't even know if it has a setter/getter or not.
@vnen if you look close at my example you see that there is define only var _some_var and not a var some_var. In my example @set some_var and @get some_var are also declaration of some_var
My problem with this is that it makes my point (3) in the original post even worse: now looking at a variable you don't even know if it has a setter/getter or not.
@vnen if you look close at my example you see that there is define only
var _some_varand not avar some_var. In my example@set some_varand@get some_varare also declaration ofsome_var
That's a pretty surprising way to declare a variable (some_var).
But ok, we have 2 variables here: _some_var and some_var. Is _set_some_var a setter for some_var? If it is then why is it changing _some_var and not some_var?
My problem with this is that it makes my point (3) in the original post even worse: now looking at a variable you don't even know if it has a setter/getter or not.
@vnen if you look close at my example you see that there is define only
var _some_varand not avar some_var. In my example@set some_varand@get some_varare also declaration ofsome_var
The fact that I hadn't notice this makes my concern even worse... You can't even tell that a some_var property exists unless you look at all the methods.
Usually code has a defined layout with properties first and methods later. This way you can easily see the available properties on the top of the file. With this notation you have to look at all the methods to see if there's also a property hidden there.
I can see it's nice to write like that but you read code many more times than you write, so I prefer being more explicit and verbose in general.
I want to express strong support for the explicitly named setter argument being optional.
some people have said that reserving "value" would be a drawback,
but really, no member variable ever should be called "value".
reserving it would enforce better naming practice. this is not a bad thing.
most setters are one-liners where explicitly declaring a name for the value would be syntactic overkill.
I at least know for sure that the vast majority, if not all, of my setters would use the default name, i.e. "value", so it just feels pointless to declare it every time.
though I can see that if you have a little bit more complex setters, choosing your own name (and perhaps type) for the value may be a nice to have.
in Nim all functions have an implicit variable called "result" that is returned if you omit an explicit return statement. this is quite similar and works great there, too.
It isn't a better naming practice if we can't have values anymore ;)
I'm thinking about wrapper classes and the like.
Also it requires the user to know more about the language, remember more keywords and just make the language more complicated in general.
With the getter and setter as proposed by @vnen the setter and getter look like functions, work like functions, take parameter like functions and basically are functions.
This way it goes quite well with everything else a new user has learned already and will be much easier to learn and remember. Also it will be more clear to everybody reading it in the future.
This transparency and readability heavily outweighs the additional 5 letters you have to type the first time you declare the setter.
Programming is (especially in large projects) is all about reading the code and only to a small amount about writing the code.
PS: If your setters are one liners, you probably don't need a setter.
It sounds like your coming from java where everything gets a getter/setter even though nobody really knows why. (My personal guess would be because they aren't really a language feature in java and instead just methods and it isn't as easy to latter switch to a getter/setter. However this isn't the case with godot).
A setter and getter in godot are only useful if they actually do something besides a simple assignment.
@Serenitor
but really, no member variable ever should be called "value".
See this comment from @katoneko, where he or she explains a valid context for using the value as an variable identifier.
@marcospb19 I think it's this comment :)
There's a use case that existed in Godot 2 that we lost in Godot 3 which I'd like to see brought back.
When all properties were only accessible via getters and setters (Godot 2), you could override the implementation in a subclass and be sure that it would always get called. When we moved to the hybrid model in Godot 3 we encouraged people to access properties directly in their code, and if you wanted to implement methods around their access you'd declare a setget.
But that only works if the setget is defined in the class which initially declares the variable. Which you don't always have access to.
In Godot 2 if I wanted to create some subclass of Node2D and override its set_pos() behavior I could do so confident in the knowledge that anyone who tried to manipulate its position would go through my function.
In Godot 3 I cannot even specify a setget on the position variable of a subclass of Node2D. And while set_position() and get_position() functions do exist and can be called, they're not always called.
If I wanted to have a set_position() for a subclass of Node2D get called every time, I'd have to ensure every line of code which accessed it explicitly calls object.set_position(value) instead of object.position = value. This is contrary to one of the primary purposes of this style of setters/getters -- to ensure that they're always called transparently, regardless of how an external class chooses to access the property they control.
For Godot 4 can we make sure that subclasses have the ability to specify setters/getters if they weren't already specified by some parent class?
This is why languages like Java encourage even trivial getters and setters for all variables. Because you don't know when some subclass might want to override or extend that behavior, but you want them to have the option to do so if they choose.
@Gastronok
The argument that Java encourages this type of use case seems more suited for work between disparate departments in an enterprise environment than an individual developer or project team. Pushing the boilerplate encapsulation of everything is one thing that drives me nuts about certain design philosophies and I would argue that avoiding having to have this mindset is a better argument for being able to override a field's side effects in a subclass.
@nobuyukinyuu Agreed. That's why I'm hoping this proposal takes that use case into account, so I can add setters to variables in some Asset Library class without modifying them or to core variables like Node2D.position without having to rebuild the engine.
I don't know if this has been talked about or if I missed it, but if I say, for example:
var my_field:int:
set():
...
get():
...
...
# Some code here
mi_field = 10
...
Will I be able to store that value of 10 directly in my_field, like in Kotlin, or do I have to use a separate variable, like in Python?
What there are now has flaws and is somewhat annoying, for example, I can’t indicate the names of functions in different order, or explicitly specify only one.
And I liked the idea with a Varant like that ...
Shadowblitz16 commented on 17 May
if setget is seperated into set: and get: I would really like to be able to use one liners...export(float) var my_value set: set_my_value, get: get_my_value
it would also be nice to beable to switch order of set and getexport(float) var my_value get: get_my_value,set: set_my_value
But! I am very afraid of the picture that is presented in the top, it seems to me that this solution is extremely not elegant.
I just watch this script and I feel bad.
when instead of this imperfect ...
var milliseconds_0: int = 0
var seconds_0 setget "set_m_0", "get_m_0"
var milliseconds_1: int = 0
var seconds_1 setget "set_m_0", "get_m_0"
var milliseconds_2: int = 0
var seconds_2 setget "set_m_0", "get_m_0"
func set_m_0(value):
milliseconds = value * 1000
func get_m_0():
return milliseconds / 1000
func set_m_1(value):
milliseconds = value * 1000
func get_m_1():
return milliseconds / 1000
func set_m_2(value):
milliseconds = value * 1000
func get_m_2():
return milliseconds / 1000
it turns out something else, even more terrible, like this ...
var milliseconds_0: int = 0
var seconds_0: int:
get:
return milliseconds / 1000
set(value):
milliseconds = value * 1000
var milliseconds_1: int = 0
var seconds_1: int:
get:
return milliseconds / 1000
set(value):
milliseconds = value * 1000
var milliseconds_2: int = 0
var seconds_2: int:
get:
return milliseconds / 1000
set(value):
milliseconds = value * 1000
@Joakker
Will I be able to store that value of 10 directly in
my_field, like in Kotlin, or do I have to use a separate variable, like in Python?
Like in python, with extra variables if a state is is needed
@OlexiyKravchuk
it turns out something else, even more terrible, like this ...
Not sure why you find this terrible, can you elaborate?
@MWFIAE
Not sure why you find this terrible, can you elaborate?
Because instead of clear order of declarations and definitions of variables and then descriptions in separate function blocks, we get a funny mess that creates very complex code readability and adds redundant designs and unnecessary semantic and visual loads.
And besides this, the search for variables in such wilds becomes very problematic, and also the functions themselves become not available for a separate call if necessary inside a class, for example.
And in the example we showed only the shortest version of the function, and in real cases they can be many more lines, this will turn the code into a monster.
the search for variables in such wilds becomes very problematic
It's still any line with no indent that starts with var, so I don't really have any trouble following the code. Not sure if I agree with this one.
the functions themselves become not available for a separate call if necessary inside a class
What about this?
func _get_seconds(i):
return get("milliseconds_" + str(i)) / 1000
func _set_seconds(i, value):
set("milliseconds_" + str(i), value * 1000)
var milliseconds_0: int = 0
var seconds_0: int:
get: return _get_seconds(0)
set(value): _set_seconds(0, value)
var milliseconds_1: int = 0
var seconds_1: int:
get: return _get_seconds(1)
set(value): _set_seconds(1, value)
var milliseconds_2: int = 0
var seconds_2: int:
get: return _get_seconds(2)
set(value): _set_seconds(2, value)
That seems pretty clear to me anyway.
in real cases they can be many more lines, this will turn the code into a monster.
Again, you just have to call a helper function to vastly simplify the workload.
@willnationsdev
Why define variables after applying them?
And complicate the code just for no reason?
@OlexiyKravchuk
Well, the order doesn't matter for the functionality of it. I could certainly have defined the methods first and stuck the helper methods towards the bottom (probably would've been more clear that way). But I'd hardly say that calling a utility setter/getter directly from the binding setter/getter is significantly complicating the code. At least, you don't have any logic-related code duplication with my version, so I'd say it's actually less complicated.
If you're really aiming to make the code simpler, then switching to an array approach is probably a lot easier overall.
var seconds := []
func _ready():
seconds.resize(3)
func set_seconds_idx(i, value, value_is_ms = false):
assert(i < seconds.size())
seconds[i] = value / 1000 if value_is_ms else value
func get_seconds_idx(i, as_ms = false):
assert(i < seconds.size())
return seconds[i] * 1000 if as_ms else seconds[i]
That works for N cases, has almost no code duplication, and cleans up everything overall.
But even then, resorting to the dynamic lookup functions get() and set() to create helper methods for your case doesn't really seem all that bad to me. But I suppose everyone has styles they do and don't like.
\^ This is steadily getting off-topic, but oh well.
@willnationsdev
The point now is not at all to simplify or optimize this particular private EXAMPLE of a code taken from the ceiling just to demonstrate the lack of elegance of the recording method.
And the question is about, in principle, to make this whiteness convenient for practical use, solving problems, and for understanding and convenience of most users, including beginners, rather than complicating and boasting about how you can still mock the order and form of recording, we are not here invent (second brainfac), then why move away from the topic of starting a discussion for the sake of discussion?
You can’t imagine how hard it is for me to do these translations in order to respond to comments, I may not have much time left, and I have to explain that even the topic does not concern ...
Any change should be aimed at optimization and improvement, I don’t see anything useful here but there is definitely something harmful, then why discuss how harmful it is and how bizarre it can be to give it useless / harmful?
@Joakker
I don't know if this has been talked about or if I missed it, but if I say, for example:
var my_field:int: set(): ... get(): ... ... # Some code here mi_field = 10 ...Will I be able to store that value of 10 directly in
my_field, like in Kotlin, or do I have to use a separate variable, like in Python?
I already mentioned this on the first post:
To avoid creating external variables for this, you can use a generated member variable inside the setter and getter:
signal changed(new_value) var warns_when_changed = "some value": get: return warns_when_changed set(value): changed.emit(value) warns_when_changed = value
@OlexiyKravchuk
Because instead of clear order of declarations and definitions of variables and then descriptions in separate function blocks, we get a funny mess that creates very complex code readability and adds redundant designs and unnecessary semantic and visual loads.
And besides this, the search for variables in such wilds becomes very problematic, and also the functions themselves become not available for a separate call if necessary inside a class, for example.
And in the example we showed only the shortest version of the function, and in real cases they can be many more lines, this will turn the code into a monster.
Honestly splitting getter/setter code from variable definition is a toll on readability in general. Because then you have to scan the file to find what the getter/setter does and that takes time.
If you really want to have an actual function, you can also use the alternative syntax that I provided:
So, I guess we can provide a special syntax if you still want a separate function:
var my_prop: get = get_my_prop, set = set_my_prop func get_my_prop(): return my_prop func set_my_prop(value): my_prop = value
@vnen
I would suggest accepting two additional keywords “set_f” and “get_f” or “set_” and “get_” instead of setget and the ability to write them on the same line with a variable in any order, or excluding any of them without a comma, and also avoiding applying the characters ":" and "=" and after the variable.
var prop = 1 get_ "get_prop" set_ "set_prop"
func get_prop():
return prop
func set_prop(value):
prop = value
@vnen I know you're not a fan of adding extra keywords, but here's another suggestion. Since functions and variables won't be allowed to share a name, you could just write the optional setters and getters as functions named after the variable, and identify them by using a prefixing keyword like setter and getter.
(Similar to the post above this one, but no explicit get/set after the variable declaration.)
Some examples might look like:
var number : int = 0
setter number(x : int) -> void:
number = x
getter number() -> int:
return number
# Or it can be similiar to static
# func, and be more verbose.
var number : int = 0
setter func number(x : int) -> void:
number = x
getter func number() -> int:
return number
md5-237acd1190f7c87a3fb3de1df9a0e574
# Or some shortened version
var number : int = 0
_set number(x : int) -> void:
number = x
_get number() -> int:
return number
md5-237acd1190f7c87a3fb3de1df9a0e574
# Then they could be in any order.
# IE - Two vars with only getters.
var number : int = 0
var is_active : bool = false
getter number() -> int:
return number
getter is_active -> bool:
return is_active
A common thread I'm seeing in the desired alternatives is just something that matches this pattern:
```gdscript
setget keyword, getter/setter, _get/_set, whatevervar a [setget]
var b [setget]
var c [setget]
[setget] func set_a(value):
pass
[setget] func set_b(value):
pass
[setget] func set_c(value):
pass
It seems like it's only the part about variable declarations being separated with lines of bindings that is the cause of frustration. If this isn't accurate, someone let me know. @vnen, can you think of a way that you would like to resolve that if that's the case? Perhaps allow setters and getters to be assigned inline rather than on separate lines? For example...
```gdscript
var x: int = 0: get = get_x, set = set_x
Or perhaps there is a specific reason why you think it would be a bad idea to have inlined [setget] bindings (either in the variable declaration or the method definition)?
I understand the frustration from having a variable definition take many lines of code, but to me I prefer that as it tells me at a glance that it is not just a simple variable, it is a property. And besides @vnen already provided an alternate syntax that is really really compact:
If you really want to have an actual function, you can also use the alternative syntax that I provided:
So, I guess we can provide a special syntax if you still want a separate function:
var my_prop: get = get_my_prop, set = set_my_prop func get_my_prop(): return my_prop func set_my_prop(value): my_prop = value
This reduces the property declaration to two very compact lines and you can organize the funcion declarations however you like. Isn't it a good enough middle ground?
Perhaps allow setters and getters to be assigned inline rather than on separate lines? For example...
var x: int = 0: get = get_x, set = set_x
I guess I failed to mention before but this is already allowed. I'm considering removing the comma too.
I understand the frustration from having a variable definition take many lines of code, but to me I prefer that as it tells me at a glance that it is not just a simple variable, it is a property.
Yeah, I'm not fond on not having any marker in the variable declaration because hides the information that it is a property. This will cause problems when refactoring later because you might not notice this detail and introduce a bug that isn't easy to pinpoint.
var x: int = 0: get = get_x, set = set_x
Sorry if im late to the discussion!
I think this kind of syntax makes it much harder to read than it currently is.
I personally prefer the proposed new syntax of
var x : int
get: return x * 100
set(value): x = value
or the classic setget notation.
This may be a dumb q, but I haven't seen it addressed, but to be sure, this would be valid?
var x: int = 0
get: get_what_x_is ()
set(value): set_what_x_is (value)
Note that I think that, with the set line, the variable assignment seems redundant? set should implicitly mean returning a value anyway, so set:(value): x = set_what_x_is (value) would be redundant?
Also, I would hope that in the case of above or similar, putting it all on the same line, i.e. var x: int = 0 get: get_what_x_is () set(value): set_what_x_is (value) would be possible (think @vnen said that's the intent?).
Whatever the result, hopefully it doesn't force having to define the functions immediately after these declarations. It would limit how one organizes their code.
I would also like the feature of on change functions that connect to multiple variables, like this:
export var color := Color(): onchage = update
export var width: float = 4.0: onchange = update
func _draw():
draw_line(Vector2(), Vector2(50, 0), color, width)
It would make writing custom GUI controls a much easier process, as now I can say which variables need to trigger a redraw.
See https://github.com/godotengine/godot/pull/32815 for more context
@creikey I think this would be better achieved with a separate annotation (or even an add-on), as an observer system can get pretty complex if it wants to be smart about tracking dependencies.
For anyone who would like to test out the new property system, it has been implemented in this PR and is now available in the current master branch. You can either compile the engine to test it, or you can use iFire's Stern Flowers build.
Important note: The master branch is still unstable and has many bugs, so don't try to open any existing projects with it.
I like this proposal, but I don't like the name of this new feature. With this proposal, there's now two types of things in Godot that are known as "properties". Essentially, we have the inspector's properties and a variable's properties. To make things more confusing, properties in the inspector are variables which means that (inspector) properties can have (variable) properties.
To avoid confusion, I recommend renaming this feature to something like variable attributes. If we don't decide to rename this feature, I think that we need to have a clear way to distinguish between the two types of properties in the documentation. For example, we could say "inspector properties" and "variable properties" or "an Object's properties" and "a variable's properties".
@Jayman2000 Inspector properties include GDScript properties. If you export a property in GDScript, it will show up in the inspector as a property.
With properties, a variable does not "have" properties (so it's not "a variable's properties"), but rather a property is a variable with custom get/set logic attached to it.
@aaronfranke Can you have a variable that is a property but isn't exported? In other words, can you have a variable that is a property as far as GDScript is concerned but isn't a property as far as the inspector is concerned?
Yes.
Maybe I'm a bit too late, but I'd like to suggest a slightly different syntax:
var x: int with:
get:
return x
set(value):
x = value
or inline:
var x: int = 0 with get = get_x(), set = set_x()
I think adding a with keyword makes the declaration read quite nicely and prevents having something like x: int: with multiple : following each other.
@aaronfranke Thanks for the clarification!
I would like to tweak my original criticism. I don't like calling this kind of variable a property because there's already a different kind of variable called a property. As a result, you can have a property which is a property and a property which isn't a property. Calling these "properties" will make it harder to search for information about them and ask questions about them. If I search for "properties in Godot", will I get information about exported variables or about variables that have setters and getters? If I ask the question "what are properties?", what kind of properties will I be told about?
I think that we should call these variables "variables with setters/getters". It's a bit more wordy, but it prevents any confusion between the two. If we don't decide to rename this feature, I still think that we need to have a clear way to distinguish between the two types of properties in the documentation. Maybe "inspector properties" and "setget properties"?
so what if we wanted to eventully add lambdas to gdscript?
it doesn't make sense that the value is explicit then
var x: int with:
get->x
set->x=value
vs
var x: int with:
get->x
set(value)->x=value
again I still think it makes more sense to implicitly type it.
for god sake at least make it a option.
I shouldn't have to type out (value) when I already know what value is and that I don't need to name it.
like literally what is the diffrence between
var x: int with:
get->x
set(value)->x=value
and
var x: int with:
get->x
set(_x)->x=_x
there is none it serves no purpose
if anything it creates ambiguity if there is a _x field defined already
and noone should be calling their variables value.
if it helps it could be something like..
var x: int with:
get->x
set->x=x.value
but I think thats a bit more confusing then just
var x: int with:
get->x
set->x=value
so what if we wanted to eventully add lambdas to gdscript?
I don't understand what difference does it make. Adding lambdas won't change the syntax for properties. Lambdas also need their arguments to be explicit.
@vnen no they don't look at C# its basically the same syntax
private float _x = 0;
public float x
{
get => _x;
set => _x = value;
}
if they needed to be explicit then it would be something like so...
private float _x = 0;
public float x
{
get => _x;
set(float value) => _x = value;
}
I came from the blog post. I have a question about this statement that I don't see answered here:
unlike setget, properties always call their setter/getter even inside the same class
I currently take advantage of the fact that I can update a variable directly from inside a class. I want the setter logic to only be called when an external consumer sets the variable, or if I want it inside the class I call that function directly. There are some cases where I want to update a variable's value without triggering the setter. How would this be done in the new syntax?
I came from the blog post. I have a question about this statement that I don't see answered here:
unlike setget, properties always call their setter/getter even inside the same class
I currently take advantage of the fact that I can update a variable directly from inside a class. I want the setter logic to only be called when an external consumer sets the variable, or if I want it inside the class I call that function directly. There are some cases where I want to update a variable's value without triggering the setter. How would this be done in the new syntax?
If I am not mistaken, now this will have to use separate functions and not use setget
There are some cases where I want to update a variable's value without triggering the setter. How would this be done in the new syntax?
I've already answered this: https://github.com/godotengine/godot-proposals/issues/844#issuecomment-630273865
Short answer: use another variable for direct access.
Short answer: use another variable for direct access.
I see, reminiscent of Javascript. I'm not exactly a fan of the create a second variable approach in JS, it only makes sense semantically when you have a getter that is a "computation" based on multiple variables, such as a full name getter would return first + last. But when all you're trying to do is provide setter logic for an external consumer, adding the extra variables kinda muddies up the code.
Another thing with the old setget syntax is I could emulate private variables by providing an empty setter. I assume this is going to cover that though? https://github.com/godotengine/godot-proposals/issues/641
I completely agree with @Giwayume
I came from the blog post. I have a question about this statement that I don't see answered here:
unlike setget, properties always call their setter/getter even inside the same class
I currently take advantage of the fact that I can update a variable directly from inside a class. I want the setter logic to only be called when an external consumer sets the variable, or if I want it inside the class I call that function directly. There are some cases where I want to update a variable's value without triggering the setter.
I think that it is absolutely unacceptable that the seter and getter will work in the interior of the class itself automatically when working with such a variable, this will cause a lot of problems, constant rekusiya and failures, this is just a maelstrom of hellish pain in the fifth point, not to mention the confusion for beginners , ponder this question better.
@vnen And this approach with a different variable
I've already answered this: #844 (comment)
Short answer: use another variable for direct access.
Only aggravates the situation several times, adds unnecessary arguments, confusion, cliché code and just a bunch of extra work and stress.
Explain why you need to invent a ridiculous problem, and to come up with an even more ridiculous way to solve it?
I guess we _could_ have a way to edit the field directly, but I'm not convinced yet that it's really needed.
If this is direct value changes within the class that defines it, this has come up for me on occasion where I prefer not to trigger code in the setter.
Though I have changed style, because of other ambiguities around setters/getters. One being that export vars call them with their scene value sometime after _init() and before _enter tree(), but only if they have a non-default value.
@OlexiyKravchuk I think that it is absolutely unacceptable that the seter and getter will work in the interior of the class itself automatically when working with such a variable
I don't see the problem. You can just have a property called my_property and have it use an internal field called _my_property. This way you can just use _my_property if you don't want the getter/setter logic.
The qeustion for me is, if it then is necessary to have implicit calls to setters/getters, as those seem to create a part of the problem. Im not suggesting that for a solution, I like how it is handled right now, just giving my two cents.
In Java you have to call the method/variable directly, but you also have access-modifiers (modifying who can access what method/variable). So if you want to access a variable you dont have access to, you would be forced to use the getter/setter that is being provided via public.
But that system has its downsides as well with a beginner-unfriendly extra layer of complexity via the access modifiers.
But i think explicit is always better than implicit (I hate java because of all that implicity: "You used that but actualllyyy...")
Keep it up!
I like the proposed syntax, I just think it would be cleaner to remove the backing variable from the equation and give a way of setting/getting the value directly.
Like inside of the setter/getter functions would be direct access to "property"...
var property = 0:
get:
return property + 1000
set(value):
property = value - 1000
Outside, maybe have something like set_direct() or get_direct().
Or "self.property" could be direct access while "property" calls the setter/getter.
Maybe a crazier idea, allow defining external & internal setters/getters as different functions if needed. This would forego the need for direct access.
@aaronfranke Probably I did not formulate the question correctly.
I ask the question ...
For what purpose is a nail sticking out of the seat? It spoils clothes, injures, it is impossible to sit, how is this justified?
And in response ...
I do not see a problem, you can first inject an anesthetic, then disinfect the wound, and after that it will put on a plaster, and sew up clothes at home, or buy a new one.
So I will rephrase my question ....
What is the overall win. Can you find a serious practical problem in which such sacrifices will really be justified?

@OlexiyKravchuk I prefer the new behavior, so I don't see the problem. IMO it was confusing that previously the behavior of a property changed depending on where you call it from which seems counterintuitive to me. I find that the old 3.2 behavior is a nail sticking out of the seat because it would be easy to accidentally use the property and not have the attached logic run with it.
@OlexiyKravchuk I don't think that that is a fair comparison. Based on your description, the nail sticking out of the seat seems like something that is meant to be clearly bad. I don't see having to use backing variables as being clearly bad. You also mention sacrifices. I think that "sacrifices" is too strong of a word here. Having to use backing variables doesn't seem like a big deal to me. Backing variables certainly aren't sacrifices.
@aaronfranke
This is precisely the problem that inside a class such logic should not be triggered automatically, but only by calling methods with special processing (and such cases are very rare in practice).
I don’t know how often you have come across a complex appearance of such logic in practice (and I’m not talking about primitive phenomena only by setting and only taking the meaning of what is seen here as an example of a misleading one, since in real code this behavior is much more complicated) , and when it comes to application in real code, it will no longer seem that the behavior is not logical.
@OlexiyKravchuk Compare
var locale setget set_locale, get_locale
func _ready():
set_locale(str(config.get_value("app", "locale", get_locale())))
func set_locale(lc):
TranslationServer.set_locale(lc)
config.set_value("app", "locale", lc)
func get_locale():
return TranslationServer.get_locale()
and
var locale:
set(lc):
TranslationServer.set_locale(lc)
config.set_value("app", "locale", lc)
get:
return TranslationServer.get_locale()
func _ready():
locale = str(config.get_value("app", "locale", locale))
It became much clearer.
And the following code is less correct, since one entity is duplicated in two variables:
var locale setget set_locale
func _ready():
locale = str(config.get_value("app", "locale", TranslationServer.get_locale()))
func set_locale(lc):
locale = lc
TranslationServer.set_locale(lc)
config.set_value("app", "locale", lc)
This is precisely because we are duplicating the value in the locale variable, and not getting it from its origin.
@Jayman2000
complicating and bloating the code,
complication of syntax,
creating confusion for beginners,
increasing the entry threshold,
increased load,
increasing the likelihood of errors,
...
how much more do you need?
@OlexiyKravchuk I disagree with literally all of your conclusions. The syntax is less complicated (using a property always does the same thing, not one of two things), it is less confusing, it makes errors less likely (usually you do want to run the setter logic everywhere in my experience), and it is more consistent with other languages such as C#, lowering the entry threshold.
@Jayman2000
complicating and bloating the code,
complication of syntax,
creating confusion for beginners,
increasing the entry threshold,
increased load,
increasing the likelihood of errors,
...
how much more do you need?
I was a begginner to programming when I started learning Godot, and it was somewhat of an entryway to broader programing concepts and knowledge to me, and my experience is that the behavior previous to this proposal was indeed confusing and prone to error.
Try to think like this, as a beginner, you're learning a new "rule" or "concept" that is "when you use setget whenever you access that variable this methods will be called except if you're in the same script that the variable was declared. Only in this case you'll have to use self.variable or call the setter function or getter function directly".
Ok it's not a long explanation, but it is pretty big if, and now whenever I'm accessing a variable outside from the script things behave like I want, after all, that was what I wanted to gain when I decided to use the setget, but then comes a time I need to use the same variable inside the script and I totally forget that variable I used everywhere else on the code behave differently just here.
For me it is much more simple and straightforwad to explain:
"When you use setget whenever you access that variable this methods will be called." And that's it. No exceptions or special cases, always. Much simpler and easier to understand as a begginer.
Then will como a time I'll need to access it without the side effects of the setter or getter, the explanation is simple an straightforward again:
"In those cases you use a backing field, a private variable that your setter and getter deal with, but that only your original script can access outside of the setters/getters"
And sincerely, the code isn't that different from what we have on 3.2
How it is in 3.2:
var some_var setget _set__some_var, _get_some_var
#.... Lots of code ....
#.... somewhere in the code I need to access some_var with the setter ....
_set_some_var(some_value)
#.... somewhere in the code I need to access some_var directly ....
some_var
#.... Lots of code ....
func _set_some_var(value):
# ... lots of complicated setter code ...
some_var = value
func _get_var():
# ... lots of complicated getter code ...
return some_var
In the new sintax:
var _backing_var
var some_var:
get = _get_some_var, set = _set__some_var
#.... Lots of code ....
#.... somewhere in the code I need to access some_var with the setter ....
_set_some_var(some_value)
#.... somewhere in the code I need to access some_var directly ....
_backing_var
#.... Lots of code ....
func _set_some_var(value):
# ... lots of complicated setter code ...
_backing_var = value
func _get_var():
# ... lots of complicated getter code ...
return _backing_var
I olny had to add one line for the _backing_var, and renamed some_var to _backing_var whenever it was used directly.
So I think all this new sintax does is add options and improve readability by making some things more explicit, which for me as a begginner~intermediate programmer is still something that helps me A LOT. And it doesn't change that much if you want to continue using the old way, since there are a lot of options available, your changes will have to be minimal.
And Lastly, I can see you're clearly outraged by this change, even though we don't see eye to eye on the downsides of it. But your form of communication in these posts have been very violent. So please, do try to calm down and cool off before commenting, specially on things that makes you outraged. The people working on this are real people, with their own circumstances and feelings, and violent communication doesn't help you or anybody
@eh-jogos do you have to explicitely fill the _backing_var in the setter of some_var?
I agree with your dissertation, but think that having to create an extra field/variable feels bad to me (but thats just my opinion. I can understand if there are bigger reasons for that change)
Keep it up!
@Reneator Yes, I think you do! I edited my example with that in mind!
I agree with your dissertation, but think that having to create an extra field/variable feels bad to me
Yeah, it feels like a workaround to a design flaw, though there are a ton of languages that do it exactly this way. That doesn't make it justified, but it's the norm. I was hoping GDScript could surpass other languages in terms of design simplicity here.
I feel like adding an explicit way to get/set the variable directly is the only thing this design needs (e.g. something that makes it obvious the getter/setter isn't going to be called).
If explicit backing fields are such a pain, would having an additional annotation for this work? E.g.
# Implicit backing field
var x: int = 5:
get: return x * 5
set(v): x = v / 5
# Explicit backing field
var _y = 1
var y: int:
get: return _y * 5
set(v): _y = v / 5
# Annotation
@rawvalue # or @field, or some better name
var z: int = 5:
get: return z * 5
set(v): z = v / 5
func _ready():
prints(x, self.x) # 5 5 # Default behavior: cannot get the raw value
prints(y, _y, self.y) # 5 1 5 # Custom explicit behavior: cannot manually get the raw value
prints(z, self.z) # 1 5 # Annotated behavior: direct variable usage is like in 3.x
I think that's fine, treat the old behavior like an advanced behavior that you have to enable explicitly, and the annotation makes it clear that it behaves differently.
Maybe an alternative is having different setters/getters for external vs internal usage, which would be a similar effect:
var z: int = 1:
public get: return z * 5
private get: return z
func _ready():
prints(z, self.z) # 1 5
TBH I don't know what a good syntax would be here, just adding the suggestion.
@bojidar-bg
wouldnt:
func _ready():
prints(x, self.x) # 5 5 # Default behavior: cannot get the raw value
actually be:
func _ready():
prints(x, self.x) # 25 25 # Default behavior: cannot get the raw value
because you wrote "Cannot get the raw value" ?
Or is the
var x: int = 5:
already accessing the setter?
I personally would prefer the initial declaration of the variable to just set it directly (but im witholding that opinion if you guys think there is something more important that makes that solution better.
But i really like that syntax! It might be some added complexity, but people that are new to godot will learn set/get in that new way and if someone prefers the old way they could do it with just small changes!
Keep it up!
If users have to invent workarounds for current properties implementation, there's something wrong with this properties.
Or is the
var x: int = 5:
already accessing the setter?
I personally would prefer the initial declaration of the variable to just set it directly
Agreed I assumed that was what it was doing. Otherwise any calculation that uses x on the right side of the assignment risks it being null on first assignment.
The old setget isn't free of workarounds (you can argue that using self.x is a lesser workaround, but it is still one). I had problems when refactoring code because of its finicky behavior too.
People is always telling that "explicit is better than implicit", but here it seems to go to an opposite trend, as people want to hide behavior with semantics (absence of setter argument, different behaviors inside and outside class). I really prefer to go explicit in both instances and use consistent behavior.
Also this discussion is long and maybe some things aren't clear so let me explain:
1) Accessing the variable inside the setter/getter have direct access. So if you don't need direct access anywhere else you can skip adding the backing field. This is the same as it was before.
2) If you use initialization (var x = 0: set....) you also set the direct value, so it's not calling the setter yet.
This has some additional benefits:
You always know when the getter/setter is being called, because behavior is consistent.
I can optimize self.x to behave the same as just x without issues. Some people prefer to use self. explicitly everywhere and right now they are losing performance (since this goes around the slow path to call the instance getter). They also are forced to not use this when they want direct access (unless they use a backing field but that's now consistent with new behavior, so the pattern is expected). With this convention the code can be optimized without getting into corner cases.
In the worst case that you do need a backing field, I don't think it's such a hassle to just add one. You have to be mindful where you use which but that was already the case with setget except that now you don't need to remember the "trick" to use the self. prefix.
If explicit backing fields are such a pain, would having an additional annotation for this work?
I would rather provide a syntax for direct access than changing each property to behave differently, which I think is worse than what happened with setget. However I don't feel it's needed and it's just blind future-proofing. After people start using this and they really see it as as problem that is hindering code writing/reading or productivity, then we can consider adding it. Let's leave the problem to actually arise first before we implement a solution.
People is always telling that "explicit is better than implicit", but here it seems to go to an opposite trend, as people want to hide behavior with semantics
I would like to assure you that there are people completely satisfied with your solution with more explicitness. We are probably silent because we are fine as is.
People is always telling that "explicit is better than implicit", but here it seems to go to an opposite trend, as people want to hide behavior with semantics
I would like to assure you that there are people completely satisfied with your solution with more explicitness. We are probably silent because we are fine as is.
Agreed! @vnen 's explanation clarified it for me and I now understand it clearly and think its a great idea and great concept!
it would be close to the java this.x and x behaviour, just with the difference that using x would implicitely call the setter. But having it as a rule like: "It's ALWAYS calling the getter and setter (if you defined one) and if you want to access it from within the class, you have to use "self.x" " is a "compromise" that sounds really good to me!
Edit: Addition: So if somebody wanted to use a backing-var or wants to provide unfiltered access to the variable he can still do so if they want/need it via declaring specific getter/setter, that may just return/set the value directly. like: get_x_direct()
Also as you might assume that this would be a corner case.
@vnen
However I don't feel it's needed and it's just blind future-proofing. After people start using this and they really see it as as problem that is hindering code writing/reading or productivity, then we can consider adding it.
This proposal basically works the same as ES6 getter/setters, except in Javascript there is no such thing as "direct access" to the property (inside of the getter/setter function, or anything, like the current proposal), essentially if a property is defined as a getter/setter, it can no longer contain a value. The property is an alias to two functions at that point.
I've worked with Javascript for quite a while now, and have never been a fan of having a 2nd backing variable unless the getter/setter is truly a combination of multiple existing variables, as opposed to a way to run some extra logic before setting a variable's value. This discussion isn't novel or a problem unique to GDScript.
Essentially it becomes a problem of coming up with a different variable name for your internal data vs external interface, and a lot of times you want that name to be the same. Finding alternate names that make sense for variables when you could've had the option to just reuse the same name would save a lot of time.
The alternative, like suggested, is just use "_" + variable_name. But here's the problem with that, it's only viable if you do it for literally every property. Otherwise it creates an inconsistency where coming back to the code a month later you wouldn't know off the top of your head which variables have "_" backing variables and which do not. Consistency is key to having a well human-maintained program where the priority is to avoid unnecessary mistakes.
So basically, I've been dealing with this issue for a long time now in other languages, and my solution is to come up with an alternate name for any variable that needs a different external interface. That's a lot of extra cognitive load to writing a program that really doesn't need to be there if the language just supports direct access from inside of the class.
But here's the problem with that, it's only viable if you do it for literally every property.
I would argue that many properties don't need direct access to a backing variable (and also most members in general can just be fields with no getter/setter logic), so no, you don't need this for literally every property.
Also, even if you have a backing variable, you shouldn't use it from outside the script it's declared in, because the point is for the property to be the public interface. Therefore it's not important to memorize which properties have backing variables.
This proposal basically works the same as ES6 getter/setters, except in Javascript there is no such thing as "direct access" to the property (inside of the getter/setter function, or anything, like the current proposal),
As I said, in this proposal you do have direct access inside the getter/setter. So you don't need a backing field unless you need direct access somewhere else.
@vnen
As I said, in this proposal you _do_ have direct access inside the getter/setter. So you don't need a backing field unless you need direct access somewhere else.
Yeah, that is exactly what I was stating here:
except in Javascript there is no such thing as "direct access" to the property (inside of the getter/setter function, or anything, like the current proposal)
The purpose of that sentence was to point out how this is different from Javascript. I understand how it works.
@aaronfranke
I would argue that many properties don't need direct access to a backing variable (and also most members in general can just be fields with no getter/setter logic), so no, you don't need this for literally every property.
Also, even if you have a backing variable, you shouldn't use it from outside the script it's declared in, because the point is for the property to be the public interface. Therefore it's not important to memorize which properties have backing variables.
You misunderstand me bro. Nowhere did I say the backing variable would be used outside of the class. I'm talking about organization of the class itself. If I had a variable "x" that needed backing but "y" did not, it makes no sense to be calling "_x" then "y" in the code to set both. This kind of thing would be an anti-pattern and a naming convention nightmare.
Basically, this backing variable idea is dooming every variable used internally in the class to be prefixed with _, where only public facing properties & getters and setters are not. This further complicates things such as local variables inside functions. Either those are prefixed with _ as well or you have this weird mix where local variables & a public property getter/setters look like the same thing from a naming convention perspective, and internal class properties look out of place.
Basically, if I'm going to prefix variables with _, it has to mean something. You don't change your naming convention on the fly because the situation demanded it. That is bad design.
The _ prefix is already a convention for private variables so it's nothing new. When you access a variable starting with _ you know it's a private value while when it doesn't have it you are accessing a public value (and the setter/getter that goes with it).
The
_prefix is already a convention for private variables so it's nothing new. When you access a variable starting with_you know it's a private value while when it doesn't have it you are accessing a public value (and the setter/getter that goes with it).
Sure, but you're rewriting the language, and what happens when https://github.com/godotengine/godot-proposals/issues/641 goes in? _ to indicate private variables is only used in languages that do not have actual access modifiers.
Sure, but you're rewriting the language, and what happens when #641 goes in?
_to indicate private variables is only used in languages that do not have actual access modifiers.
GDScript doesn't have access modifiers. That one is a community proposal we don't know yet if that's gonna be done or not.
I still prefer a difference between x and _x than a difference between x and self.x. Again, once this is released if users find it a real problem we consider a special syntax for direct access. The experience with JavaScript doesn't tell me much because usage is different between languages. I feel this is not used often enough to justify this yet.
The experience with JavaScript doesn't tell me much because usage is different between languages. I feel this is not used often enough to justify this yet.
It's just weird, man. Because at least Javascript has a set design philosophy, you cannot store a value in a getter/setter variable, it must be stored in a second variable. Whereas the GDScript proposal does allow you to store the value with the getter/setter, but only in certain use cases. Honestly if it worked like Javascript where you couldn't store the value and were forced to use a second variable all of the time I wouldn't have as much a problem with it, right now I'm getting mixed messages from this design.
I agree properties should only be getters and setters they should not hold data of any kind just refrence it
@vnen if the "default backing field", the one with the same name as the property, is never accessed in the setter*, is it planned to avoid initializing it. So there is no unused, potentially large, object allocated?
* (or in the getter, I guess, but accessing it only there would be a bug, I'd say, and should maybe be flagged with a warning.)
@vnen if the "default backing field", the one with the same name as the property, is never accessed in the setter*, is it planned to avoid initializing it. So there is no unused, potentially large, object allocated?
Yes, the space is allocated only if you use the variable.
Honestly if it worked like Javascript where you couldn't store the value and were forced to use a second variable all of the time I wouldn't have as much a problem with it, right now I'm getting mixed messages from this design.
Its the same for python, C#. I think you really can't solve problem 5 while having these weird "maybe" generated variables. Because you would just invert the confusion as long as there are ways to use x = as direct access variable and as a property. Every programming language that has properties that I know of uses the name of the property exclusively as syntax sugar for getter/setters, never is any value actually stored under that name. If you try to use the property's name as a variable in the property definition, good luck. This way it is clear that you always mean property access, because nothing else exists.
This is the only real "consistency" that you can have. I strongly disagree when @vnen calls it consistent behaviour when its sometimes direct access and sometimes property access, even if its "consistently" sometimes this and sometimes that. You still have to remember this which is quite hard if you had exposure to properties in any other programming language. I think just explaining these two different variants to anyone who has never heard anything about properties is a nightmare, more so than explaining the direct access case to someone who is "only" familiar with properties in every other language.
Further, when I had just stumbled upon properties in GDScript while reading code and seeing both ways of doing it -- assuming that I would actually be able to infer the behaviour -- I would probably have assumed that the way with backing variables is a hacky misuse and that there is now an allocated but unused variable. I would probably refrain from using it with backing variables because I don't like hacky misuses.
I think this is why all the other languages force @Giwayume to come up with different internal/external names. Its just way simpler to understand and keep track of. And if you at one point really want to change just the variable directly just use _x = or whatever you use as your internal variable naming convention. We are all consenting adults here, as Guido van Rossum once said. But at least its easy doable and very obvious. Not so if you have defined the property with that auto-generated shadowed variable.
The first syntax (in the first two blocks of @vnen 's initial post) is the only one that makes sense to me in this context. But my taste is that it should look more like a function, because it is syntax sugar for functions. So I would prefer dead simple:
var milliseconds: int = 0
get seconds: int:
return milliseconds / 1000
set seconds(value: int):
milliseconds = value * 1000
But this is just my personal taste.
Edit: Forget my taste. This syntax would allow you to do weird stuff like
var x_i: int = 0
var x_f: float = 0
get x: int:
return x_i
set x(value: float):
x_f = value
I don't know if its necessary to allow this. Probably just @vnen ' s proposal minus the awkward generated variables (;-)) is the better solution.
After https://github.com/godotengine/godot/pull/40598 is setget always called inside the same class to solve (5)? This is a bit limiting.
For example, I have a constructor for a UI element that takes 2 objects and updates its text after that depending on their value only once.
But when I access any such object of this class from the outside, then I update automatically update UI text.
So, after 4.0 my constructor will trigger text change twice. I can avoid it by adding boolean variable, but it will not very readable.
@Shatur95 In your particular case, I see that you have both resize and set_team_number calling _update_text, and both of those are called in the constructor. If you need all of those to stay the same, you can just use a backing field. But I'm a bit confused, with your current code, wouldn't _update_text never run if typeof(slots) == TYPE_INT_ARRAY?
@Shatur95
The proposed solution here is this:
var _team_number: int
var team_number: int:
get:
return _team_member
set(number: int):
_team_number = number
_update_text()
func _init(tree: Tree, number: int, slots) -> void:
_team_number = number
# The rest of your constructor
@aaronfranke
But I'm a bit confused, with your current code, wouldn't _update_text never run if typeof(slots) == TYPE_INT_ARRAY?
I think their personal coding problem is off topic here, Though I realize this thread is very long already.
If you need all of those to stay the same, you can just use a backing field.
The proposed solution here is this:
Thanks the tip about backing field. The properties syntax should improve the readability of the code, but for me personally, the option with a backing field looks less readable.
It was a little more convenient in 3.2, because I could change the values of variables without calling the setter directly or just call setter function (or via self) when needed.
Off topic
But I'm a bit confused, with your current code, wouldn't _update_text never run if typeof(slots) == TYPE_INT_ARRAY?
Oh, yes, thanks. I just forgot to add _update_text() after loop. Just not tested this condition for now.
We discussed this in a meeting with the core devs and since this is already implemented with https://github.com/godotengine/godot/pull/40598, then it can be closed.
Note that the implementation took in consideration the discussion here, by finding a compromise with differing opinions.
Discussion can still happen here. Any idea for a big change in the syntax/behavior should be made as a new proposal.
Is there docs somewhere for how it was implemented?
Before I get into concerns I want to say that I do like the direction this feature is going.
I see some potential confusion/issues with initialization. In most languages with properties, the backing field and property have separate initializers so you can choose whether to go through the setter or not. Example:
var _foo: int = 0 # < Initializing backing field. Setter not called
var foo: int:
... #property stuff
var _foo: int
var foo: int = 0: # < Initializing property. Setter is called
... #property stuff
Usually, having the backing field and property separate don't matter. With gdscript, however, we have @export to worry about.
Under the old solution, the backing field and the property were tied together in a way that export could get extra data, like default values. Breaking that tie gets makes exports a little confusing (which, again, most languages don't have to worry about.)
The second use-case from above is fine, but the first use case from above gets confusing.
var _foo: int = 0 # < Initializing backing field. Setter not called... the first time
@export var foo: int: # < Can't tell default val. Val always saved, thus setter always runs after first save.
... #property stuff
I'm not sure if there are other implicit issues with breaking the tie. Also, _I know this isn't a super big issue_. I just could see folk's assumptions about other languages leading to confusion as to why the setter is getting called, which leads to issues being opened and stuff. Getting ahead of it by making sure the docs are good would be... good.
Most helpful comment
In order to expand on "C# does it" I did a quick research (please correct me where i'm wrong) about setters in other languages. I've checked languages that were considered to use for scripting in Godot plus Haxe. My results:
Explicit setter's value: Lua, Python, Squirrel, JavaScript, ActionScript, Haxe
Implicit setter's value:
Personally i consider implicitness of setter's value a small issue which does not justify reserving a word.