Godot-proposals: Add union types to Typed GDScript

Created on 8 Aug 2018  路  11Comments  路  Source: godotengine/godot-proposals

EDIT: Tried to format it according to the 'template'.

Describe the project you are working on:
I was working on a game using Godot. (Have since moved to another engine due to many issues with Godot.)

Describe the problem or limitation you are having in your project:
(Original text)
The main reason I want it is due to Array and Dictionary type not being able to receive null values and I want to be able to use null values as the "default" value for functions.

This is a very common pattern in my pre-existing code and without this ability I'm not sure what to use as "default" values. Empty arrays and dictionaries? That could cause needless memory allocations I think. Also, there could be a difference in meaning between passing an empty array and passing null to a function, for example. Also it is simpler to just use null and compare/check for null.

Dictionary is used as an 'anonymous object' very often, so it makes a lot of sense to allow null. Since the type does not allow it, union types could help.

This is the biggest issue that makes me not able to use Typed GDScript more in my code.

Describe the feature / enhancement and how it helps to overcome the problem or limitation:
Union types would be written as Type1 | Type2 in place of the type.

Describe how your proposal will work, with code, pseudocode, mockups, and/or diagrams:
Examples:

# heal the target (which has its data in a Dictionary) if specified, otherwise if no target specified, heal self
func heal(target: Dictionary| null = null):
    # heal
    ...
# a function that receives a parameter that is either an int or a String
func do_something(parameter:  int | String):
    # do something with the parameter
    ...

# apply damage to a Player character or an Enemy character
func apply_damage(target:  Player | Enemy):
    # apply the damage
    ...

If this enhancement will not be used often, can it be worked around with a few lines of script?:
The workaround is to not specifiy types and thus lose type safety.

Is there a reason why this should be core and not an add-on in the asset library?:
It is part of GDScript which is core.

gdscript

Most helpful comment

I'd say the handling of nullable should be a separate issue and not handled by union types. For Type safe nullables in other languages like TypeScript and Kotlin, you usually just suffix the type with a ?, which is a nice syntactical trick. If more types should allow nullable other than just Object, then something like that should be available to typed GDScript.

As for Union types, I'm in full support of this, as it's something that languages which do not allow for method overloading pretty much need to have if they're going to allow for types. Python does it this way too, if we want to continue with the ideology of GDScript should be pythonic.

All 11 comments

Isn't Dictionary nullable though? Or is this too confuses typed GDScript?

Isn't Dictionary nullable though? Or is this too confuses typed GDScript?

Yeah, I thought it would be nullable, but no:
image

My initial comment and discussion about this:
https://github.com/godotengine/godot/pull/19264#issuecomment-398391457
https://github.com/godotengine/godot/pull/19264#issuecomment-398395954

From:
http://docs.godotengine.org/en/3.0/getting_started/scripting/gdscript/gdscript_basics.html?highlight=constructor#built-in-types

The only type that can be null currently is Object.

Looking at that list, for example, you can't define a function that receives a Color and allow sending null instead of a Color to use the default color. Or Vector3 or Rect2 etc.

My other feature requests (just dropping the link here so I don't have to search for it in that crazy long thread again .. will maybe create issues for more of them later)
https://github.com/godotengine/godot/pull/19264#issuecomment-398398236

I'd say the handling of nullable should be a separate issue and not handled by union types. For Type safe nullables in other languages like TypeScript and Kotlin, you usually just suffix the type with a ?, which is a nice syntactical trick. If more types should allow nullable other than just Object, then something like that should be available to typed GDScript.

As for Union types, I'm in full support of this, as it's something that languages which do not allow for method overloading pretty much need to have if they're going to allow for types. Python does it this way too, if we want to continue with the ideology of GDScript should be pythonic.

I'm not against this, but don't expect it for 3.1. I deliberately made the typing as simple as possible because I knew it would break a lot of stuff (as it did) and complicating it would break even more. We also have to evaluate this carefully, because union types are more prone to runtime errors (though less than a true Variant) and it will lose the benefit of the typed optimizations.

Array and Dictionary type not being able to receive null values

Only Objects can be nulled, Array and Dictionaries are a "primitive" type (that is, they are defined inside Variant), so they are not considered objects.

Empty arrays and dictionaries? That could cause needless memory allocations I think.

null is also a Variant, I don't think empty Arrays and Dictionaries allocate more memory than that.

Also, there could be a difference in meaning between passing an empty array and passing null to a function

And that's why null is not allowed. We certainly could add nullable or sum types, but I wouldn't expect an int to be null, and the same logic applies to Arrays and Dictionaries since they're all built-in types.

if we want to continue with the ideology of GDScript should be pythonic.

We don't have this idea though. We try to mimic Python for syntax, just because users expect it (since GDScript is already somewhat similar to Python), and it's easier than the bikeshed discussion most of the time. In fact, I'm not sure what's the plan for GDScript, since godotengine/godot#18698 didn't reach a conclusion yet.

We could have tagged unions (ala Rust, Scala, Haskell) instead of unions if type safety is an issue. It wouldn't solve the problem of compatibility of old code, though.

For Type safe nullables in other languages like TypeScript and Kotlin, you usually just suffix the type with a ?, which is a nice syntactical trick. If more types should allow nullable other than just Object, then something like that should be available to typed GDScript.

Yes, that would also be nice.
How it would look in the example above:

# heal the target (which has its data in a Dictionary) if specified, otherwise if no target specified, heal self
func heal(target: Dictionary? = null):
    # heal
    ...

Just wanted to say that I also would love union types but not for nullable, but for errors, for example I'd love to be able to have my parser do something like:

func compileSource(source: String): CompiledBlah | SourceError

and if we're going the typescript/haskell route, then being able to define types as union of other types (type CompileResult = CompiledBlah | SourceError) would be even better.

For now, I'll just stick to not having a type (which is kinda sad, would at least like to have a any type, at least until better options are available)

Having full-on unionized types seems like overkill to deal with the actual problem of needing to handle nullable / optional parameters in a method. As mentioned, only Objects, not Arrays or Dictionaries, can be null for a reason. Given this, I think the only appropriate solution to this problem would be to add support for method overloading to GDScript. This way, you can re-define a method multiple times with multiple sets of typed parameters and have confidence that the logic you write will apply only to those particular objects.

I suppose, to play Devil's Advocate, that might lead to people creating empty implementations of a method that just accepts null, purely so they can create optional operations...

func use_array(p_array: Array):
    print(p_array)
func use_array(p_array: null):
    pass # do nothing. Why can't we just make p_array nullable and check inside the method?

...but that also seems like more of a problem regarding one's use of the method itself. Why call use_array at all if you are passing a null value into it, i.e. the problem solves itself by modifying one's API to execute the if-check at the time of calling the function rather than inside of it. Now, whether that's a positive change or not (since it takes the parameter-handling logic out of the method, so-to-speak) is up for debate I suppose.

@chanon Can you adjust the OP to fit the proposal template?

EDIT: ok I will try to adjust it.

I create a method that gets Array as an argument, but say I'd like it to work with PoolStringArray as well.
Now I can't specify arg type at all.

Imo In such cases some kind of union types really could be helpful and flexible

func(arg: Array | PoolStringArray):
Was this page helpful?
0 / 5 - 0 ratings

Related issues

PLyczkowski picture PLyczkowski  路  3Comments

SpyrexDE picture SpyrexDE  路  3Comments

wijat picture wijat  路  3Comments

SleepProgger picture SleepProgger  路  3Comments

WilliamTambellini picture WilliamTambellini  路  3Comments