Asked on Reddit: Which of Rust, D, Go, Nim, and Crystal is the strongest and why?

Dave via Digitalmars-d digitalmars-d at puremagic.com
Thu Jun 11 19:53:42 PDT 2015


It should be noted that functional languages that utilize monads
often make you consider the exceptional case, and this is
enforced by the compiler (sound familiar?)

> I also literally said "with the limitness of returned errors.". 
> That part is important part of the sentence. My point is that 
> the exception mechanism is much less limited from the returned 
> value mechanism, because it lets you handler the error in a 
> higher level without modifying the middle levels to acknowledge 
> it. The price for this is slowness.
>
> The benefits of nothrow by default come naturally with returned 
> errors - the users can't implicitly ignore them, and since the 
> possible errors are encoded into the return type any tool that 
> can display the return type can show you these errors.
>
> With nothrow by default, you are paying the performance price 
> of an exception mechanism, and then do extra work to add to it 
> the limitations of returned errors, just so you can get the 
> benefits that come naturally with returned errors. Wouldn't it 
> be better to just use returned errors in the first place?

Perhaps I am missing something big, but I don't see the
performance price that is being paid with nothrow as an
annotation nor as a default. Even with your explanation. No throw
means no throw. No expensive exception mechanism is used. You can
still return whatever you want. You are just guaranteeing no
exceptions.

> This is not very different than returned errors: you either 
> handle the error or change the function's signature so it can 
> pass the error to it's caller.

I agree for the most part on this.

> The big benefit of unchecked exceptions is that they require 
> minimal effort to use - when I code and encounter a path that 
> you can't handle, I just throw an exception(I can also use 
> helper assert/enforce functions). I don't need to interrupt the 
> flow and start adding `throws` all over the project, making my 
> brain do a "context switch" that makes me forget the immediate 
> thing I was working on, and increasing the chances of my commit 
> conflicting with other concurrent commits(because it was 
> touching general functions all over the place). No - I throw 
> the exception, and leave worrying to how it will be handled to 
> later.

The one issues with returned errors is unless you wrap it up in
something like Expected<T> or a specialized struct of some sort,
you can only return one thing in many languages. Even if the
language supports a tuple syntax, in the desired case (where
things work as expected) you return information that is unneeded
(the error). This is something that the Expected<T> object
considers. I'd say this is where exceptions are beneficial (not
the fact that you can throw them and not worry about them),
because you only pay the price when needed, at the expense of
some overhead with the exception handler and the code slowing on
the exceptional path. Take as a counter example of Go which uses
multiple return values to inform users of errors, you get
information that is only useful sometimes.

> The alternative is not that I leave my current task to 
> implement something that'll handle the exception at the right 
> place - the alternative is that I continue my current task 
> without throwing the exception, and only deal with it later on 
> when something crashes or doesn't work properly.
>
> Why? Because I have a task to do, and if every time I encounter 
> a place that *might* indicate an error I'll need to make sure 
> that error is handled, I won't be able to focus on the actual 
> task.

Dealing with the exceptional case should be "part" of the task.
It may be boring, but that is how you write solid code. Because
if your sequence of logic requires Parse() to succeed to get a
result to pass into B(), but Parse() fails, continuing on and
using B() may cause issues. Now throwing an exception is really
useful in this case because you guarantee that you aren't going
to do B with undefined data or defaulted data. However, unless it
is clear, people may not know they need to catch an exception.
Thus the exception propagates up to the top and we now have a
problem.

So now imagine a call server where we have multiple processes
each handling thousands of calls. A call comes in and during the
processing of the call an error occurred and an exception is
thrown. Now if you catch the exception at it's source, no
problem. You can just log that something unexpected happen and
not accept that specific call. If you don't handle the exception
and it manages to escape to the top of the process, bye bye to
all those calls rather than just one. You now have thousands of
upset customers rather than one.

If you are using returned errors and you ignore the error for
some reason and continue on with the process and try to use a
pointer that you expected to be set by a function in which you
didn't check the error for, bye bye thousands of calls (if your
lucky...). Long story short. Respect your errors.

You can often get away with not catching exceptions in UI code,
because often times the UI code intercepts the exception at an
upper level and prints out an error in a window or something.
This becomes trickier in server code.

> Out of the unhandled error possibilities, exceptions are 
> easiest to fix when they do happen. Forbidding unchecked 
> exceptions doesn't mean there are no errors - just that you 
> don't have this amazing mechanism for tracking and debugging 
> them.

Why have unhandled errors in the first place? Design this problem
away.


More information about the Digitalmars-d mailing list