I'd like to suggest adding pattern matching to GDScript and I would be willing to implement it.
As of now, GDScript doesn't feature a switch
statement - you have to chain multiple if statements.
_(All the syntax here is just a first sketch, suggestions welcome)_
Pattern matching is the inspection of the structure and contents of a value to branch execution.
So for simple values (like numbers, strings) it acts like a switch statment.
var num = 42
match num:
0: print("Zero")
23: print("Half way there")
42: print("The answer")
The difference to a switch
statement is that those cases can be nested. You can match on the contents of an array for example.
var array = [13, "37", ["test", "array"]]
match array:
[13, "37", ["test", "array"]]: print("Wow, a perfect match!")
[13, 37, ..]: print("l33t man") # .. matches the rest of the array
[]: print("empty array")
Matching on a dictionary would work the same, except that matching on keys wouldn't be possible.
Dictionaries would be matched on the key and the value.
Instead of simply branching depending on the equality of values it would be possible to bind new variables.
When the first pattern that matches the value is encountered the corresponding branch will be executed. If a pattern doesn't match the next one is tried. So the *more general patterns should be defined beneath the more specific.
To bind a new variable you would simply write a new identifier instead of a value.
var array = [13, "37", ["test", "array"]]
match array:
[13, "37", [var a, var b]]: print(a, ", ", b) # a = "test", b = "array"
var n: print("Unknown array structure: " + str(n))
When matching dictionaries only the value would be bindable. e.g. {"test": var a}
Just matching on a new identifier is equal to the default
case in switch
-statements.
_Let me know what you think_ :blush:
Here, have a :+1: from me :) do the haskell :P
I like this because the syntax is basically equivalent to a switch (bar fall-through but who needs this anyway), but can optionally be more powerful when needed
I assume you mean this to accept a block of statements:
var num = 42
match num:
0:
do_the_zero_routine()
print("Zero")
23:
print("Half way there")
42:
print("The answer")
make_it_sparkle()
@vnen Yes, of course!
I don't see how useful such a syntax is compared to the cost of implementing it in GDScript. I almost never had to do that in any game I made. And when I needed this kind of matching it was straightforwardly implemented with some ifs.
Just throwing an idea in here.
In Haskell it's possible to add guards to every pattern, like this:
case [1, 3, 3, 7] of
[1, a, b, 7] | a * 10 + b < 42 -> "Ok, passed the test"
| otherwise -> "That is unacceptable!" -- otherwise = True
_ -> "I won't inspect you! >=D"
Possible GDScript syntax could be something like this:
match [1, 3, 3, 7]:
[1, var a, var b, 7]
if a * 10 + b < 42: print("Ok, passed the test")
[1, var a, var b, 7]: print("That is unacceptable!")
_: print("I won't inspect you! >=D")
I feel like the if
is more pythonesque but it adds a lot more noise to the code.
Also when taking the if
route I don't know if the otherwise
case should be implemented. A fall-through works too like in the example.
@Zylann I think it's much clearer and easier to read than multiple if
s. But sure, this isn't _needed_, but I think it would be a great addition to the language because it's really pleasant to use. But that's subjective of course.
The performance cost shouldn't be that high. I would transform these nested patterns into multiple simple pattern matches. If a pattern doesn't match in the first inspection the next case is tested, so it wouldn't be that much slower than if
s.
Well, I agree that performance penalties should be avoided. I'm not sure if this syntax will or not be more costly than regular if
chain though. It depends a lot on implementation.
@vnen regarding implementation, I want to implement it like described in this book. (The implementation of functional programming languages by Simon Peyton Jones)
edit: I won't implement it that way because GDscript is a dynamically typed language and doing compilation like that would require unnecessarily complex transformations and runtime checks.
Example translation from
match array:
[1, 3, var a]: print(a)
_: print("No match")
to
if typeof(array) == TYPE_ARRAY:
if array[0] == 1:
if array[1] == 3:
var a = array[2]
if array.size() == 3:
print(a)
else:
var n = array
print("No match")
So the complexity of not matched expressions should not have any significant impact on execution speed compared to hand written if
s.
@karroffel well, it looks confusing to me. Not that reading it is confusing, but regarding all current features of GDScript it looks like an alien, and I don't see where this could be used often. I never worked in functional languages like Haskell btw :s
@Zylann it can be used everywhere where a switch could be used. Just read it like a switch statement :wink:
I understand the lack of "need" to have it, but then I could argue that for
loops are unnecessary because there are if
-statements and recursion :laughing:
Like I said, you can do everything in GDScript without a pattern matcher, but once you got used to pattern matching it becomes really useful and powerful.
var inventory = ["sword", "shield", "magic key"]
match inventory:
["sword", "shield", "magic key"]:
print("You came as a pure warrior, Welcome!")
can_open_door = true
break;
["sword", "shield", ..]:
can_kill_monster = true
can_broke_door = true
can_evade_monster = true
break;
["sword", .. ]:
can_broke_door = true
break;
["shield", .. ]:
can_evade_monster = true
break;
@karroffel Will this be possible?
Will a negative pattern format to make ".." not to match with "magic key" for last three cases be implemented? Or "if" statements inside these case blocks better fit for this situation.
Should this pattern matching be as full-featured as regular expressions? :D (not asking for it, some of it just looks similar)
For me having a negative pattern (not a regex) will make a use case, cause it will make scenario coding easy and readable. But if result will be less than that, I would prefer regular "switch" and "if" statements unless there is a side benefit like performance gain or less LoC.
Note: May be more than that will not work either. :)
@hubbyist arrays depend on order, so your example wouldn't work that way. The ..
is just there to ignore the rest of the array and match it (because it depends on order and you don't care about the rest, only the first few elements for example). Dictionaries don't have a specified order, maybe I could add special syntax to just match the keys to use it like a regular set.
Oh and you wouldn't need break
because there is no fall-through
@Zylann No, it will only compare for equality and bind variables and all of that can be nested.
I implemented the parser yesterday. I added a "set syntax" for dictionaries to check if a key is present.
var inventory = {"sword": 20, "shield": 100, "magic key": 2}
match inventory:
{"sword", "shield", "magic key"}:
print("You came as a pure warrior, Welcome!")
can_open_door = true
{"sword", "shield", ..}:
can_kill_monster = true
can_broke_door = true
can_evade_monster = true
{"sword", .. }:
can_broke_door = true
{"shield", .. }:
can_evade_monster = true
So in the example I don't care about the values, I just want to check if a property is present.
It is also possible to use simple constant expressions as a pattern, like this:
var size = 1024 * 1024 * 1024 # GiB
match size:
1024:
print("KiB")
1024 * 1024:
print("MiB")
1024 * 1024 * 1024:
print("GiB")
_:
print("something different")
Today I will transform this into if
s and var
s and then most of the hard work is done
I would love to see this implemented. I ask about switch statements every 3-6 months :)
@karroffel About guards, pattern if cond
sounds pretty good :smile:
(I'm talking about cases like
var arr = [1,2,3]
var first_required_value = 1
match arr:
[x, ..] if x == first_required_value:
foo(bar)
a:
printerr("Bad luck")
)
Would an else/elif
be allowed too?
At first: matching on constants is already finished, so basically a switch (it's finally here!!), now I have to work on the array and dictionary matching...
I don't know how to implement fallthrough efficiently yet. To maximize efficiency I want to sort the patterns, so a general _fallthrough_ could be problematic. (if I don't sort I may have to check array contents multiple times)
@Zylann I still have to think about how to implement guards. else
should be allowed if guards are allowed IMO but there should also be a fallthrough if there is no else
and the other guards fail, so it's a bit problematic right now.
Something like try-catch style may be better than a fallthrough IMO.
var inventory = {"sword": 20, "shield": 100, "magic key": 2}
match inventory:
{"sword", "shield", "magic key"}:
print("You came as a pure warrior, Welcome!")
can_open_door = true
...
...
...
{"shield", .. }:
can_evade_monster = true
nomatch:
print("You can't do anything here!")
It will be more readable and in need may be combined to another match statement or function call.
I think that for no match, an else
would be more readable (not sure if it should be inside or outside the block though).
What about;
match x:
...
...
otherwise:
...
syntax?
Using else
feels like over loading keyword.
Usually, pattern matching uses:
_
= matches anything when there's no match ("wildcard pattern"). Does not bind an identifier.
This is consistent in all languages I know: Haskell, Scala, Elm, Racket, F#, OCaml, Rust. I don't think there's an exception.
So the idiomatic way to do it, to stay sorta in tune with other languages that have pattern matching, should be to use _
.
Incidentally, all those languages usually (not always, Scala does not iirc) implement some sort of Nothing
pattern, for when there's no value (which is different from fallthrough).
So after discussing guards on the IRC (thanks @akien-mga) I think we got a nice compromise.
Instead of adding guards it is possible to use continue
to check the other patterns. So guards would be simple if/else
s in the branching code and failing/fallthrough would be done via continue
.
That keeps the parser simple and the patterns are a bit easier to read.
I think _
should be the default wildcard pattern.
I will do the matching in-order which may be more inefficient but it simplifies a lot of the transformations.
I would honestly just implement the standard switch syntax, simply because
users ask a lot for it, and wouldn' t go much further than that
On Fri, Oct 7, 2016 at 9:12 AM, Karroffel [email protected] wrote:
So after discussing guards on the IRC (thanks @akien-mga
https://github.com/akien-mga) I think we got a nice compromise.Instead of adding guards it is possible to use continue to check the
other patterns. So guards would be simple if/elses in the branching code
and failing/fallthrough would be done via continue.
That keeps the parser simple and the patterns are a bit easier to read.I think _ should be the default wildcard pattern.
I will do the matching in-order which may be more inefficient but it
simplifies a lot of the transformations.—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
https://github.com/godotengine/godot/issues/6593#issuecomment-252236803,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AF-Z24qABylU7GPi4UNXJ1myXyKsGcIdks5qxjcRgaJpZM4KFJl9
.
@reduz I see, but why strip away features that don't need to be used, they are optional. All the switch functionality is there and pattern matching got a lot more common in programming languages in general.
If a new construct is added to the language, I don't see why it should be made less powerful than it could be.
And like I said many times, this syntax isn't written into stone, it could be changed to the switch syntax without tossing away the additional features.
naive question : isn't
match size:
1024:
print("KiB")
1024 * 1024:
print("MiB")
1024 * 1024 * 1024:
print("GiB")
a:
print("something different")
the same as :
switch size:
case 1024 :
print("KiB")
# implicit break
case 1024 * 1024 :
print("MiB")
# implicit break
case 1024 * 1024 * 1024 :
print("GiB")
# implicit break
case a :
print("something different")
# implicit break
???
so wouldn't it jsut be a matter of adding/replacing keywords and keeping the more powerfull pattern matching ?
@SuperUserNameMan It is the same. (but case a
would be default
)
That's what I'm saying, it would be possible to use the switch syntax but keep all the destructive pattern matching features
if using this to do basic switch/case
(without using the more powerful pattern matching features thing), does it adds extra cost compared to a if/elif/else
equivalent :
if size == 1024 : print("KiB");
elif size == 1024 * 1024 : print("MiB");
elif size == 1024 * 1024 * 1024 : print("GiB");
else : print("something different");
?
does it generate more opcodes ?
is the code behind it as fast ?
@SuperUserNameMan basic typechecking is needed to catch invalid equality checks. Other than that the code would be the same as you wrote it.
I support this. The idea of removing functionality from something that the suggester is ok with implementing himself makes no sense. People can use it just like a switch, but also make use of the pattern matching. I don't understand why it has to be dumbed down.
@karroffel : then, i personally see no reasons (me neither) to get rid of the more advanced pattern matching features things that you have already implemented if it can do basic switch/case without extra cost (or just little cost) and if the little extra cost adds a little benefit compared to the if/elif/else
statements.
So it would be just a matter of replacing/adding traditional switch/case/default
keywords to make it look more "familiar" and more "friendly".
(btw, maybe the case
keyword could be made optional, and switch
could be made an alias of match
... this way, everybody could be happy )
(btw, maybe the case keyword could be made optional, and switch could be made an alias of match ... this way, everybody could be happy )
No, trying to make everybody happy this way leads to bloat and a terrible syntax and UX. Find the best syntax, and enforce it.
In "regular" switch statements there must be a break
to avoid fallthrough which isn't idiomatic _pattern matching_ style. So mixing these syntax's would require bookkeeping of which syntax was used and code has to be generated differently. I'd like to have a uniform syntax
I'd like to suggest using the match syntax. There would be no mandatory break
and even in switch statements fallthrough is very uncommon.
All that changes is:
switch
to match
case
and break
continue
That's it. The functionality is there, pointing to the match statement in the docs should be enough since everybody new has to learn GDscript anyway.
Using break
in switch
es in the first place is un-intuitive to me
Personally I hate to write break
keyword in switch statements. It is mostly extra line of code which is mostly unnecessary. If another case starts this is clear indication that previous one has finished. Fall through just works for channeling multiple choices into single function and only this makes break meaning full. But this can be done by combining empty cases into one single case with or
keyword like using switch(true):
method or using continue
keyword as suggested by @karroffel.
continue
would be the programmatic way of doing it. I don't see any reason not to implement something like 1, 2, 3
or even 1 .. 3
in patterns. Yes, it would increase complexity, but so does a mandatory break
Would it be possible to replicate this switch usage with pattern matching? (pseudocode)
switch(val):
case 0:
return false
case 1:
case 2:
case 3:
print("Stuff")
break
case 4:
print("42")
break
default:
print("Default")
@Zylann
match val:
0: return false
var x:
if x >= 1 && x <= 3:
print("Stuff")
else:
continue
4: print("42")
_: print("Default")
or maybe I'll add something like this
match val:
0: return false
1 .. 3: print("Stuff")
# different version
# 1, 2, 3: print("Stuff")
4: print("42")
_: print("Default")
What if _
is an existing variable? It's a valid name after all :)
@Zylann _
is a kind of pattern, not a name bind. If it would be a name it would shadow the other variable but since it's just a pattern nothing special happens and the old _
can still be used
What happens if you put _:
not as the last match? Should it throw an error?
@Zylann Well, it matches everything, so everything after that wouldn't be executed except for when you place a continue
inside the branch.
So it shouldn't throw an error because you may continue execution with a continue
About 1..3
construct, I prefer, by far, the x,y,z
one.
Rationale:
match 1..3
is ambiguous. Am I matching against the full pattern, or one of the members of the genrated ranged? What if I want to actually match an array that looks like [1,2,3]
?match http_response:
200: "data loaded successfully"
403,404,500: "an error occurred"
or
match tile_at(x,y):
TILE_WATER:
drown()
TILE_WALL,TILE_BARREL,TILE_CHAR,TILE_SPIKE:
stop()
continue
TILE_SPIKE:
life = life -2
Unrelated, but on the subject of dreamland implementations, I like that in some languages, match is an expression, so you can do something like:
var acceleration = match input:
UP:
vector(1,0)
DOWN:
vector(-1,0)
Not advocating for this, as the whitespace-type syntax of GD makes it cumbersome, just thought it's an interesting thing :)
For people advocating for switch
vs match
. I frankly do not see a valid rational reason for that
This is going to be a bit lengthy, not because I care about this so much (I am likely to almost never use switch
of match
myself anyway), but because I'd like the convo to be more constructive, instead of being lost in meanderings.
Here are the arguments against match
, from what I can gather:
Let's start with argument 1:
match does everything switch does, minus default fallthrough.
However, fallthrough by default is:
break
after each case
. I'm sure it exists, but never seen it)continue
makes more sense).TL;DR: match
does everything switch
does well, while avoiding the pitfalls of switch
, _and_ allows for deeper semantics if the programmer wishes to use them (not the case with switch
, which is less flexible than regular ifs
)
The argument for familiarity is also flawed, as GD is it's own language, and there shouldn't be a problem in learning one new word.
Alternatively, the argument of familiarity should be extended to: whitespace as syntax, elif
, no curly braces, one file = one class, ...All of which may or may not be familiar, depending on which languages you've used before. None of the above were for me, for example, but match
is.
Python doesn't even have switch
at all (and for good reason), so even invoking GD's syntactic closeness with Python isn't a valid argument.
But, assuming switch is "familiar" (which, again, really depends which language you're coming from, but let's say it is, for the sake of argument), for a programmer, looking up the doc and replacing
switch val:
case a:
do_something()
break;
case b:
do_something_else()
break;
with:
match val:
a:
do_something()
b:
do_something_else()
...is trivial.
There's basically no learning curve at all. For someone reading code, it's also just as trivial to understand what's going on, even if you've never seen a match
before. Incidentally, that's how I learned of the match
construct: reading code and stumbling upon it. I didn't need more than a split second to understand it was just a cleaner switch
without break
s and case
s. Sure, understanding how powerful it was required to read the docs, but understanding it enough to use it everywhere a switch
could be used needs about 1 to 10 seconds of learning.
For a newbie, it's easier to learn, and he or she doesn't need to deal with understanding what fallthrough is, or remembering to add a break
after each statement.
TL,DR: the argument of familiarity doesn't hold; first, it is subjective. Second, even assuming it is true, for a programmer, learning a slightly different syntax is on par for the course, and for non-programmers, match is less likely to be buggy, yet, slightly easier to learn (same syntax minus one construct).
In summary, this language improvement proposal:
elif
instead of else if
)I think it should be possible to use constants as a pattern (to match stuff like TYPE_FLOAT
or so). Problem is, that it's not possible to detect at parse time (where I am doing the transformations) if a StringName names a constant or not.
So to differentiate constants and bindings it would be necessary to use var name
for a binding and simply SOME_CONSTANT
for constants. (I'll update the examples in my initial comment)
In order to match on patterns of different types it's necessary to add simple typechecking to avoid errors at runtime. To keep things simple I just want to do stuff like that (I'm actually doing it like this right now):
match 4:
"hello": print("It's a string that says \"hello\"")
4: print("It's 4")
# compiles to something like this
var match_value = 4 # actually it's "#match_value"
# this name can only be created by the compiler
# but this messes up syntax highlighting ;)
if typeof(match_value) == typeof("hello") && match_value == "hello":
print("It's a string that says \"hello\"")
# goto end
if typeof(match_value) == typeof(4) && match_value == 4:
print("It's 4")
# goto end
# end:
If you try to match on patterns that have side effects the pattern would execute _twice_ (because of the typecheck), so it wouldn't be good to use patterns with side effects. I could work around this but that would require bookkeeping of temporary variables that would just make the whole transformation more complex.
So I'd say that patterns with side effects should be avoided but not disallowed. What do you think?
What is a pattern with side effects? Calling a function in the pattern?
match val:
get_some_int(val): some_effect()
get_some_other_int(val): some_other_effect()
...That?
And yeah, I think using var
to declare variables in the pattern is less confusing and more appropriate.
quoted from : @Xananax
I think using var to declare variables in the pattern is less confusing and more appropriate.
:+1:
@Xananax no, something like that:
var counter = 0
func side_effect_pls(x):
counter += x
if counter > 1:
return x
return counter
...
match 3:
side_effect_pls(3): print("<- that's baaad")
Yes, that's what I meant.
I think that may be useful (being able to invoke functions in the pattern), but if the implementation gets overly complex because of it, then it's not worth it. It can always be implemented later, if people request it.
"Don't code for average case, don't code for best or worst case, code for most used case and stop at that" -- some dude, I don't remember who.
Should follow @reduz's principle: "Add the minimum needed to work. Only add more stuff if many people request it".
How are you going to count the people who request it? How much is many?
With our fingers. If it doesn't fit on one hand, it's probably many.
Is there a reason you need to use the underscore for a fallthroughwildcard? This takes away the possibility of using it as a variable
@ClikCode
Fallthrough? Fallthrough is done with continue
. You mean wildcard patterns.
Quote Xananax:
This is consistent in all languages I know: Haskell, Scala, Elm, Racket, F#, OCaml, Rust. I don't think there's an exception.
So the idiomatic way to do it, to stay sorta in tune with other languages that have pattern matching, should be to use _.
@karroffel All of those languages are functional or heavy functional-inspired. What about the imperative languages?
@bojidar-bg most imperative languages don't feature pattern matching so it's not really possible to give an example.
C# 7 will have some form of pattern matching but it's just an addition to a basic switch
, so it's not useful here.
Imperative languages don't have pattern matching because it has its roots in algebraic datatypes, which still aren't adopted in imperative languages. Many modern languages have pattern matching as they have algebraic datatypes too. So it's mostly a historic thing.
~
match blah:
1:print("I am some one")
else:
print("I am no one")
~
Is how python would do it if they ever did this
@ClikCode what about
match x:
[1, _, _, 7]: print("kek")
[1, else, else, 7]
is not really pleasant for the eyes.
what about using '?' or '*' instead of '_' character ? These are used as wildcards for regular expressions so not to far from pattern matching. This will leave valid variable name characters out of pattern matching syntax.
What happened to "variables should have descriptive names"? Is there any valid reason that someone would want to call a variable _
unless they want it to be forgotten?
func (_,x) => x+1
The implicit meaning of the function above is the that the first argument is skipped (without even reading its body). Regardless of pattern matching, I doubt you can make a case for wanting to call a variable _
, unless, again, you don't want to use it.
The javascript library "underscore" is called _
precisely because no one ever would use this as a variable name and was as such a relatively "safe" global.
In 20 years of code, working with open-source, in companies small and big, I've never seen once someone use _
for a variable name, unless they really really wanted it forgotten. Not saying my anecdotal experience is proof of anything, but it's still an indicator.
So that's about the "why". I realize it is not a compelling argument, someone could still want to name a variable _
, but considering most common case is important nonetheless.
Secondly; in some languages (don't know if the Godot implementation does that), the _
does resolve to whatever was passed; It is a usable variable, whereas it'd be syntaxically impossible to use ?
or *
for the same effect.
e.g:
match val:
0: print("it's a zero")
4: print("it's a four")
[_]: prints("it's an array containing",_)
_: prints("it's a ",_)
Again, I don't know if Karroffel's implementation has that (not all match implementations do), but regardless; that's a useful convenience and could potentially be added later. It's the reason why underscore was chosen rather than a reserved keyword or an non-valid variable name character.
Lastly; Usage and compatibility with common syntax trumps this minor convenience. For that one coder with his/her own special style that likes to use _
, we'd:
@Xananax thanks for taking the time to write that, those are the key factors why I think _
is the best choice.
Currently I just ignore the value but it would only be a small addition to make _
a useable variable. If that's requested I'll add that.
😄 for yes, please make _
a usable variable
(problem with making it a usable identifier is that _
may occur multiple times in a pattern. Which value should be used? up for discussion...)
"Add the minimum needed to work. Only add more stuff if many people request it"
me quoting vnen quoting reduz
Seems like I'm going to have to step in on this one. I don't really expect my suggestion to find any traction, but still, there are some things which need to be mentioned.
@karroffel
Let me share some python usability on this topic:
match foo:
[_, _]: print("what ever")
could just as easily be replaced with
match foo:
[*, *]: print("what ever")
problem with that would be that it's harder to type, at least in my US keyboard, but, there are numerous other symbols that could be sacrificed for a throw away variable.
match foo:
[*, *]: print("what ever")
regarding using _
as a value returned for a wildcard. I believe you've already implemented this.
match bar:
var n: print("Unknown array structure: " + str(n))
could just as easily be:
match bar:
var _: print("Unknown array structure: " + str(_))
Only problem is, it doesn't look as nice. But it still helps with readability since you are keeping the rules across all valid variable names.
@Xananax
I think you kind of proved my point. the underscore
library for javascript uses the _
because it is so rarely being used for anything else. But it's being used now, isn't it?
I don't think bringing up other languages is the way to go with this conversation.
Regarding the underscore's usefulness as a variable that resolves to whatever was passed.
for this pattern [1, _, _, 2]:print(_)
which one would be printed? Not that clear is it. Try this instead [1, *, var _, 2]: print(_)
and we all know which one will be printed, don't we.
My problem is not with the underscore being used, my problem is using a valid variable name. So either change the valid variables or change the implementation.
You tell a newbie, "A variable is a name for a value, it can use any letter, number, or the underscore character, and it has to start with either a letter or an underscore. You can use this anywhere, except for here, here, and here."
Regards, to all who took the time to listen.
Most helpful comment
For people advocating for
switch
vsmatch
. I frankly do not see a valid rational reason for thatThis is going to be a bit lengthy, not because I care about this so much (I am likely to almost never use
switch
ofmatch
myself anyway), but because I'd like the convo to be more constructive, instead of being lost in meanderings.Here are the arguments against
match
, from what I can gather:Let's start with argument 1:
match does everything switch does, minus default fallthrough.
However, fallthrough by default is:
break
after eachcase
. I'm sure it exists, but never seen it)continue
makes more sense).TL;DR:
match
does everythingswitch
does well, while avoiding the pitfalls ofswitch
, _and_ allows for deeper semantics if the programmer wishes to use them (not the case withswitch
, which is less flexible than regularifs
)The argument for familiarity is also flawed, as GD is it's own language, and there shouldn't be a problem in learning one new word.
Alternatively, the argument of familiarity should be extended to: whitespace as syntax,
elif
, no curly braces, one file = one class, ...All of which may or may not be familiar, depending on which languages you've used before. None of the above were for me, for example, butmatch
is.Python doesn't even have
switch
at all (and for good reason), so even invoking GD's syntactic closeness with Python isn't a valid argument.But, assuming switch is "familiar" (which, again, really depends which language you're coming from, but let's say it is, for the sake of argument), for a programmer, looking up the doc and replacing
with:
...is trivial.
There's basically no learning curve at all. For someone reading code, it's also just as trivial to understand what's going on, even if you've never seen a
match
before. Incidentally, that's how I learned of thematch
construct: reading code and stumbling upon it. I didn't need more than a split second to understand it was just a cleanerswitch
withoutbreak
s andcase
s. Sure, understanding how powerful it was required to read the docs, but understanding it enough to use it everywhere aswitch
could be used needs about 1 to 10 seconds of learning.For a newbie, it's easier to learn, and he or she doesn't need to deal with understanding what fallthrough is, or remembering to add a
break
after each statement.TL,DR: the argument of familiarity doesn't hold; first, it is subjective. Second, even assuming it is true, for a programmer, learning a slightly different syntax is on par for the course, and for non-programmers, match is less likely to be buggy, yet, slightly easier to learn (same syntax minus one construct).
In summary, this language improvement proposal:
elif
instead ofelse if
)