The switch statement has a long history, and most languages support it or something similar. In my experience, I found it to be very useful — both for conciseness, and also improving performance. With the recent release of Java 7, you can finally switch over strings.
In Whiley, the syntax for switch statements currently looks like this:
for token in modifiers: modifier = null switch token.id: case "PUBLIC": case "public": modifier = ACC_PUBLIC break case "FINAL": case "final": modifier = ACC_FINAL break ... default: throw SyntaxError("invalid modifier",token)
(This excerpt is based on my Java Assembler/Disassembler benchmark)
Now, I’m having something of a dilemma over this syntax: should I support fall-through case statements or not?
As you can see from above, my initial reaction was: yes. But, I’m starting to think this is a really bad idea and there are a few reasons:
- Obviously, fall-through by default is dangerous as its easy to forget the
break
. I’ve already done this a few times! - Generally, I think fall-through is the exception, not the rule. That is, most cases do require the
break
and, hence, fall-through by default causes additional verbosity. - The overloaded
break
statement can be annoying when you actually want to break out of a loop.
Now, of course, I’m not the first to think along these lines (see e.g. [1][2][3]) and, in fact, I’m probably being a bit slow on the uptake here!
Several languages (e.g. Go, CoffeeScript, Pascal) do not support fall-through by default. For example, Go supports an explicit fallthrough
statement, which might look something like this in Whiley:
for token in modifiers: modifier = null switch token.id: case "PUBLIC": fallthrough case "public": modifier = ACC_PUBLIC case "FINAL": fallthrough case "final": modifier = ACC_FINAL ... default: throw SyntaxError("invalid modifier",token)
Go is quite interesting as it provides multi-match case statements as well, which might look like something like this in Whiley:
for token in modifiers: modifier = null switch token.id: case "PUBLIC","public": modifier = ACC_PUBLIC case "FINAL","final": modifier = ACC_FINAL ... default: throw SyntaxError("invalid modifier",token)
I really like this syntax (which I think is originally from Pascal). In Pascal, you can provide ranges as well which certainly makes sense.
So, all things considered, I’m convinced it is a mistake to support fall-through by default in Whiley. Fortunately, it’s not too late for me to correct this!
The big question then is: do I support explicit fallthrough, multi-match cases or both?
Since your language is not C-style curly brace-based, I think its safe to take the no-fall-through route.
I agree it is more logical and less error-prone. But I think it’s dangerous whenever C-style curly brace languages diverge from what the programmer expects based on previous experience. That’s not going to be the case here.
Do you support switch on Class as well as String? Java missed that one, sadly.
JH
Hey John,
Yeah, that was my reasoning originally — stick with what most people will expect. But, I’ve just decided to stuff that … I can’t stand fall-through by default!
In theory, you will be able to switch on types. However, that is not implemented yet. You can switch on strings already … so it should be easy enough to cover types.
I would say to not allow fall-through but allow multi-match. I think multi-match accounts for most uses of fall-through. The ‘break’ statement in a switch has always rubbed me the wrong way.
My initial reaction after reading that you were not so keen on fallthrough by default was to suggest something like multi-match cases. I then scrolled down and saw that you mentioned that too.
I think you should include both multi-match cases AND explicit fallthrough because just having multi-match cases doesn’t cover the situation where you want a certain case to perform an operation and then fallthrough to another case.
Hey Daniel,
Yeah, I was thinking about that. I have encountered a number of situations where I do something specific and then fall-through to another case. That can still be done by adding an explicit if-statement on the matched value, although I suppose it’s ever-so-slightly less efficient.
Yea that’s true, but which one is nicer to write? Also if you did have explicit fallthrough you’d have to come up with some fancy keyword. How would you feel about overriding continue? (Assuming you use continue in the first place)
Hey Daniel,
Ah, I’m ahead of you on this one! Already considered continue, as this makes heaps of sense to me. However, given the conflict with loop-based continue, I still might steer clear of it. I was thinking of “next”, since “fallthrough” is kinda long. Or maybe just “fall”.
D
What makes even more sense to me would be to use next to skip to the next iteration of a loop (what continue is currently used for), and to use continue to fall through to the next case. The only main reason I can see at the moment for not doing this is the legions of C/Java programmers who are already familiar with using the continue keyword in loops.
I saw a language a while ago, and of course now I can’t remember what it was, that had default fallthrough for empty case blocks only, i.e:
case "PUBLIC":
# falls through to "public"
case "public":
modifier = ACC_PUBLIC
# non-empty block, doesn't fall through to "FINAL"
case "FINAL":
I always liked this, but I don’t recall what language used it.
Hey James,
I think it’s C#
http://www.java2s.com/Code/CSharp/Language-Basics/Emptycasescanfallthrough.htm
D
Hey Daniel,
That’s quite a big reason not to, unfortunately!
D
I’ve never seen multi-cases before. Nice alternative.
Here’s a bit of a wide-ball: within a multi-case you could potentially allow a nested switch (on the same variable), which only accepts the values given in the multi-case.
e.g.
switch str:
case "a","b":
callSomething(str);
switch str:
case "a":
doAThing(str);
case "b":
case "c": //<-COMPILER ERROR
case "c":
callSomethingElse(str);
default:
throw SyntaxError("invalid modifier", str)
The nice thing about enum switches is that the possible values are pre-defined, and the compiler can even warn you when there’s a case missing. In theory it could do it in the nested switch too.
I guess it’d be a lot of work for slim gains, but just a thought…
p.s. +1 for supporting type swtiches.
(I meant to say that this would provide an alternative to ‘explicit fallthrough’)
I’m given to understand that C# requires either a break or the fall-through equivalent (continue, perhaps?) on every non-empty case section. That would solve the ambiguity problem rather nicely, I think. So something like this:
switch foo:
case "BAR":
wibble
wobble
case "BAZ":
fnord
… would be a compile-time error, rather than a run-time bug. I like that level of idiot-proofing, personally.
Personally, I like the Perl 6 idiom, given/when, which is rather more extensible. Off the top of my head, and freely adapting it to what I’ve seen here of your syntax, I think it would work like this:
given foo:
when "BAR", "BAZ", "QUUX":
wibble
break
when /^fn[aeiou]+rd$/i: # regular expression match
wobble
break
when .toInteger < 0: # method call on the 'given' value
glorply
continue
default:
plinge
I hope the formatting comes through. The lack of preview in this comment form is a mild annoyance.
[…] Changed the way cases of a switch statement work. Now, you can no longer “fall-through by default“. […]