More interestingly, how does this help uptime? I've been in telephony for a while. My software related downtime (vs human error breaking things) seem either related to logic bugs (restarting won't help) or load (simply too little hardware for a, say, 100x increase in traffic).
Maybe Erlang's selling points make more sense compared to C instead of a managed language? For instance I often hear about the robustness of the Erlang VM, yet JVM or CLR integrity has never been an issue for me.
Nevertheless, I'll try.
In Erlang, almost everything is immutable. So, if you want to update state, you build a new state, and then call yourself recursively with the new state. Seems annoying, right?
Except, I can invert this where I keep the state the same but vary the code. If I want to update my code, I can tell the old code to pack up it's old state and call my new code recursively.
No downtime. At all.
Robustness: the Erlang VM keeps track of a cost for just about everything in its preemptive scheduler. The scheduler is tuned for latency rather than throughput. So, when you get 100X increase in traffic, the scheduler continues to make progress even if it's resource is getting overloaded. In addition GC pauses are per scheduler/process rather than global. This is a function of the fact that Erlang tends to serialize nearly everything so it doesn't have to share.
As for kill/restart, these things tend to be an architectural idiom that is shared by the standard library rather than an obscure technique that only a handful of the programmers grasp. Kill/restart is expected to be NORMAL in Erlang and is thus fast with low overhead--try/catch tends to be abnormal and restarting OS processes tends to be slow.
There are a lot of good things wrapped into Erlang. If you've just used imperative C variant languages (C, Java, C#, Python, Ruby, etc.), I recommend using it in a bit of anger. It can be frustrating until you get your head wrapped around it, but I still retain the architectural elements when I write certain tasks in other languages.
Restarting does help with logic bugs depending on what execution path triggers it. Bringing back a process to a known good state is a heavily useful concession to keep services up and doing work until the root cause is diagnosed and fixed.
You might also start your query to the list with a brief summary of your background and programming experience so you can get the best answer possible.
You would have tested at least the success path in your code (but not probably all the error conditions). 'Logic bugs' then should fall on the non-critical path, so dropping the state that led you to take that path is okay; if it's a critical process, you restart it into a known good state. A trivial example of this might be a user giving you bad input; any validation (or even a lack of validation, provided it breaks something down the line) should cause the data to drop, and the listening process to still be up (i.e., crash and restart into a known good state), ready to take other user requests (and to leave any other in process user requests alone).
For load, Erlang makes it easy to write code that automatically scales for load (up to the hardware limit, obviously). It also makes it easy to write code that can be distributed, running across multiple machines, and able to scale up that way as well. It's also pretty easy to write rate limiters and things, to automatically shed load if you're bottle necking somewhere.
Erlang also makes it harder for humans to break things (in the sense of user error, or production support tinkering with it). You can still do it, but the supervisor processes oftentimes help mitigate that (a human puts in bad state such that it affects an important process, the process will crash and restart).
How is it different than a try/catch? Because rather than trying to figure out every location that an error can occur in your code, you instead ask "what happens if -anything- goes wrong with this process? How do I get this process back to a good state?" You already partially answered this question when you created the process, and had to ask "how do I start this process in a good state?" Does the process depend on another process being up? Then you would have had to already specify as part of the supervisor that the other process is started first, and picked a supervision strategy that will restart that other process if this one restarts, causing both to dump their state and reinitialize. A try/catch, not only do you have to recognize each individual thing that can go wrong (or have a catch-all that hides errors), you have to decide how to recover from it, and it oftentimes is not at all clear how you do that. You end up with a lot more places you can miss handling errors, a lot of assumptions being made, and there isn't a good way to tie multiple processes together.
Restarting an OS process is actually a great example of why this philosophy works well. If the app dies, restart it; it probably died because of bad state. You already understand that that is a great option for unexpected bugs that leave the entire app in a bad state. Only, oftentimes that's overkill; your bad state is probably relegated to a very small part of the app, that, if the app is written in such a way that unrelated things stay unrelated (and communicate with just shared nothing message passing), can individually be restarted within the app. Subsystems, if you will. That's what Erlang does. The idioms and functionality intrinsic to the language make it far easier to write code encapsulated across process lines, such that an error in one process can be treated as identical to any other error in that process, or that kind of process, and handled in a well defined way (drop the state; restart the process into a known good state), leaving the rest of the app unaffected.
Erlang is excellent for problems where latency is an issue. It's really good for problems where scaling is an issue.
But uptime is the big one. The whole immutable, recursive calls, and kill-and-restart quickly is a very good architecture for when you need all those nines.