Closed Akxe closed 4 years ago
If it reuses parts of switch, it will not advance. It must be entirely new for me to agree to advancement - for learnability, googleability, confusion, etc.
Note that your first example pretty much works already with the existing switch-cases with a minor modification (apart from possible issues with inheritance or realms).
let vehicle = new Truck()
switch (vehicle.constructor) {
case Car:
console.log('This is a car!');
break;
case Truck:
console.log('This is a truck!');
break;
case Train:
case Metro:
console.log('This is a transport!');
break;
}
// This is a truck!
Yes @noppa, unless you extend classes.
instanceof is forgeable and does not work across realms; i would also not be interested in enshrining instanceof comparisons into pattern matching (except when a user chooses explicitly to use it, ofc).
(to be clear, I’m not a champion of this proposal; just a TC39 delegate)
@ljharb Nor does .constructor
as you cannot have the actual reference to it. But it wasn't really the point. The point is the same as many others here raised. This syntax is like one from completely different language, that is based around the different syntax. It is very alien to me and based on number of issues on syntax theme, I don't think I am the only one...
I wanted to propose different syntax that is based on language with similar syntax. The author of this proposal is currently writing in rust and thus the syntax is from rust, with exception of the arrow, that has different width, but from what I read she wanted the fat one too. I don't think it is a good idea to diverge from JS syntax this much.
@Akxe
In my opinion, and several others, defaulting to fallthrough is a defect and we should absolutely take this as an opportunity to move away from it and the break
keyword.
See for example https://stackoverflow.com/questions/252489/why-was-the-switch-statement-designed-to-need-a-break
The most relevant bit is this by stack-overflow user Michael Burr:
Many answers seem to focus on the ability to fall through as the reason for requiring the break statement.
I believe it was simply a mistake, due largely because when C was designed there was not nearly as much experience with how these constructs would be used.
Peter Van der Linden makes the case in his book "Expert C Programming":
We analyzed the Sun C compiler sources to see how often the default fall through was used. The Sun ANSI C compiler front end has 244 switch statements, each of which has an average of seven cases. Fall through occurs in just 3% of all these cases.
In other words, the normal switch behavior is wrong 97% of the time. It's not just in a compiler - on the contrary, where fall through was used in this analysis it was often for situations that occur more frequently in a compiler than in other software, for instance, when compiling operators that can have either one or two operands:
switch (operator->num_of_operands) { case 2: process_operand( operator->operand_2); /* FALLTHRU */ case 1: process_operand( operator->operand_1); break; }
Case fall through is so widely recognized as a defect that there's even a special comment convention, shown above, that tells lint "this is really one of those 3% of cases where fall through was desired."
I think it was a good idea for C# to require an explicit jump statement at the end of each case block (while still allowing multiple case labels to be stacked - as long as there's only a single block of statements). In C# you can still have one case fall through to another - you just have to make the fall thru explicit by jumping to the next case using a goto.
It's too bad Java didn't take the opportunity to break from the C semantics.
In addition I am also not in agreement with the core statement of your argument that we need to reuse existing parts of JS. I feel that if the syntax is too similar but with very different functionality then it will be harder to differentiate between them and easier to mix them up by mistake. They should be very different to show that they are not the same functionality.
To me, fallthrough is a key feature of the switch. That being said, if there is a block that is defined using { ... }
syntax as block usually is, I would assume that there would be no fallthrough and be ok with it but as long as there isn't a block. I would see it as a bug.
I met JS very early, and I have stayed on it for the past 10 years. I tried (usually because of school) PHP, C#, Java, Ruby, Python and possibly some I forgot already. I never felt any of these languages to be superior to JS; I just wanted some small extract of their syntax.
@PetterSa if you use syntax that is completely alien to JS this would feel as some hack that JS has had due to its evolution (the fact that JS ignores <!--
, and similar quirks). The language syntax needs to stay consistent; I am sure you agree with it.
The core of this proposal to be able to switch branches based on condition, this is the thing switch was created for, thus it should be used to do so.
switch (*expression*) {
case 'value1': // Falltrought
case 'value2' {
... no need of `break`, as this is a block
}
case of { error } {
... this expression has an error in it ...
}
}
can be the perfectly valid syntax, it feels similar enough to be sure what to expect. (If this would be the one to go with I would split this proposal to two for greater chances of success.)
It seems very dangerous to have the presence of a colon affect whether or not there's an implicit break
after the case...
switch (expr) {
case 'value1': {
} // fallthrough
case 'value2' {
} // no fallthrough
}
To me, fallthrough is a key feature of the switch.
But it doesn't really relate much to pattern-matching. This is probably one of the resaons why there's a strong desire to not mix in syntaxes from a related-but-different syntactic construct, since the semantics (or at least many of the intended uses, borrowing from functional languages) is different in many ways.
Like, (to me) this isn't "let's make switch but with some additional nice features", but more "let's borrow pattern-matching from functional languages because it's a powerful construct that would fit in quite well with the way JS is often used these days". The difference in viewpoint here is probably what leads to these discussions of "should the syntax be similar to switch
or not".
The difference I proposed is that you have to choose. If case expression
is followed by :
it fallthrough unless break
is used. But if it is fallowed by block{ ... }
. It will not fallthrough anymore. This will enable you to do things like:
switch (enumValue) {
case enum.option1 {
console.log('only run for option 1!');
}
case enum.option2:
console.log('this runs only for option2.');
case enum.option3:
case enum.option4 {
// This would be a lexical block, where `this` is inferred from the parent block
let variable = 5;
console.log('only run for option 2, 3 or 4!');
}
}
console.log(typeof variable); // undefined in all cases
This is one way to solve fallthrough. It has distinct syntax yet similar to JS eyes while conveying the message of the block better that will run better.
Note: I don't necessarily like the fact that option2 does what it does, but it is used and it has its uses.
Then there is the second part of the pattern matching:
switch (*expression*) {
case of { error } {
// ... this expression has an error in it ...
}
case of { message } do {
// The syntax with do might be better on eye
}
}
@Akxe it is already legal today to follow a case with a block, which is why I made that remark about the existence of a colon implicitly adding a break
after the block. My value1
case in my earlier example is already legal syntax today, with well-defined behaviour (that of course can't change).
@FireyFly Oh, I didn't think of that... let's then use the do
keyword for all non-fallthrough blocks.
I now see what you mean... Although they seem unrelated. I do not think they should be completely separated. To me, pattern matching should be part of switch syntax, but it should build upon new switch non-fallthrough syntax.
Having completely new syntax for pattern matching is quite too big to be added to JS. A function would is much more appropriate.
@Akxe Per the link i referenced it is clearly explained why fallthrough is considered a bad feature for most people, I will say the arguments presented there still holds and I can't really see a good counter-argument in your posts. I have also updated my post with the relevant answer from stack-overflow.
This proposal suggests not only to expand with enhanced abilities to switch branches, but also to return a value based on what was selected. This makes it not only a feature for switching branches, but also something more.
In my eyes there is enough to justify a new syntax:
switch
not sufficient to describe the functionality@PetterSa yes but only if one does not include this as fallthrough:
switch (over) {
case 'value1':
case 'value2':
... do something...
}
case expression do {}
.I don't really see why to add new syntax and reserve new keywords for something that fits so nicely in existing while making the current switch better.
@Akxe because imo switch is terrible and should never be used, and I don't want to suddenly have to start advocating "use switch but only if you use it with this exact syntax", because that is confusing for newcomers and veterans alike. Pattern matching is a good thing I want to advocate everyone use over switch, and that requires it be distinct syntax.
@Akxe Pattern matching also supports returning a value based on what was chosen, how do you suggest implementing this without breaking existing switch statements?
@PetterSa how about the do expression
imo switch is terrible and should never be used
@ljharb Nothing is this one-sided and if you think it is then you might not have understood them properly...
The issue of syntax will never get resolved like this, thus I'll be closing this.
@Akxe Are you suggesting that some of the cases should return something and some of them not? That sounds like a nightmare of bugs. Also, the switch statement does not return values, introducing that would mean breaking backwards compatibility.
@PetterSa No. Like this
const result = do {
switch (n) {
case 1:
'first';
break;
case 2 do {
let a = 'second'
a;
}
default:
n;
}
}
Switch itself doesn't need the functionality as wrapping the whole switch in do {}
expression block will suffice.
The current switch proposal differs too greatly feel like part of JS. We should try to reuse parts of JS switch if we want this to be accepted. Maybe we could take a look at syntax that C# uses. Lately, C# has been taking a lot of notes from JS/TS world, thus it might make more sense to look into its syntax for JS.
Syntax example:
Simple (edited to fit JS world)
Car
,Truck
,Train
andMetro
are user-defined classes.Multiple values
If the expression passed is inline array, one can use deconstruction-like syntax to do the matching.
if once wants to use this syntax on array in variable:
([...array]) switch { ... }
.Pattern matching
Points against current syntax:
JS doesn't need nor want more keyword if necessary.
when
is just synonym tocase
. Alsowhen
is found in many JS codebases.->
we have=>
and blocks ({ ... }
). This isn't really needed either.