No no no no. That's a silent failure and a bug. That means our code is doing something we don't intend or understand.
No no no no. That's a silent failure and a bug. That means our code is doing something we don't intend or understand.
In many other cases, of course, the robust thing to do is to catch a failure early and prevent some code from executing before it can do more harm, and err on the side of the system doing nothing instead of it doing something wrong. But neither of these is a universal rule.
Ever written a large code base with isolated I/O, functional code, and typed/static analysis? Because I have, and nothing unintended happens except at the I/O level.
When something unintended does happen, it throws an exception: something genuinely exceptional has happened.
This code base has yet to throw an exception in production, and it also hasn't had a bug in production (after running for 6 months with ~1,000 active users).
Also, from the sounds of it, it doesn't seem like a distributed system. Client-side JS is by its nature a distributed system, dealing with network partitions all the time because end-user internet connections are unreliable.
If a bug causes an unexpected undefined value, it will lead your code to an undefined behavior. It might work well 999 times and wipe everything on the 1000th execution. Thankfully, Javascript is mostly limited to web browsers.
Exceptions make it so that nothing unexpected happen. This is especially useful when you do not know the whole codebase. Of course, there are many cases when you can just ignore the errors. Exceptions allow you to fine tune this.
Let's not confuse "undefined", a JS value that is basically like C's NULL, with "undefined behavior", the concept from e.g. the C language spec. Operations on the JS value "undefined" are perfectly well-defined in the C sense; you can reliably test for it and have a case to handle it. In particular, the well-defined behavior for Underscore/Lodash in response to mapping over "undefined" is to return an empty array. The programmer upthread is using the library's documented and well-defined behavior; there is nothing wrong with that.
It is just like how, in C, some functions (like time()) are well-defined if you pass them a NULL pointer, and some functions (like strlen()) are not, and result in undefined behavior. In this case, the functions in question are all well-defined if you pass them "undefined".
No, it makes it so that your program surprises the user by crashing, which is (hopefully!) pretty unexpected.
Unless, of course, you use the exceptions to provide some sane default code path that handles the problem, but that's exactly what lodash is doing for you in this example.
1. Where did anyone say we were hitting undefined behavior? We're hitting a well-defined JavaScript value whose name is "undefined".
2. Why is crashing correct? Don't answer in terms of language specs, answer in terms of desired behavior for whatever you're trying to do with the program. Software engineering is a tool for accomplishing other things. Sometimes, yes, software crashing is the right thing in service of that other goal. But not always.
Crashing in such case is used to avoid incorrect functioning of the program, such as overwriting user data or introducing security vulnerabilities. By definition, if the behavior wasn't expected, the program is in unknown state. Sure, you can gracefully handle such situations (e.g. allow website to load other scripts if the particular piece that "crashes" not important for its functioning, crash just some kind-of sub-process and restart it, or — if it's user input — end up with some predefined value regardless of the incorrect input, etc.), but the correct _default_ behavior is to crash, otherwise you'll end up with unknown state and the algorithm that you wrote will be incorrect.
I recommend anyone who wants to see how much unexpected behavior is in their JavaScript programs to install typescript@next and start adding types, compiling with tsc --strictNullChecks.
More for avoiding guard scaffolding than for undefined behavior.
If you tell a computer something it doesn't understand, it should tell you that it doesn't understand. There's no scenario where "just guess what you think I wanted to do and then do it" is a safe or reliable way for a program to run. By definition, sometimes it will work as intended and sometimes it won't. That's a bug. It's the worst kind of bug, actually: silent and undetectable until you catch the problem in the final output.