6 ms·
> 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
by Seenso 7y ago
> 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.
- ravenstine 7y ago> 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.
- Seenso 7y ago> 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'); }
- sombremesa 7y agoMany 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.
- hombre_fatal 7y agoWell, 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.
- clon 7y ago> 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
- UK-Al05 7y ago> 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.
- joshuamorton 7y agoResult 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.
- UK-Al05 7y agoI find result and option types compose nicer using monadic style composition.
- iainmerrick 7y ago>> 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).
- dnautics 7y agoI 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.
- iainmerrick 7y agoAgreed, 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).
- kenoyer130 7y agoIn 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.
- iainmerrick 7y agoC++ too. I suspect it’s true in almost every language. Maybe not Python? But Python is slow regardless.
- petters 7y agoIn C++, exceptions are often faster than manual error checking when errors are rare. But C++ does not provide a stack trace.
- safety-second 7y agoC++ 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.
- iainmerrick 7y agoYou 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.
- earenndil 7y agoHence 'when errors are rare'.
- iainmerrick 7y ago
- baddox 7y ago> 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.
- erik_seaberg 7y agoIf 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.
- baddox 7y agoThat'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-matching.html#nonexhaustive https://www2.lib.uchicago.edu/keith/ocaml-class/pattern-matc... [1] https://dev.to/babak/exhaustive-type-checking-with-typescript-4l3f https://dev.to/babak/exhaustive-type-checking-with-typescrip...
- cheez 7y agoSQLAlchemy does this well: obj = session.query(MyObject).one_or_none() # no exception if missing obj = session.query(MyObject).one() # exception if missing
- hinkley 7y agoI was watching this the other day: https://www.youtube.com/watch?v=AnZ0uTOerUI 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.
- yawboakye 7y ago> It's not a normal thing for code that needs that record that wasn't found. Why call it then?