You're thinking of defaultless switch statements. Rust and Typescript catch those issues as long as you don't add a default. I'd already thought of it when I typed it
>And who said you'd ever need to add a new shape?
ô_o
>Maybe you will need to add a new operation? Try to add a new operation `calculateWhatever` and see how many places of the code you need to chnage instead of just adding one new function with a switch.
You do realize that your "one function with a switch" does have to cover every case exactly like the clean code version, it's not magic you're just fitting it all into one switch/case, and you'll probably end up extracting those case: blocks into separate functions anyway.
And on top of that you're not safe from a colleague adding a useless "default" case at the end ouf ot paranoia and making your compiler not catch a future problem when a new shape gets added.
Productivity is achieved in spite of clean, not thanks to it.
The difference is, adding "default" cases to complete switch statements happens all the time, you've probably witnessed it, I know I have, whereas colleagues adding a dozen classes to add two numbers doesn't.
You're also never safe from meteorites crashing your building, let's talk about real antipatterns
But the same problem exists with interfaces and virtual dispatch! If you provide a default implementation at the interface / abstract class level, then the compiler won't tell you forgot to implement that method, because it would see the default one exists.
The compiler not catching it is a limitation of the language he uses, not the limitation of the general concept of switch / pattern matching. Scala, Haskell, Rust do catch those.
> If you follow the principle "switch statements over [X]", try to add a new shape down the line and see how quickly you run into problems.
And who said you'd ever need to add a new shape? Maybe you will need to add a new operation? Try to add a new operation `calculateWhatever` and see how many places of the code you need to chnage instead of just adding one new function with a switch.
Often you really don't know in which direction the code will evolve. Most often you can't guess the coming change, so don't make the code more complex now in order to make it simpler in the future (which may never come).