DEV Community

Discussion on: Early Return in Elixir

Collapse
 
hlship profile image
Howard M. Lewis Ship

I subscribe to the common belief that exceptions should be reserved for exceptional situations: actual failures.

You should most certainly profile this code vs. a more traditional approach. I would be surprised if you didn't see a huge performance gap ... throwing an exception is most languages is very costly, and disruptive to the kinds of optimizations that a compiler might make.

Collapse
 
damonvjanis profile image
Damon Janis • Edited

I also subscribe to that belief, and it's how I've operated so far in my career in Elixir.

The interesting thing to me about throw/catch in strictly the Elixir context is that I've never actually seen it used for exceptions (or really anything else honestly). Typically the only thing you do with exceptions is rescue them, which isn't common but does come up occasionally. The one example I can think of is that the Bamboo email library until recent versions would raise instead of returning an :error tuple if the third-party email provider like SendGrid didn't send the email successfully, so you were forced to rescue if you wanted to handle an email failure gracefully.

I created a quick Benchee script on your recommendation, and these were the results on my 2013 MBP:

Comparison: 
return        6.31 M
throw         3.25 M - 1.94x slower +148.97 ns
**All measurements for memory usage were the same**
Enter fullscreen mode Exit fullscreen mode

Using a throw does interfere with tail call optimization, which is one of the tradeoffs you mentioned might be present. It looks like the code with the throw is about half as fast as the one with the return, so there is somewhat of a performance penalty.

I really am not recommending that anyone takes this as a pattern and uses it extensively instead of the usual control flow structures in Elixir. It's just an interesting quirk of the language that a rarely-used BIF plus the elegant syntax for function level catch / rescue handling turns out to implement an early return with surprisingly little ceremony.

EDIT: the original results I posted showed that the throw version was faster, but I realized there was a bug in my script and I was calling the throw version in both cases. I updated it, and the throw version is about twice as slow.