News for hackers. Stuff that matters.
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).