godotengine / godot-proposals

Godot Improvement Proposals (GIPs)
MIT License
1.16k stars 97 forks source link

Add support pattern matching for `is` and `is_instance_of` #10836

Open MikeSchulze opened 1 month ago

MikeSchulze commented 1 month ago

Describe the project you are working on

I'm working on a unit testing API and had very often the case to manually downcast to explicit types to fix warning issues.

Describe the problem or limitation you are having in your project

If debug/gdscript/warnings/unsafe_property_access is set to the level WARN, we get these kinds of warnings.

The property "position" is not present on the inferred type "InputEvent" (but may be present on a subtype).

func get_mouse_position(input_event: InputEvent) -> Vector2:
    if input_event is InputEventMouse:
        return input_event.position
    return Vector2.ZERO

The current solution to solve it need an extra variable assignment or implicit downcast.

func get_mouse_position(input_event: InputEvent) -> Vector2:
    if input_event is InputEventMouse:
        return (input_event as InputEventMouse).position
    return Vector2.ZERO

# or
func get_mouse_position(input_event: InputEvent) -> Vector2:
    if input_event is InputEventMouse:
        var me := input_event as InputEventMouse
        return me.position
    return Vector2.ZERO

Describe the feature / enhancement and how it helps to overcome the problem or limitation

It should be possible to write simpler/shorter code by using auto downcast to a variable when using is or is_instance_of.

This will clean up the code and is state of the art for a modern programming language.

The simplicity of pattern matching can be deceptive. Here is an example of how developers usually implement an equals() method in a class. In the following code, the class MyNode implements the equals to use explicit downcast.

class MyNode extends Node3D:

    var _value: int

    func equals(obj: Object) -> bool:
        if obj is MyNode:
            var other: MyNode = obj
            if other.position == position and other._value == _value:
                return true;
        return false;

The following code shows how the preceding equals() method could be simplified by using pattern matching for is and the further simplification of if statements:

class MyNode extends Node3D:

    var _value: int

    func equals(obj: Object) -> bool:
        return obj is MyNode mn and mn.position == position and mn._value == _value:

Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams

The pattern matching should work like in Java14. If a variable defined after an is or is_instance_of expression, it should be assigned if the expression true otherwise continues in the code as the if/else describes.

Simple example:

func get_mouse_position(input_event: InputEvent) -> Vector2:
        # do automatic downcast to `InputEventMouse` and create the local variable `ime` as type of `InputEventMouse`
    if input_event is InputEventMouse ime:
        return ime.position
    return Vector2.ZERO

It should be allowed to write better/compact if statements


     # the variable obj is a Variant
     if obj is Node3D node and node.position == position and node.visible == true:
        ...

If this enhancement will not be used often, can it be worked around with a few lines of script?

Needs explicit downcast

Is there a reason why this should be core and not an add-on in the asset library?

This will clean up the code and is state of the art for a modern programming language and should be a core feature.

dalexeev commented 1 month ago

See also: