Open lukostello opened 4 years ago
This was discussed previously in https://github.com/godotengine/godot/issues/18345 and https://github.com/godotengine/godot/issues/6300 (and https://github.com/godotengine/godot/issues/1736).
So far consensus has been that such a feature would not be necessary in GDScript.
Consider that it is mostly sugar for a variable assignment:
with some.thing:
property = 4
# - can also be written as ->
var x = some.thing
x.property = 4
And it will behave the same.
And at times, it can be confusing to someone unfamiliar with the semantics:
with $node_a:
$node_b.position = position
# Does it mean this:
get_node("node_b").position = get_node("node_a").position
# Or does it mean this?
get_node("node_b").get_node("node_a").position = get_node("node_a").position
# Or even one of those?
get_node("node_b").position = position
get_node("node_b").get_node("node_a").position = position
var a = 5
with b:
c = a
# Does it mean this:
b.c = 5
# Or does it mean this?
b.c = b.a
# Likewise
with x:
y = z
# Does it mean this:
x.y = z # which is an error if z is undefined
# Or does it mean this?
x.y = x.z
In addition it might result in some nontrivial code changes to autocompletion (since it has to be told how with
statements should be autocompleted)
This is a proposal, however, so feel free to discuss it, if you consider that the feature can be beneficial in an object-oriented language such as GDScript.
The way I envision it (and remember it in GML) is that it is effectively a writing a method in the class of the instance being "withed" But it doesn't always make sense to organize that method in that class. Sometimes it makes more sense to organize it somewhere else.
Take for example a ball. Suppose this ball is used for many sports. And in each sport the ball behaves differently depending on how it is used in each sport. So rather than writing a function
use(sport)
{
match sport:
baseball:
hitIt()
bowling:
rollIt()
football:
ThrowIt()
}
you'd write in the baseball class
with(ball){
HitIt()
}
Note that the methods would actually be written there I just wrote as if they are being called there as a shorthand. Its not a perfect example. But I think its enough to illustrate what I'm imagining. Its an effective way to put what would normally be methods of another class in a different class for organizational purposes. Mostly I don't like having to write otherObject.property over and over. But I also want to keep by code organized in a way that isn't cluttered and weird.
This was discussed previously in godotengine/godot#18345 and godotengine/godot#6300 (and godotengine/godot#1736). So far consensus has been that such a feature would not be necessary in GDScript.
At least that's a demonstrable number of feature requests I'd say, including this one.
See discussion in godotengine/godot#25669. In there people suggest adding with
keyword for variable scope/visibility. What if the same keyword could implement the functionality behind both this proposal and act as a scope statement?
By the way, the with
keyword as described here seems to be used in Pascal too. In theory this could also help with dictionary operations:
collision = {}
collision["point"] = result.point
collision["normal"] = result.normal
collision["rid"] = result.rid
collision["collider_id"] = result.collider_id
collision["shape"] = result.shape
collision["linear_velocity"] = result.linear_velocity
collision["metadata"] = result.metadata
vs:
collision = {}
with collision:
point = result.point
normal = result.normal
rid = result.rid
collider_id = result.collider_id
shape = result.shape
linear_velocity = result.linear_velocity
metadata = result.metadata
Alternatively, scope
keyword could be used for that. With no arguments passed to scope
, it would act as you expect, well, creating a scope. If it takes more than one argument, then it would work just like the suggested with
: "looking up" fields/properties inside an instance/variable (for assignment at least), also creating a new scope:
var a = Vector2(0, 0)
scope:
var a = Vector2(1, 1)
print(a) # prints Vector2(0, 0)
var a = Vector2(0, 0)
scope a:
x = 1
y = 1
print(a) # prints Vector2(1, 1)
Dart has so called cascade notation, which does the thing being described (acting on an object in sequence).
var button := Button.new()
..icon = Texture.new(...)
..text = "Press me."
..flat = true
add_child(button)
@katoneko I'm afraid that particular syntax doesn't save any meaningful amount of space (horizontal or vertical). However, it makes refactoring harder as you may have to move the initial var
declaration when moving variables around.
In general, I think we should be optimizing for easier refactoring rather than aiming to type the lowest amount of characters possible. Many other modern languages seem to be going in that direction. Also, explicit is better than implicit :slightly_smiling_face:
Also, explicit is better than implicit
If everything is explicit, nothing becomes explicit, so we get unnecessary verbosity over explicitness at the end of the day, which doesn't allow the developer to focus on things that really matter: algorithms.
Also, making a language explicit makes more sense for languages with static typing rather than dynamic, which is implicit. I'd like GDScript to remain its implicitness to some extent, but with the advent of static typing in GDScript people tend to choose being "explicit" by annotating each and every variable/method almost religiously, without considering the practical aspect of doing so.
Following the principles which are never questioned is the worst thing a person can do in terms of individuality and the problem at hand. Mentioning general development principles just signifies that there's no certainty over the feature being useful or not.
But thinking in terms of collective, mass development which is so inherent in open-source software development, I'd say that yeah explicit may be better than implicit and may be the actual reason for those languages adopting such principles, because that seems to be the bulletproof way of ensuring readable, self-documenting code which can connect developers on the mental map level, and because a lot of people don't seem to document the features in the first place due to laziness/lack of resources/time/motivation, you name it. It all boils down to the human factor as I say.
Краткость — сестра таланта (Brevity is a sister of talent) - Anton Chekhov.
Bold IMHO. 🙂
in gml it can be used with a instance or a type
for example if you do..
with(get_node("Sprite")):
h_frames = 1;
it only works on that instance. but it you do..
with(Sprite):
h_frames = 1;
it does it to all Sprites instances that exist in the game
Working with all instances of a type sounds dangerous to me, so I think it is better to make it work for arrays, and ask users to use groups.
In this case it would become something like this for loop:
for x in get_tree().get_nodes_in_group("Sprite"):
x.h_frames = 1
But I find a bit less reason to make with
work with both arrays and actual objects. Orthogonal features are usually better, as they allow things to be implemented only once (less surface for bugs) and helps users decide which one to use (less time wasted weighting alternatives).
In this case, with(Sprite): h_frames = 1
might be written as:
for sprite in get_tree().get_nodes_in_group("Sprite"):
with sprite:
h_frames = 1
# Alternatively, with less indent:
for sprite in get_tree().get_nodes_in_group("Sprite"): with sprite:
h_frames = 1
I think that allowing the user to add logic to every instance of a type could be slow but it might also have be good for sorting nodes.
maybe there could be a optional argument to the with statement like so..
with Node2D, position.x > 0 && position.x < 24:
#all node2d's with a x position between 0 and 24
I would just like to add that this feature would streamline building finite state machines in Godot and potentially be substitute/replacement for nodes having multiple scripts.
user could add Node
with a script, then wrap it's logic in with
statements like this:
This would be really useful for component oriented design. We could have a whole node dedicated for running logic in a with statement on a specific node. And if we support exporting properties from inside a with statement, it would allow for containerizing logic in scenes.
Describe the project you are working on: I am working on a puzzle game which works similar to Rubik's cubes and combination locks
Describe the problem or limitation you are having in your project: One of my objects is changing the effect of another in a lot of ways and it is tedious to keep doing otherobject.property1.dosomething() otherobject.property1.dosomethingelse()..etc
Describe the feature / enhancement and how it helps to overcome the problem or limitation: if we had a with(instance) method like game maker then it would be less tedious an easier to read.
Describe how your proposal will work, with code, pseudocode, mockups, and/or diagrams: https://docs.yoyogames.com/source/dadiospice/002_reference/001_gml%20language%20overview/401_18_with.html
If this enhancement will not be used often, can it be worked around with a few lines of script?: Either tediously or by writing a method in another class when organizationally it might not fit as well there
Is there a reason why this should be core and not an add-on in the asset library?: Its used fairly often in gml I don't see why it wouldn't be in here.