Replies: 18 comments 43 replies
-
I'm more of a static typer with defined structs and types, so I'm very rigid when it comes to structs, but since it's GDScript, I understand that people prefer more flexibility when it comes to how the data's represented. This is definitely about no other language other than GDScript, but if we were to be attaching bindings, I hope they can be represented as language-specific structs rather than array-like structs. That's just my take on this, though. |
Beta Was this translation helpful? Give feedback.
-
Another question that's just occurred to me is should subsets of names and/or values suffice for considering them equal?
What would
|
Beta Was this translation helpful? Give feedback.
-
I feel the same way. To be honest, I'm not able to come up with a single use case where considering them equal would be beneficial, it just struck me as an edge case and I wanted to see if others had use cases where it'd either benefit them or give them headaches. |
Beta Was this translation helpful? Give feedback.
-
I think that while the other proposal describes structs implemented in terms of dictionaries, they shouldn't expose their implementation to users. They should be convenient and clear in their own way instead of being dictionary-like because of their implementation. It should be reasonable to change the implementation of a performance -focused feature away from dictionaries in the future. 1: What should the syntax be for creating a struct instanceMy experience with Godot's type inference implies that the first indicates that the following two are valid: var named_int : NamedInt = [name = "Godot", value = 4]
var named_int := [name = "Godot", value = 4] as NamedInt
var named_int := [name = "Godot", value = 4] # surely not correct! So I'd prefer the However, given that classes are created with var named_int := NamedInt.new(name = "Godot", value = 4) 2. When should two structs be considered equalI think the answer for this depends on whether the implementation makes structs types behave like golang interfaces: allows OtherNamedInt to be passed to a function expecting NamedInt because they have the same data. (structural typing)
3. How should structs appear in documentationIf a function returns a struct with 10 fields or a struct containing other structs, only A ( |
Beta Was this translation helpful? Give feedback.
-
I personally think that A is way more clear in what it's doing, and I think that for documentation option a is also better. When it comes to equality, here's my two cents. struct Foo:
var a : int
var b : String
struct Bar:
var a : int
var b : String
var c : bool = false
var structa := Foo(a = 0, b = 1)
var structb := Bar(a = 0, b = 1, c = true)
# structa == structb should be false, as their absolute contents are different. Because it doesn't make sense for two structs with different sets of variables to be equal (even if their contents are the same), I propose a method which checks all the variables with shared names for equality. Something like # returns true, because `a` and `b` have the same values in both.
structa.named_parameters_equal(structb) Obviously this needs more clarification on how it should work (some people may want floats with no decimal point and ints to be treated as equal for reasons), but I think that a few functions for checking equality of structs with names/positional values/etc, would be the best solution for this. |
Beta Was this translation helpful? Give feedback.
-
Question 1 → a) Question 2 → same reference; the rest of options (same type structs, same name, same value, and same name and value and struct type, etc etc) should be evaluated with operators and auxiliary methods. Question 3 → definitely not [] and {} for its members to avoid confusion with arrs and dicts.
|
Beta Was this translation helpful? Give feedback.
-
I'd rather prefer something more dictionary-based. I'm assumed they will be otherwise used very much the same as dictionaries even if the implementation is very different. Putting square braces, or the like is more likely to confuse users as to how structs are used--even if the implementation is more array like, that's not a detail the user needs to care about. Let me just spitball something like: struct my_struct {name = "Godot", value = 4} With static typing I think that, as with "enum," a struct should be kind of its own thing, not introduced by var. I'm not certain what I think about equality. My usual inclination would be to say that my_struct is a type, and only objects of the same type are equal, but for duck-typed languages that might not be so useful. I guess the question becomes, for what purpose has the user created objects with the same signature but different aliases? |
Beta Was this translation helpful? Give feedback.
-
I like the idea of duct-typing (if two structs have the same fields, allow them to freely being assigned to each other). What do you think about being able to upcast structs? The ability to assign a value of
|
Beta Was this translation helpful? Give feedback.
-
If we arranged the struct fields in memory alphabetically, then in this example:
If we were to consider |
Beta Was this translation helpful? Give feedback.
-
For the benefit of folks that mostly interact with these discussions via email notifications, I'm copying a fairly lengthy update I've made to my OP.
These are related to my question 2 above, but go a step further by looking at cases where one struct has the same fields as another but in a different order or one struct has more fields than other. For example, let's consider a few different structs. struct NamedInt:
var name : String
var value : int
struct Named:
var name : String
struct NamedValue:
var name : String
var value : Variant
Struct IntNamed:
var value : int
var name : String
var named_int := NamedInt(name = "named_int", value = 3)
var named := Named(name = "named")
var named_value := NamedValue(name = "named_value", value = "not an int")
var int_named := IntNamed(value = 5, name = "int_named") With these in mind, we can think about which of the following structs should and should not support. The lines in the following code block are meant to be read in their own context, not as sequential instructions. # upcasting -----
# implicit upcast
named = named_int
# explicit upcast
named = named_int as Named
# explicit up-construction
named = Named(named_int)
# down casting -----
# implicit downcast
named_int = named
# explicit down cast
named_int = named as NamedInt
# explicit down construction
named_int = NamedInt(named)
# permutation casting -----
# implicit permutation cast
named_int = int_named
# explicit permutation cast
named_int = int_named as NamedInt
# explicit permutation construction
named_int = NamedInt(int_named)
# recursive upcasting -----
# implicit recursive upcast
named_value = named_int
# explicit recursive upcast
named_value = named_int as NamedValue
# explicit recursive construction
named_value = NamedValue(named_int)
# recursive down casting -----
# I think you get the picture by now
# recursive permutation casting -----
# I think you get the picture by now In each of these situations, I could imagine a few different levels of support
Taken together, these amount to A LOT of questions to be answered, but I think they are pretty important design decisions that should not be made lightly. This is my personal stance at the moment, but it could easily be swayed by a good argument.
My reasoning at the moment is loosely based on how subtype polymorphism is defined in the textbook Types and Programming Languages. What do you all think? |
Beta Was this translation helpful? Give feedback.
-
I haven’t followed this discussion too closely but from the update examples you just posted I agree with all points of your stance, especially with downcasting throwing errors unless it uses the constructor |
Beta Was this translation helpful? Give feedback.
-
It looks to me like a number of these questions boil down to whether the GDScript structs should be nominally or structurally typed. For my part, I prefer nominal typing, and I essentially agree with @Stwend's claim that
Since I think that the answers to most of the questions posed in this thread become fairly straightforward if we accept the premise that a struct's type should be considered part of its identity, I'd like to offer a more robust defense of this position. I'm skeptical of the sort of structural typing ideas being floated here for a couple reasons.
The
|
Beta Was this translation helpful? Give feedback.
-
From the issue: #7329 (comment)
|
Beta Was this translation helpful? Give feedback.
-
Can't you just make a new class? |
Beta Was this translation helpful? Give feedback.
-
so there doesnt exist any room to a future possible lightweight data structure that lives in the memory stack? (due to Variant) |
Beta Was this translation helpful? Give feedback.
-
Hi all, it's been a while since I've updated this discussion, but as my work on adding Structs to GDScript is nearly ready to review, I have encountered another essential question. I've updated my original post with this question as well for posterity. Question 5. When (if ever) should casting between Structs and Arrays or Dictionaries be automatic?Consider the following example. struct NamedInt:
var name : String
var value : int
var named_int := NamedInt(name = "Godot", value = 4)
named_int = ["Godot", 5] # should Array to Struct be automatic?
named_int = {named = "Godot", value = 4} # should Dictionary to Struct be automatic?
var array := []
array = NamedInt(name = "Godot", value = 4) # should Struct to Array be automatic?
var dictionary := {}
dictionary = NamedInt(name = "Godot, value = 4) # should Struct to Dictionary be automatic? Each of the cases above will certainly work if the user adds the appropriate explicit cast using I'm interested to hear what you all think. |
Beta Was this translation helpful? Give feedback.
-
Maybe make implicit casting trigger a breakpoint, but not crash. Or at
least print an error in debug builds.
If something slips through in a production release, it would be nice to
avoid crashing the player's game.
…On Mon, Oct 7, 2024, 7:09 PM nlupugla ***@***.***> wrote:
I like the idea of having an "unsafe cast" warning.
The reason I'm inclined against implicit casts is that I am imagining that
if a user has gone through the effort of creating a struct, rather than a
simple Array or Dictionary, that user probably cares about static typing
enough that they wouldn't want their types to be sneakily stripped away.
That said, I could easily imagine users that see things differently, so I'm
happy to be persuaded in the other direction.
—
Reply to this email directly, view it on GitHub
<#7903 (reply in thread)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AACHIV2XDKGUEMI6EH54HWDZ2MIB7AVCNFSM6AAAAAA5LGJJ46VHI2DSMVQWIX3LMV43URDJONRXK43TNFXW4Q3PNVWWK3TUHMYTAOBXGM3TCMA>
.
You are receiving this because you were mentioned.Message ID:
<godotengine/godot-proposals/repo-discussions/7903/comments/10873710@
github.com>
|
Beta Was this translation helpful? Give feedback.
-
I learned today that the ability to pass arguments by name in general is untenable due to some GDScript limitations #902. An argument could be made that an exception could be made for Struct constructors, but I think it's probably best to keep things consistent with the rest of the language. So if constructing via Note you could always use a Dictionary if you wanted to assign values by name via |
Beta Was this translation helpful? Give feedback.
-
Hi Godot community! I have been having a lot of fun implementing the proposal by @reduz #7329 for adding struct-like types to GDScript. That proposal was mainly concerned with how to implement structs as Arrays with individually named and typed elements. It briefly discussed the syntax for using them in GDScript and C++, but did not go in to details on how this new feature should behave.
I'd like to open a channel for discussing the nitty gritty details of structs as a language feature in GDScript. To be clear, I don't want to discuss how they will be implemented, that is better discussed in the original proposal. I've listed below a few of the questions that I'm currently puzzling over.
For the sake of concreteness, let's consider a struct defined in GDScript like so:
Question 1: What should the syntax be for creating a struct instance?
Some possibilities:
a)
b)
Question 2. When should two structs be considered equal?
Question 3. How should structs appear in documentation?
Suppose a function returns a
NamedInt
. Here are some ways that fact could be displayed in documentation.a) NamedInt
b) Struct[String, int]
c) Array{String, int}
d) [name : String, value : int]
More to come...
There are many more questions to be answered about the new struct-like type. I'll try to update the post with particularly hairy ones that are brought up in the discussion.
Looking forward to hearing everyone's ideas!
Edit December 13 2023, 9:54:
Thanks for the discussion so far everyone! There seems to be a consensus that when it comes to creating a struct, the
syntax is preferred over something like
Another question has come up as I've been working on structs.
Question 4. How should structs be formatted when printed?
For example, what should the following output?
Here are some of the options I'm considering.
a) [name: "Godot", value: 4]
b) {name : String = "Godot", value : int = 4}
c) NamedInt(name = "Godot", value = 4)
What do you think?
Edit January 11 2024, 10:46:
@AndrewAPrice has brought up some interesting questions about duck typing and when two structs should be considered equivalent.
These are related to my question 2 above, but go a step further by looking at cases where one struct has the same fields as another but in a different order or one struct has more fields than other.
For example, let's consider a few different structs.
With these in mind, we can think about which of the following structs should and should not support. The lines in the following code block are meant to be read in their own context, not as sequential instructions.
In each of these situations, I could imagine a few different levels of support
Taken together, these amount to A LOT of questions to be answered, but I think they are pretty important design decisions that should not be made lightly. This is my personal stance at the moment, but it could easily be swayed by a good argument.
My reasoning at the moment is loosely based on how subtype polymorphism is defined in the textbook Types and Programming Languages.
What do you all think?
Edit October 6 2024, 13:44:
Hi all, it's been a while since I've updated this discussion, but as my work on adding Structs to GDScript is nearly ready to review, I have encountered another essential question.
Question 5. When (if ever) should casting between Structs and Arrays or Dictionaries be automatic?
Consider the following example.
Each of the cases above will certainly work if the user adds the appropriate explicit cast using
as
, but the question is if the casts should occur implicitly as well. I am inclined to say that these kinds of casts should never be implicit, but I can also see the appeal of being able to write the very concisenamed_int = ["Godot", 5]
over the more verbosenamed_int = NamedInt(name = "Godot", value = 5)
ornamed_int = ["Godot", 5] as NamedInt
.I'm interested to hear what you all think.
Beta Was this translation helpful? Give feedback.
All reactions