That's a bit of a misrepresentation. Error handling on the BEAM has a few more layers than in other environments; specifically, the supervision tree can be used to "let things fail". That's not the layer where you should log or handle failures - that's a safety net that ensures your whole system won't go down if your error handling in a single process doesn't work.
For error handling, there are roughly these layers:
- functions can return {:ok, value} or {:error, error}
- functions can raise errors (similar to exceptions) that can be caught
- processes can be monitored from the outside, you get notified when they die
- processes can be linked and exits can be trapped, also notifying you on failure
- supervisors can handle process deaths in a configurable manner
- higher-level behaviours often expose their own error handling callbacks
So there's a bit more to error handling on the BEAM, and I get that becoming familiar with all of them and using them properly can be a challenge. The defaults skew towards high-availability, which is not always what you want in development - sometimes, failing fast and completely (up to stopping the app or the BEAM as a whole) is more convenient. You can have that; you just need to ask for it specifically in your code.That's a choice, but it's not idiomatic.
You're expected to write things like...
ok = thing_that_might_not_work().
(Well, that's what it looks like in Erlang anyway). If there's an error, it doesn't match, so it crashes. You don't have to check for success, but it's easy to, and 'let it crash' is the mantra, so yeah. Then you watch for crashes, and fix them with hot loading, and pretty soon you have a reliable system.Let it crash ends up not quite working, so you end up catching a lot of errors, but you should be logging them, not swallowing them...