Language Proposal: The 'Any' Switch Case

Meghan Denny on July 18, 2019

This feature proposal is language agnostic, as I have not seen this in any language before. If you know of an existing better way to do this, pleas... [Read Full]
markdown guide
 

Code is read much more often than it is written, and we understand code best when it executes the way we read it: top to bottom. We get into trouble when the flow jumps around. This is what makes asynchronous code so hard to understand without async/await.

Take this code block for example:

first();
setTimeout(function(){
    third();
}, 0);
second();

Here, the name of the functions I'm calling indicates in what order they're called, and there's this annoying jump.

So I think that even though


switch (a)  {
    case 1:
        doOne();
        doCommon();
        break;
    case 2:
        doTwo();
        doCommon();
        break;
    ...
}

Having this common code in each case might be a little bit longer, but when reading it, it takes a little less overhead. If the common code between cases spans so many lines that it's annoying to update everywhere, maybe you should put it into a function.

 

Readability is absolutely, and even I had some concerns about readability before I made the post. While the idea was drawn to me because I was thinking about the amount of cases there may be, so it would be more than a few lines, the particular piece of code that drew me to this, also may be able to be reworked to not use a switch at all and achieve the functionality I was hoping for.

 
 

So I've thought over this a couple of times, and I may not see the beauty of it, or am missing the benefits, but why couldn't you just put the line of code you want to run either before or after the switch? For example:

You propose:

const myValue = 10;
switch (myValue) {
    case 10:
        doThingForValue10();
        break;
    case 5:
        doThingForValue5();
        break;
    any: // maybe some sort of syntactic sugar to make block run before/after cases are evaluated:
        console.log(myValue);
        console.log("run for all cases");
}

However, this begs the question of when the any block actually runs. There can be lots of discussion on how we should achieve that, but I think it gets messy especially when you want some code to run before AND after. I think that you can write code with the same functionality in vanilla javascript which would look like this:

const myValue = 10;
console.log(myValue); // this would run just the same as it would inside the any block
switch (myValue) {
    case 10:
        doThingForValue10();
        break;
    case 5:
        doThingForValue5();
        break;
}
console.log("run for all cases"); // not only can we choose if it runs before/after, but we can split code that would be inside the any block to run before AND after
 

What about simplifying this:

switch (number) {

  any: // Runs before any matched case

  case 1: // ...

  case 2: // ...

  any: // Runs only after case 1 or 2 match

  case 3: // ...

  case 4: // ...

  any: // Runs after any of the previously matches cases

  default: // ...

}
 

Ooo, I like this! I thought about putting it before and after, and I love the addition of the middle block. But I worried about the readability of this. Since it begs the question, at least at this stage, does the middle bottom any run after 1,2,3,4 or just cover 3,4?

 

I think it would just have to just be specified by the language. I'm not coming up with anything that wouldn't add a bunch of lexical complexity.

This does remind me though, when you have increasingly complex relationships between data and behavior, at some point it's best to abstract that into it's own class that can encapsulate that logic so that the language doesn't have to make increasingly strong opinions on how those relationships are expressed.

 

I have the feeling it'd complicate things for the reader while effectively saving only a few lines. What if suddenly you need to change the order in which operations are run but only for one case?

But it's also similar to a finally construct for exception handling. It might make sense in that regard

 

What about a template case?

switch (number) {

  template:
      before: // do something before
       after: // do something after

  case 1: // ...

  template case 2: // ...

  template case 3: // ...

  case 4: // ...

  default: // ...

}

template could be applied to only a particular subset of cases, and the template definition could be extended to do exception handling, etc.

code of conduct - report abuse