Erlang's error handling is very well thought out. In fact, error handling is the main reason Erlang is shaped the way it is.
It is a combination of Go's technique but with panics for exceptional errors.
Like Go, functions should return error values for known errors. For instance, `file:open/2` returns `{error, enoent | eacces | eisdir | enotdir | enospc}` for known error conditions that can be handled. Unknown, exceptional conditions, like a NAS going down, can't be compensated for because the set of unknown errors is unbounded. There could literally be an infinite number of things that can go wrong that you don't know how to handle. For try/catch style error handling, the only safe way to handle the unknown errors is to do a Pokemon catch clause globally or around every single potentially error causing function call.
Because of the fact that you can't stop errors from happening is why Erlang has processes. When an unknown error occurs, the individual process dies and anyone monitoring that process is notified. OTP has supervisors whose sole job is to watch over processes and restart them if they crash.
Any state that needs to be preserved between restarts is stored somewhere safe (Erlangers calls this the "error kernel") so that when the process comes back it'll resume where it left off.
Once you structure your code to "Let it Crash", your code gets much simpler and safer. #1, the error return values forces you to compensate for known errors that can happen with a function, #2 you have to think about data durability in order to "Let it Crash". #3 Supervisors take care of the rest.
Data durability is made much easier with the fact that individual processes are single threaded and data is immutable, this makes them transactional by nature.
It is a combination of Go's technique but with panics for exceptional errors.
Like Go, functions should return error values for known errors. For instance, `file:open/2` returns `{error, enoent | eacces | eisdir | enotdir | enospc}` for known error conditions that can be handled. Unknown, exceptional conditions, like a NAS going down, can't be compensated for because the set of unknown errors is unbounded. There could literally be an infinite number of things that can go wrong that you don't know how to handle. For try/catch style error handling, the only safe way to handle the unknown errors is to do a Pokemon catch clause globally or around every single potentially error causing function call.
Because of the fact that you can't stop errors from happening is why Erlang has processes. When an unknown error occurs, the individual process dies and anyone monitoring that process is notified. OTP has supervisors whose sole job is to watch over processes and restart them if they crash.
Any state that needs to be preserved between restarts is stored somewhere safe (Erlangers calls this the "error kernel") so that when the process comes back it'll resume where it left off.
Once you structure your code to "Let it Crash", your code gets much simpler and safer. #1, the error return values forces you to compensate for known errors that can happen with a function, #2 you have to think about data durability in order to "Let it Crash". #3 Supervisors take care of the rest.
Data durability is made much easier with the fact that individual processes are single threaded and data is immutable, this makes them transactional by nature.