3 ms·
This style gets the label 'poor-mans-pattern-matching' from me. If pattern matching would not be in my daily vocabulary, as it's also not available in JS, I'd c
by aastronaut 6y ago
This style gets the label 'poor-mans-pattern-matching' from me. If pattern matching would not be in my daily vocabulary, as it's also not available in JS, I'd consider it a misuse of switch/case and this post also makes an odd example for its usefulness.
The example I would pick is the following: Consider you need to switch depending on a version (of a specification in my case), but this version isn't represented as an enum in the codebase, but as a number instead. So our team had something like this in the codebase (early return):
function foobar(version: number): string {
if (version === 3.1 || version === 3) {
return 'result_3';
}
if (version < 2 && version >= 1) {
return 'result_1';
}
if (version >= 2) {
return 'result_2';
}
throw new Error(`Cannot interpret version '${version}'`);
}
I read it as "people don't care about branching order that much, so how can I make my wish for better readability more clear?".... my end goal then was to bring it into this state (a distinct enum as discrete value of the version):
enum Version {
_1_1 = 1.1,
_2 = 2,
_3 = 3,
_3_1 = 3.1,
};
function foobar(version: Version): string {
switch (version) {
case Version._3_1:
case Version._3:
return 'result_3';
case Version._2:
return 'result_2';
case Version._1_1:
return 'result_1';
default:
(function (val: never): never {
throw new Error(`Exhaustiveness reached: ${val}`);
})(version);
}
}
...and my interim solution that made it into the PR in time turned out to be something like this (switch true):
function foobar(version: number): string {
switch (true) {
case version >= 3:
return 'result_3';
case version >= 2:
return 'result_2';
case version >= 1:
return 'result_1';
default:
throw new Error(`Cannot interpret version '${version}'`);
}
}
My PR was flagged by the team for misuse of the switch statement, we had some discussion and I changed it back to the simple if/else branching from above.
- alerighi 6y agoswitch (Math.floor(version)) { case 1: return 'result_1'; case 2: return 'result_2'; case 3: return 'result_3'; default: throw new Error('...'); } Isn't that more clear?
- aastronaut 5y agoI think I didn't make my point quite clear. Let's consider the case where version 3.1 needs to return something different than version 3. A good (IMHO) early-return pattern would look like this: if (version >= 3.1) { return "result_3_1"; } if (version >= 3) { return "result_3"; } // ... ...but in the wild I often see it developed into something like this: if (version >= 3) { if (version === 3.1) { return "result_3_1"; } return "result_3"; } // ... ...probably because people like to see major version numbers packaged together in blocks. With the switch-true on the other hand I could make good use of this 'stepping through ranges'-problem and could even make use of the fallthrough, in case it should fall back to a different value: switch (true) { case version >= 3.1: return "result_3_1"; // just comment this out if you need the result of version 3 instead case version >= 3: return "result_3"; // ... }
- bananaface 5y agoWhy not just: function foobar(version: number): string { if (version >= 3) { return 'result_3'; } else if (version >= 2) { return 'result_2'; } else if (version >= 1) { return 'result_1'; } else { throw new Error(`Cannot interpret version '${version}'`); } } ? I don't see what advantage the switch provides here.