4 ms·
There are better ways. The Erlang way would be to code for the happy path, and not try to anticipate most errors. If your process encounters an error, it will
by waffle_ss 12y ago
There are better ways.
The Erlang way would be to code for the happy path, and not try to anticipate most errors. If your process encounters an error, it will crash, the supervisor will restart it, and it will continue processing. Of course, you can choose to handle anticipated errors if you would like to be more user friendly (e.g. provide a user-friendly message if a file is missing), or if you want to log the error or something. But generally it's considered defensive programming and an antipattern to try and handle every error condition in an Erlang program like you have to do with e.g. Java exceptions.
In languages with powerful type systems like Haskell, errors can be handled by using (for example) the Either[1] type. That way, you are forced to pattern match on the type (it will be a Right if successful or a Left if unsuccessful/errored), so it's not possible to "forget" to check for a null reference like you do in Go (i.e. checking err != nil all over the place).
[1]: http://hackage.haskell.org/package/category-extras-0.53.4/docs/Control-Monad-Either.html http://hackage.haskell.org/package/category-extras-0.53.4/do...
- inkel 12y agoThank you for the explanation. I think it makes sense somehow, however, and high likely because I've never dealt with errors in that way, the _encounter error, crash, restart, continue_ workflow seems a bit awful to me :) The Haskell solution seems really nice. I've only used Haskell for pet projects and it's one of my favorites languages (though I don't have any proficiency with it,) so thank you for teaching me something else about it.
- rdtsc 12y ago> _encounter error, crash, restart, continue_ workflow seems a bit awful to me :) Granted it is better and less awful than error, everything stops, get calls from customer at 4am, fix, continue cycle ... ;-) It is a bit different. Learn about it some more and you'll love it. The reason you can't easily do it in other languages is that errors are not isolated. That crash that happened, it might have left some global variable some place in a strange un-expected state. That is why Erlang/Elixir processes do not share memory. That way it lets you feel a bit safer about crash-restart parts of the system.
- masklinn 12y ago> high likely because I've never dealt with errors in that way, the _encounter error, crash, restart, continue_ workflow seems a bit awful to me It is a different way to see things, but makes sense when you consider that Erlang comes from the world of distributed high-availability telecoms where "one machine" is the same as no machine (because when — not if — the machine crashes, the system is dead). Applied to the development environment, that means of course erlang developers try to avoid errors, but its designers know it's not possible to have 0 errors so they've built support for independent error recovery into the system: when an error occurs, the whole agent may be corrupted (in an invalid state or whatever) so the erlang way is to throw it out and have an independent "auditor" (the supervisor) decide what should be done. At scale, that's essentially what Netflix builds (and exercises with their Chaos Monkeys)
- muraiki 12y agoSomething to think about: this is similar what people are building (outside of Erlang/Elixir) using Docker and CoreOS. Deploy a bunch of instances of your app both to scale and to handle failures. Need to update the OS on a box? Restart it, there's other app instances to cover it. A container crashed? Restart it -- it's not sharing state with other instances so things will be fine. Another way to handle error propagation transparently is using monads. Even if you haven't used monads in Haskell, you've probably used them in Javascript: promises are monads. Consider how both errors and exceptions propagate through .then chains in promises. There might not be as much syntactic sugar in JS as there is in Haskell, but the idea is pretty much the same.
- falcolas 12y agoGo's panic() acts similarly to what you're expecting out of Erlang. You still have to call it for other libraries. > so it's not possible to "forget" to check for a null reference like you do in Go The Go compiler will complain if you don't use the `err` variable after creating it, helping to make you remember to check it, and letting you branch accordingly. This doesn't help with someone who re-uses the err variable or assigns the error return to `_`, but those are conscious choices to bypass the checks Go adds for you.
- rdtsc 12y ago> Go's panic() acts similarly to what you're expecting out of Erlang. You still have to call it for other libraries. Go uses a shared heap. So even though one go-routine panics, you can't safely assume the state of your system is still predictable. If it is not predictable you can't necessarily safely restart that go-routine. Without Erlang/Elixir I would actually do it with OS processes / containers at a higher level. Erlang's processes are only a few K of memory and are very easy to restart and handle so you get all that built in.
- falcolas 12y agoTo be fair, if you're modifying the heap from a goroutine, you're "doing it wrong". Go provides channels, which work quite well at reducing the need to mutate the global state. I use such a pattern quite frequently (the http.Server), and have never had a problem with heap corruption due to a handler goroutine panicing.
- rdtsc 12y agoThat's true. To that extent C, C++, Java work took. The Threads + thread safe queues is a common paradigm I've used. The problem comes with using other libraries, sharing code with others in a large code base. But errors and concurrency bugs still happen. But just like type systems, they are there not just to make code faster but add some guaranteed safety. The guaranteed is important. In case of concurrency, errors and state, the isolated heap also adds that guarantee. That is important.