Hacker News new | ask | show | jobs
by aasasd 2269 days ago
As a technical aside (I know it's not the main point, but still): with the ubiquity of Git workflows employing pre-merge checking on the platform of choice (e.g. Github or a CI tool), this is rather easily done via a rule in an off-the-shelf linter. No `break` in a `case`? Can't merge it.

As a language-design aside, `switch` in general is stinky stuff. Not just with the fall-through: it also violates the regular C-style syntax for no particular reason, having a mini-syntax of its own instead.

But the most perverse thing I've seen done with `switch` is using it as `if`:

    switch (true) {
        case ($a == $b): ...
        case ($c == $d): ...
        case (itsFullMoonToday()): ...
        default: ...
    }
6 comments

If that’s the most perverse you’ve seen, I guess you haven’t seen Duff’s device, which mixes it with a do…while loop (code copied from http://www.catb.org/~esr/jargon/html/D/Duffs-device.html):

   register n = (count + 7) / 8;      /* count > 0 assumed */

   switch (count % 8)
   {
   case 0:        do {  *to = *from++;
   case 7:              *to = *from++;
   case 6:              *to = *from++;
   case 5:              *to = *from++;
   case 4:              *to = *from++;
   case 3:              *to = *from++;
   case 2:              *to = *from++;
   case 1:              *to = *from++;
                      } while (--n > 0);
   }
I have no idea if this still works in modern C, but here's something kind of similar I've seen used in C circa 1986:

  switch (foo)
  {
    case 1:
      ...code just for case 1...
      if (0)
      {
    case 2:
        ...code just for case 2...
      }
      ...code for both case 1 and case 2...
      break;
    case 3:
      ...

  }
used when two cases have a common tail, and you have sufficient space constraints that you do not want to duplicate the common tail code, and you have sufficient time constraints (and maybe space constraints on the stack) that you don't want to put the common tail code in a subroutine and call it from both cases.
Oh my god. I’ve wondered about a way to do this but... this is just devious.
This is goto with slightly more code ;-)
That's not perversity, that's suicide by art.
Post author here.

One of the nice advantages we had in using our static analysis tool for this, with real code intelligence, instead of a simpler linter, was that we could do a much more clever check. The actual rule was "every case must be terminal", and we had a rich analysis for "terminal", so stuff like this would be considered legal:

    case blah:
      if (cond) {
        throw blah;
      }
      if (cond2) {
        some_noreturn_function();
      } else {
        return 42;
      }
etc etc. My example is a bit contrived, and the wisdom of doing such complicated things in switch/case is questionable, but it was useful when dealing with an existing codebase.

Another advantage was that our tool was designed for extremely fast analysis over the entire codebase, so errors were given to programmers as they were writing code, immediately. (200ms response time to any change. At that level you can run it on every keystroke, which we did.) Traditional linters can in principle do this, but in practise are often not written with this sort of performance requirements in mind.

(And yes, PHP allowing arbitrary expressions in case labels was... probably not a good idea.)

FYI, phpcs now has something similar (since roughly 2016), but I imagine your implementation predates that by a significant margin (and yours obviously supports noreturn too).

> PHP allowing arbitrary expressions in case labels was... probably not a good idea.

Yeah, possibly the worst offender is the (somewhat common) "switch (true)" pattern: https://psalm.dev/r/4c168a9c5d

Sadly I don't have the latitude you all do to govern how code gets written by users of my own static analysis tool – fall-through has to work there, too: https://psalm.dev/r/1d4ee59bd6

A switch is a kind of computed goto. It's a fantastically useful construct when you need it. You don't usually need it. I'm not sure what mini-syntax you're referring to, the syntax is roughly what I would have guessed it would look in C if I hadn't seen it and you just told me its semantics.

Most people treat switch as a peculiar form of match (or cond or whatever), probably because this would usually be much more useful to have than a computed goto, and in many derivative languages they've put in weird additions that seem to be an attempt to drag it halfway to that, things that don't really make any sense in C, like allowing dynamic expressions in the cases (such as your example), or forcing all cases to be at the top level.

(I feel like I write a lot of "in defense of switch" comments on HN...)

> Most people treat switch as a peculiar form of match (or cond or whatever)

Completely made up switch illustrates how I use it in every language if I can...Go has a succinct version of this.

    switch(True) {
        case ($x == 1):
            $x++;
            break;
        case ($x > 3):
            $x--;
            break;
        default:
            break;
    }
Why? It's longer, more complex, and more highly indented than a normal if-elif-else.
This is actually a feature in Go, but you leave off the "true" condition entirely: https://tour.golang.org/flowcontrol/11

I quite like it, as it cuts down on the amount of brace and if/else if noise.

That could be the work of a Lisp coder rebelling against having cond taken away, and having to fend with if/else/elif.
Oh, no—that was the guy who loved MS and .NET despite working in a Linux-based shop, drove a Chinese-Russian SUV when not riding a roadbike in tight pants, supported Putin and swore about every ten words in both speech and chats, that's all while looking hella dorky. One thing I can't picture him doing is pining for Lisp.

Now, grinding everyone's gears with his abuse of `switch`—that was right up his alley, what with already grinding them with his political commentary outside of code.

What language is that? It's not C, because it wouldn't compile.
Looks kind of like Perl to me, except that Perl uses `given … when …`.
The third example is valid C.