News for hackers. Stuff that matters.
Re:You’re better off using Exceptions by Seenso on Monday February 03, 2020 @11:57AM (#22226056)
> A record not being found is a normal thing!

It's not a normal thing for code that needs that record that wasn't found.

> They literally tell you nothing and there's no way to solve them without catching/rescuing them. A null value, a plain "error" object, or an error argument in a callback would have been sufficient. If I need an exception to be raised for this kind of thing, I'll do it myself.

They tell you lots: you asked for something your code path wanted and your request couldn't be satisfied. Also, you've been helpfully kicked onto the alternative execution path to handle that situation.

Exceptions are a hell of a lot better than littering your code with null checks or error code checks, especially when you forget one and get a null pointer error or your code wanders away from the root cause and fails later.

Re:You’re better off using Exceptions by ravenstine on Monday February 03, 2020 @12:18PM (#22226293)
> It's not a normal thing for code that needs that record that wasn't found.

No offense, but I don't know where that idea comes from. Systems are checking for records all the time in ways where the absence of data doesn't necessitate throwing an exception.

For instance, a page, user, or piece of media on a website may have existed at one point but was since deleted, but still has a permalink floating around the net. Is it useful, in the case that someone clicks on such a link, to throw an exception when I can instead choose to render different page content when a record wasn't found? I'll never need a stack trace for that.

> They tell you lots: you asked for something your code path wanted and your request couldn't be satisfied. Also, you've been helpfully kicked onto the alternative execution path to handle that situation.

Why would I want a code path for expected behavior? I agree for cases like a failed connection, where the system is actually broken, but there isn't anything fundamentally broken about data absence.

> Also, you've been helpfully kicked onto the alternative execution path to handle that situation.

That's not only presumptuous, but if I wanted that to happen, I can do so myself.

> Exceptions are a hell of a lot better than littering your code with null checks or error code checks, especially when you forget one and get a null pointer error.

Hmmm...

  const record = store.findRecord(params.id);
  
  if (record) {
    render('show-record');
  } else {
    render('record-not-found');
  }

or...

  try {
    const record = store.findRecord(params.id);
    render('show-record');
  } catch(err) {
    if (err.name === 'RECORD_NOT_FOUND') {
      render('record-not-found');
    } else {
      throw err;
    }
  }
I'll let people decide which one is better. I personally prefer the first one.
Re:You’re better off using Exceptions by hombre_fatal on Monday February 03, 2020 @12:37PM (#22226582)
Well, in real code you are likely bubbling up an error in some way for your 404 Not Found and 500 Internal Error handlers to kick in.

    item = db.findItem(id)
    assert(NotFoundError, item != null)
    render('show-item', item)
upstream middleware:

    try {
      res = await downstream() 
    } catch(e) {
      if (e is NotFoundError)
        render('not-found')
      else
        render('internal-error')
    }
you can do this with other patterns for upstreaming errors like Result + Rust's handy short-circuiting, but your example doesn't demonstrate much. what about internal errors like your store throwing a database error, your first example doesn't even have a store capable of erroring? your comparison incomplete. and surely you don't do all error checking at every callsite no matter which pattern you use.
Re:You’re better off using Exceptions by clon on Monday February 03, 2020 @12:51PM (#22226767)
> Well, in real code you are likely bubbling up an error in some way for your 404 Not Found and 500 Internal Error handlers to kick in.

Which is essentially GOTO 404

A horrible idea that I see often in clever frameworks disobeying encapsulation and reasonable control flow in favour of magic

Re:You’re better off using Exceptions by _ZeD_ on Monday February 03, 2020 @01:15PM (#22227128)
That is a wonderful idea. You don't need to deal with 404, 500 or other stuff, you just write for the "happy path". The framework deals with the rest.
Re:You’re better off using Exceptions by mumblemumble on Monday February 03, 2020 @01:43PM (#22227441)
It's both wonderful and terrible.

On the one hand, it's terribly convenient when you're writing the code.

On the other hand, as clon says, it breaks encapsulation. Not necessarily, but I've rarely seen these style of Web frameworks used in a way that doesn't result in network communication concerns wormholing their way through the entire codebase. And it sometimes does do weird things with control flow. Again, not necessarily but in practice. For example, JAX-RS frameworks do the exception mapping off in some magic location that's outside the part of the call stack that you directly control, meaning that customizing the logic requires hacking some fairly complex special-purpose mechanisms into the framework.

I suspect that there's a way to achieve convenience without relying on spooky action at a distance. But one could be forgiven for looking at the way popular frameworks on popular platforms work and concluding that cleanliness must be the price of convenience.

Re:You’re better off using Exceptions by majewsky on Monday February 03, 2020 @01:29PM (#22227282)
Then you fill in a complex form with 6 object references, and the framework just spits out "NOT FOUND", which means "have fun figuring out which one is the missing object".
Re:You’re better off using Exceptions by joshuamorton on Monday February 03, 2020 @01:59PM (#22227616)
`raise MissingField(field_name)` solves this problem.
Re:You’re better off using Exceptions by ravenstine on Monday February 03, 2020 @02:01PM (#22227633)
At which point you end up doing the same work you would have had to do in the first place, except with extra lines for exception handling. ;)
Re:You’re better off using Exceptions by cjfd on Monday February 03, 2020 @01:01PM (#22226929)
The code example is not convincing at all. Obviously a single if is much cleaner than a single try/catch block but that is not the comparison. The if needs to be replicated N times for every N things that could fail and that are calling each other in a potentially deep call hierarchy. The try/catch block only needs to occur once at a level that is above all of the N things that might fail.
Re:You’re better off using Exceptions by ravenstine on Monday February 03, 2020 @03:50PM (#22228895)
That doesn't always make sense when you're dealing with a generic exception class. What you are assuming is that a try-catch block higher up in the hierarchy can handle a RecordNotFound exception for all its descendants, but that's not necessarily going to be the case. It's basically the inheritance problem in object-oriented programming, but in reverse. This is why I'm in favor if exceptions for non-broken states to not be a thing or at least be optional. Making a request to an API or database and getting a successful response returning no data(or a message that says data wasn't found) means that the code and the services in the system are actually working correctly, and throwing an exception is treating the situation like a code problem when it could(and often is) simply be a data problem, which is common and often expected.
Re:You’re better off using Exceptions by cjfd on Tuesday February 04, 2020 @10:51AM (#22236927)
Yes, the try/catch block is better if the catch block can handle exceptions for all its descendants. As always in programming YMMV and this is sometimes true and sometimes it is not and when it is not it is better not to throw an exception.

I agree that a database API should not itself throw an error if an empty result set is found. It may throw an error if there is an SQL error, though, and really should. When one gets an empty result set where this should not happen, for instance because a row was just inserted and one is in a transaction, the user code could throw an exception instead.

Making exceptions optional is something that I have indeed done in the past because some uses of the code could benefit from exceptions while other uses did not so much.

Re:You’re better off using Exceptions by dllthomas on Monday February 03, 2020 @01:21PM (#22227187)
In my experience, on the web there are two different kinds of things we might be fetching.

Some things are essential to the page we are rendering, and if they are missing we should 404.

Some things are ancillary, and if they are missing we should render a stub in their place. E.g., saying that a comment is from "deleted user".

Exceptions are a good fit for the former, where we want to unify every failure. They are a poor choice for the other case, where we want to treat each failure special.

Which case a given fetch represents is a choice that should be driven by the fetcher.

Re:You’re better off using Exceptions by AstralStorm on Tuesday February 04, 2020 @11:52AM (#22237813)
Why are they a poor choice for the latter?

They have a type, make it say MissingUserException and the special place can handle it in a special way. Ensure it is a subclass of some general exception type you also handle elsewhere and you're good.

Often people throw generic exceptions instead of using the type system.

Re:You’re better off using Exceptions by dllthomas on Tuesday February 04, 2020 @07:50PM (#22242934)
In the latter case you will often want to pass "data or the fact the data was missing" along to other functions. With a result value (of whatever form) that's trivial. With exceptions, it's certainly plenty doable but it's messy.
Re:You’re better off using Exceptions by cjfd on Monday February 03, 2020 @02:01PM (#22227642)
Sure, it depends on the situation. In the case you outline, there could also be room for an auxiliary function like fetch_alternatives({ preferred_resouce, second_best_resource , third_best_resource}) that would fetch them in until one can actually be obtained and throw an exception if all of them fail. Not that I am married to exceptions or anything. One should just use what is best in the situation that one is in. Actually, I think articles that claim that one language construct is better than some other language construct are quite silly. It mostly depends on the situation. Though, there also may be language constructs that are best left off deprecated, depending the language that you are using.
Re:You’re better off using Exceptions by irjustin on Monday February 03, 2020 @12:56PM (#22226852)
In general, I think this is why Rust uses `panic` since the name is way... angrier.

I like it. I wish that the concept of exceptions was closer to Panic because then we wouldn't be doing nearly as much flow control.

In the Rails world (where I live) looking top down, exceptions are handled via flow control or 500 pages. Anything that doesn't result in a 500 is effectively flow control.

While I agree with Rust's semantics, it generally isn't that bad to work around and knowing which is what.

Re:You’re better off using Exceptions by Seenso on Monday February 03, 2020 @12:52PM (#22226789)
> I'll let people decide which one is better. I personally prefer the first one.

That's JavaScript, right? That's probably not the best language to use to judge the concept of exceptions.

It's cleaner in languages like Java:

  try {
    Record record = store.findRecord(params.id);
    render(record);
  } catch(RecordNotFoundException e) {
      render('record-not-found');
  } catch (Exception e) {
      render('unexpected-error');
  }
Re:You’re better off using Exceptions by ravenstine on Monday February 03, 2020 @03:44PM (#22228822)
@Seenso You probably won't find, this, but I think I somewhat misunderstood the first thing you said.

My response to that would be that if it isn't a normal thing for your code to encounter a record not being found, then you should handle that state in whatever you see fit, whether that's by passing down a value or object reflecting that error state or throwing your own exception(yes, I do think such a thing can be appropriate). I still don't favor the latter, but at least with throwing your own exception your application can catch it at a higher level and will be less likely to mistake it for another exception of the same class that should be handled differently. If you are using an SDK that throws an error for a "record not found" type situation, it would certainly work in your favor if your application really does benefit from it, but I think a lot of code doesn't actually call for such a thing.

But my point is that there's no reason I can think of that an exception can't be totally optional in these circumstances. I don't think it should be assumed that everyone wants an exception. There's a reason why we use conditional syntax like if-statements because, if used try-catch syntax as control flow for everything, the flow would be bass ackwards and hard to understand. In my opinion, the example situation I described is on the borderlands where an exception might make sense but probably doesn't actually make things better.

Every project is different, and people might choose to use exceptions a lot, which is fine for them. I've personally been better off not relying on the exception-based control flow taught to me when I learned Ruby, but that doesn't mean I would come into a project and remove everyone's exception handling.

Re:You’re better off using Exceptions by kstenerud on Monday February 03, 2020 @03:14PM (#22228477)
Your first example loses the real reason for the error and just assumes "record-not-found", so it will give misleading errors when the network drops for a moment, possibly resulting in an attempt to insert a duplicate record if the code tries to add-if-not-found. That'll make for some debugging fun!

And this is the problem with error checks: They're purely optional as far as the language is concerned, so we keep getting them wrong or forgetting things, even when we're being careful.

Re:You’re better off using Exceptions by necovek on Monday February 03, 2020 @02:46PM (#22228161)
That's a pretty good example. Now imagine you want to structure your code in a way that render('record-not-found') happens in a caller to that block. So exceptions as a general approach work pretty well to allow simple code organisation according to your needs.

And you are the one who knows best if something is an expected failure, so wrap that up in a fetch_one_or_none() — as a matter of fact, most DB interfaces I work with have an API of the kind.

Basically, exceptions are a decent way to keep what might need to be a separate control flow, well, separate. And you can choose at any time to "join them in". As a concept, they are quite like interrupts on the very low (hardware) level, so you can learn only one "tool" to deal with them.

(And yes, I can trivially find cases where I want errors to bubble up when a record is not found, eg. a required configuration record or similar)

Re:You’re better off using Exceptions by sombremesa on Monday February 03, 2020 @12:29PM (#22226471)
Many years of experience tell me that you're going down the futile path of getting a tiger to change its stripes. As more and more devs get minted, prepare for codebases (and libraries) to get a LOT more gnarly. Even so, it's a good thing to lower barriers of entry and we can be discerning about where we get our code.
Re:You’re better off using Exceptions by je42 on Monday February 03, 2020 @02:27PM (#22227950)
The article is fairly specific:

The first example: - no stack trace - loosing the specifics of error to troubleshoot.

The second example: - explicitly discards the stack trace - explicitly discards the error specifics.

Hence, if this is really what you want. fine, but in a lot of cases you do want to see stack traces and the exact reasons (and meta data why an error was thrown).

Re:You’re better off using Exceptions by iainmerrick on Monday February 03, 2020 @12:34PM (#22226534)
>> A record not being found is a normal thing!

> It's not a normal thing for code that needs that record that wasn't found.

This is something of a religious dispute and there are arguments both ways.

But I think it’s important to note that an exception is vastly more expensive than a simple function call or return value. So I’d say you need a really good reason to use exceptions.

I agree with the GP that in general, “lookup failed” is not an exceptional error. If it always succeeds, why are you looking up something in the first place?

I agree with the original poster that exceptions do have their place, even though they’re expensive.

If you have the primary key of a database object that you know to be correct, and you try to retrieve that record but fail, that might legitimately be an exception (unless having records deleted from under you is a common occurrence in your app).

Re:You’re better off using Exceptions by dnautics on Monday February 03, 2020 @01:38PM (#22227370)
I don't see what the problem is. The API should expose two methods: `fetch()` which returns, say, nil if there's no result (or an error monad or error tuple, depending on your language), and say `fetch!()` (or `fetch_throws()` if your language doesn't support exclamation points) which raises an exception if there isn't a result. The programmer gets to choose, on a case-by-case basis.
Re:You’re better off using Exceptions by iainmerrick on Monday February 03, 2020 @03:02PM (#22228344)
Agreed, it’s ideal to have both options available.

Kotlin gets this right for the most part (although I think it would be better if it had checked exceptions).

Re:You’re better off using Exceptions by kenoyer130 on Monday February 03, 2020 @12:36PM (#22226560)
In Java/C# land exceptions are EXPENSIVE. Like magnatudes more expensive. You have to build a full stack trace etc. Removing places in the code where it is "Throwing exceptions for non exceptional circumstances" has a dramatic performance increase benefit.
Re:You’re better off using Exceptions by aratauto on Monday February 03, 2020 @01:10PM (#22227063)
Technically in Java it is possible to create an exception object once, and throw it multiple times, making throws much cheaper as the stack trace is filled during the object's construction only.
Re:You’re better off using Exceptions by acidictadpole on Monday February 03, 2020 @04:10PM (#22229141)
Does this actually save much time? I thought it was the throw mechanic that was slow, rather than just creating the exception?
Re:You’re better off using Exceptions by aratauto on Monday February 03, 2020 @06:43PM (#22230655)
According my unscientific tests, it is about over 100 times faster. On my laptop I can throw one million exceptions in 15ms millisecond, when reusing the same exception objects, compared to 2 seconds when creating a new exception object every time with a quite shallow stack.

When a new exception object is created, the slowest operation is filling information about the stack trace. It is possible to override Exception's fillInStackTrace() with an empty implementation. In this case throwing exceptions with new exception objects, is only slightly slower than using one exception object (17ms vs 15ms for 1M throws).

Deeper stacks make difference even bigger. Adding 100 nested invocations to the stack slow downs the classic approach (a new exception object created just before invocation) to 9 seconds, while alternative approaches are not affected.

Re:You’re better off using Exceptions by haagen on Tuesday February 04, 2020 @10:29AM (#22236616)
Re:You’re better off using Exceptions by ReidZB on Monday February 03, 2020 @01:11PM (#22227080)
The HotSpot JVM may decide to stop building stack traces in some circumstances to improve performance (see the 'OmitStackTraceInFastThrow' option, enabled by default). Not sure about other languages / runtimes though.

That said, I agree exceptions should be reserved for exceptional circumstances. Something like checking if a record exists or not should probably use something like Optional instead, unless you are in a situation where a record "should" exist but doesn't (which is itself an exceptional case).

Re:You’re better off using Exceptions by iainmerrick on Monday February 03, 2020 @12:41PM (#22226642)
C++ too. I suspect it’s true in almost every language.

Maybe not Python? But Python is slow regardless.

Re:You’re better off using Exceptions by petters on Monday February 03, 2020 @12:55PM (#22226839)
In C++, exceptions are often faster than manual error checking when errors are rare.

But C++ does not provide a stack trace.

Re:You’re better off using Exceptions by safety-second on Monday February 03, 2020 @02:37PM (#22228074)
C++ exceptions are only good if you use them like signals from C or panics in Go. This means you have to use error values for 99% of errors or you lose this benefit. The fact that the STL sprinkles exceptions everywhere doesn't help this. C++ exceptions are so non-deterministic and slow that every real-time system disables them just to be sure.
Re:You’re better off using Exceptions by renox on Monday February 03, 2020 @05:14PM (#22229880)
> But C++ does not provide a stack trace

Which makes exception in C++ a very, very bad idea!

My reaction to 'the test fails because map::at threw an exception': stupid STL!!

A core dump would be so much easier to analyze.. Gdb's 'catch thow' is wonderful (well, it is after I fixed the part of our codebase which use exceptions as a control flow mechanism)

Re:You’re better off using Exceptions by nemetroid on Monday February 03, 2020 @06:13PM (#22230428)
> My reaction to 'the test fails because map::at threw an exception': stupid STL!!

> A core dump would be so much easier to analyze..

Uncaught exceptions call terminate(), which by default calls abort(), which generates a core dump. So if you want a core dump, just avoid catching the exception.

Re:You’re better off using Exceptions by renox on Tuesday February 04, 2020 @04:57AM (#22233946)
Except that I didn't put all these "catch".. But you're right, that's what should be done (or at least, rethrowing the exception from the catch block).
Re:You’re better off using Exceptions by AstralStorm on Tuesday February 04, 2020 @11:57AM (#22237897)
C++ can have a stack trace, there are enough libraries providing this functionality and even compiler extensions. Say, libunwind, though you will have to demangle names. And a debugger will see the stack as it is.

Also works in C.

Re:You’re better off using Exceptions by iainmerrick on Monday February 03, 2020 @02:48PM (#22228191)
You don’t always get a stack trace, but it still has to unwind the stack, calling destructors as needed, and check the exception’s type against each catch block.
Re:You’re better off using Exceptions by earenndil on Monday February 03, 2020 @02:53PM (#22228232)
Hence 'when errors are rare'.
Re:You’re better off using Exceptions by iainmerrick on Monday February 03, 2020 @02:59PM (#22228303)
I guess we’re violently agreeing? Exceptions are an order of magnitude (or several orders) slower than function calls and returns; but that can still be efficient overall, if only a tiny fraction of calls fail.

I’d simply add that most people greatly underestimate how expensive exceptions are, even in C++.

Re:You’re better off using Exceptions by AstralStorm on Tuesday February 04, 2020 @12:00PM (#22237930)
Calling the destructors is identical to ending any scope in C++, including function calls. Where is that extra expense?
Re:You’re better off using Exceptions by iainmerrick on Tuesday February 04, 2020 @12:33PM (#22238370)
You likely have to parse exception blocks and maybe chase pointers to find the objects to destroy. In the happy path, you can just call the destructors directly.
Re:You’re better off using Exceptions by baddox on Monday February 03, 2020 @12:38PM (#22226585)
> It's not a normal thing for code that needs that record that wasn't found.

In many popular programming languages, the way to check whether some value has a certain property is with an “if” statement. It would be very odd to replace every code path inside an “if” statement with exception control flow simply because that code path “needs” some condition to be met for it it to execute.

Re:You’re better off using Exceptions by erik_seaberg on Monday February 03, 2020 @04:19PM (#22229237)
If a user doesn't exist, any code that relies on having a user is broken, and I want a guarantee that code can't be reached. Throwing does that, mapping an Option does that, but "if" doesn't.
Re:You’re better off using Exceptions by baddox on Monday February 03, 2020 @06:39PM (#22230620)
That's just a matter of a specific language's syntax, type-checking, or linting. Pattern-matching in OCaml [0] will in most cases warn about non-exhaustive checks. It seems like TypeScript has something similar, although I haven't used it [1].

[0] https://www2.lib.uchicago.edu/keith/ocaml-class/pattern-matc...

[1] https://dev.to/babak/exhaustive-type-checking-with-typescrip...

Re:You’re better off using Exceptions by hinkley on Monday February 03, 2020 @02:09PM (#22227745)
I was watching this the other day:

https://www.youtube.com/watch?v=AnZ0uTOerUI Unconditional Code by Michael Feathers

One solution to this problem is discussed toward the middle: giving data instead of asking for data avoids error conditions. Doing so pushes use of the data farther down the call tree and pulls acquisition up near the top, where we are closer to the user and thus able to more clearly decide what if anything to do about these corner cases.

Re:You’re better off using Exceptions by UK-Al05 on Monday February 03, 2020 @12:26PM (#22226404)
> Exceptions are a hell of a lot better than littering your code with null checks or error code checks, especially when you forget one and get a null pointer error or your code wanders away from the root cause and fails later.

That's basically limitation of the language. Null checks and result checking can basically be abstracted away with non-nullable types, option types, and result types and bind.

You only need to do match or check right at the edge, so they're not everywhere.

Re:You’re better off using Exceptions by joshuamorton on Monday February 03, 2020 @02:03PM (#22227666)
Result types are exceptions with different performance characteristics.

Like, handling a checked exception (Java) and a Google-cpp StatusOr<T> or a rust Result<T> provide extraordinary similar code. Unchecked exceptions provide a bit of extra dynamism, and languages without either are anti-user.

Re:You’re better off using Exceptions by UK-Al05 on Monday February 03, 2020 @02:32PM (#22228013)
I find result and option types compose nicer using monadic style composition.
Re:You’re better off using Exceptions by cheez on Monday February 03, 2020 @01:54PM (#22227549)
SQLAlchemy does this well:

obj = session.query(MyObject).one_or_none() # no exception if missing

obj = session.query(MyObject).one() # exception if missing

Re:You’re better off using Exceptions by yawboakye on Monday February 03, 2020 @02:19PM (#22227869)
> It's not a normal thing for code that needs that record that wasn't found.

Why call it then?