Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add fallthrough for match cases #160

Open
neoxid501 opened this issue Oct 15, 2019 · 7 comments
Open

Add fallthrough for match cases #160

neoxid501 opened this issue Oct 15, 2019 · 7 comments

Comments

@neoxid501
Copy link

Describe the project you are working on:
It's an RPG where when a character levels, specific stats are increased for example speed, skills, etc. The problem that this proposal will be addressing isn't specific to ours but moreso a feature that offers a solution to simplify the amount of work required for a situation.

Describe the problem or limitation you are having in your project:
Admittedly I wouldn't call it a problem, it's more of a QoL change where a keyword is added such as fallthrough where in a match statement, if used the next block is executed without the need for an additional check or comparison.
The problem we initially ran into was that if the player is say level 3, then we would like everything that happens to them on lvl 3 to happen, then 2, then 1.

match 3:
    3:
        print("Only this prints.")
    2:
        print("But I want these ")
    1:
        print("to print too!")

However due to the way that continue currently works where it rechecks the value being matched, only Only this prints is put into console. We eventually found a solution (described below) however I still think the feature would be useful.

Describe how this feature / enhancement will help you overcome this problem or limitation:
I am primarily making this feature proposal because having used switch case before in other languages, I assumed that using the keyword continue would effectively act as a fallthrough. However it seems that Godot will continue to check for a match instead of just executing the next case as it would in Java for example. From my understanding that Godot attempts to be a very flexible toolkit not beholden to other languages/engines, but I think that adding fallthrough simplifies the problem solving needed for certain logic problems.

Show a mock up screenshots/video or a flow diagram explaining how your proposal will work:
Not... entirely sure what to put here, but effectively what I'm proposing would be a keyword fallthrough or something along those lines where when used it continues and executes the next block without doing a check on the values. Adding a keyword this way would maintain backwards compatability so no previously written match statements would need to be rewritten.

Describe implementation detail for your proposal (in code), if possible:
A keyword fallthrough where if used in a match, the next code block is executed without the need for a comparison.

match 3:
	case 3:
		print("This will print")
		fallthrough
	case 2:
		print("And so will this.")
		continue
	case 1:
		print("However not this.")
	_:
		print("But this will also print.")

Console Output:

This will print
And so will this.
But this will also print.

If this enhancement will not be used often, can it be worked around with a few lines of script?:
Using the current match you could do this

match 4:
    4:
        print("However this is very long, ")
        continue
    3, 4:
        print("and if I need to implement say 20 levels ")
        continue
    2, 3, 4:
        print("worth of rewards then the list of matches ")
        continue
    1, 2, 3, 4:
        print("gets really long the longer it goes.")

However this runs into a space problem where if it goes on to say value 100 it takes up a tone of space and it still has to run a check through the list of values.

Another alternative I thought of would be to start from the lowest level and check climbing up using if statements.

var lvl = 4
if(lvl>=1):
    print("But this runs into a problem where ")
    if(lvl>=2):
        print("the indentation will keep pushing ")
        if(lvl>=3):
            print("the code furth off to the right ")
            if(lvl>=4):
                print("which is inconvenient.")

This helps cut back on the number of checks being done, however still runs into a space problem especially with the amount of indentation.

for i in range(1, level + 1):
    match x:
        1:
            print("You're done at level 1!")
        2:
            print("No you fool, level 2!")
        3:
            print("Level 3, actually!")
        4:
            print("No more than level 4!")

This is actually the solution that we're using, however it doesn't solve a case where functionally you would like two of the options to be connected, and one to be independent.

var val = 3
match val:
    case 3:
        print("hello")
        val = 2
        continue
    case 2:
        print("howdy")
    case 1:
        print("whoa")

This now solves the above problem, but requires the changing of the variable val. Alternatively the value could be duplicated to a temp value but that would introduce another variable if fallthrough was available this could be simplified to:

var val = 3
match val:
    case 3:
        print("hello")
        fallthrough
    case 2:
        print("howdy")
    case 1:
        print("whoa")

No temp variable or value changing needed. It also simplifies a lot of the through process of how to solve the issue (as the above code blocks were in fact how we iterated on the issue).

Is there a reason why this should be core and not an add-on in the asset library?:
This could be an asset, however I think there can be situations other than my use case. In general for instance:

If option 3 happens, option 2 also happens.
If option 2 happens, option 3 doesn't happen.
And option 1 only occurs if option is picked.

It's mostly weird to me that seemingly I fallthrough wasn't implemented when match was first created, but of course that's just a personal preference. Though apparently the issue has been raised before so there is precedence.
https://www.reddit.com/r/godot/comments/b8gqli/match_fallthrough_with_continue_gdscript_help/

I apologies if this is way too long and verbose, but hopefully this can get added to Godot's core and if not someone plz make an addon I will use it ty!

@LotsaBugs
Copy link

LotsaBugs commented Oct 17, 2019

As a temporary solution, you could do:

func fallthrough_match(__case : int):
	assert (__case >= 0)
	var __falling_range : Array = range(__case, -1, -1)

	for __c in __falling_range:
		match __c:
			5:
				<do something>
			4:
				<do something>
			3:
				<do something>
			2:
				<do something>
			1:
				<do something>
			0:
				pass
			_:
				assert (false)

That will loop it (__case + 1) times and do something until it hits 0.

@vnen
Copy link
Member

vnen commented Jan 27, 2020

I thought continue did this, but oddly it actually tests the next pattern. I'm not sure why it was implemented that way, doesn't seem useful to me. I'll probably remove this in the next version.

Honestly though, for the OP's system I would devise some in-script abstraction to perform this, because using a bunch of match blocks doesn't seem very maintainable (100 patterns would be a mess, fallthrough or not). Not judging the way you code (I've done worse to ship something), but we shouldn't be facilitating bad practices.

For instance, I would add conditions and results to some array, then walk through the list, running everything greater than the given value. If you need actions, the array can be a list of functions.

@starry-abyss
Copy link

starry-abyss commented Jan 27, 2020

oddly it actually tests the next pattern. I'm not sure why it was implemented that way, doesn't seem useful to me. I'll probably remove this in the next version.

What?? Remove continue? I need it in for loops obviously. And that's how it works anywhere else. :D
Ah, nvm, I happened to read the example where match was inside for.

@Calinou Calinou changed the title Match-Case Fallthrough Add fallthrough for match cases Jan 31, 2021
@xsellier
Copy link

Since the match keyword looks like the Rust match keyword it would be better not to have break or continue or fallthrough but something like that:

var value = 2

match value:
  1:
    print("Value is one")
  2:
    print("More than one")
  1, 2:
    print("That's all folks")

It should print:

# More than one
# That's all folks

@Calinou
Copy link
Member

Calinou commented Nov 24, 2021

Since the match keyword looks like the Rust match keyword it would be better not to have break or continue or fallthrough but something like that:

Implicit fallthrough is the source of many bugs in programming, so I would rather not have it become the default in GDScript. Having an explicit fallthrough keyword sounds like a better idea to me, so that you can still use continue within a match branch (in a for or while loop).

@xsellier
Copy link

It is an explicit fallthrough since there are 2 caes that match the same condition.
1 and 1, 2, it is just smarter I believe since you don't have to put all the fallthrough beside each other.

@akien-mga
Copy link
Member

akien-mga commented Jul 3, 2022

Referencing godotengine/godot#37684 which aimed at implementing this proposal (but got stale as there was no consensus yet on how the feature should behave and/or be named exactly).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants