Search code examples
gdscript

Execute a side effect when declaring an expression?


I have a ternary expression with multiple conditions, where I would like to perform an assertion on one case:

var result = (
  "bananas" if input == "yellow"
  else "oranges" if input == "orange"
  else {
    assert(false, "Unsupported fruit color %s detected!" % input)
    "bananas" # fallback to default
  }

This is not, in fact, valid GDScript and does not compile because this is not valid syntax:

{
  someStatement
  anExpression
}

But does GDScript provide a way to do such a thing, executing a statement for its side effects and then returning an expression?


Solution

  • GDScript does not support statements inside of expressions (ternary expression or otherwise).

    You can, of course, use and statements (such as if). Although statements do not return. But you can do this:

    var result = "bananas" #default
    if input == "yellow":
        result = "bananas"
    elif input == "orange":
        result = "oranges"
    else:
        assert(false, "Unsupported fruit color %s detected!" % input)
    

    Or using a match statement:

    var result = "bananas" #default
    match input:
        "yellow":
            result = "bananas"
        "orange":
            result = "oranges"
        _:
            assert(false, "Unsupported fruit color %s detected!" % input)
    

    Which I believe to be easier to read.


    Anyway, to answer the question, what comes to mind is to use a method. Since a method can have statements and return.

        var result = (
            "bananas" if input == "yellow"
            else "oranges" if input == "orange"
            else fail(input)
        )
    

    Where fail is a method that looks like this:

    func fail(input) -> String:
        assert(false, "Unsupported fruit color %s detected!" % input)
        return "bananas" # fallback to default
    

    You didn't tell me which version of Godot you are using. What I said above should work in either Godot 3 or Godot 4.

    With that said, starting form Godot 4, you can use inline anonymous methods ("lambda"), so you might do something like this:

    var result = value if condition else (func(): 
        assert(false, "HELLO")
        return false).call()
    

    I'm showing this simple example to highlight that the indentation gets awkward because the inline anonymous method requires and indention level, but we also need to wrap it and parenthesis and call it.

    Now, for your case, it would be like this:

        var result = (
            "bananas" if input == "yellow"
            else "oranges" if input == "orange"
            else (func (input) -> String:
                assert(false, "Unsupported fruit color %s detected!" % input)
                return "bananas").call(input)
        )
    

    I'll reiterate that I believe the match version is easier to read, but you do you.