←back to thread

873 points belter | 2 comments | | HN request time: 0s | source
Show context
Terr_ ◴[] No.42946597[source]
> Java is a great language because it's boring [...] Types are assertions we make about the world

This is less of a mind-was-changed case and more just controversial, but... Checked Exceptions were a fundamentally good idea. They just needed some syntactic sugar to help redirect certain developers into less self-destructive ways of procrastinating on proper error handling.

In brief for non-Java folks: Checked Exceptions are a subset of all Exceptions. To throw them, they must be part of the function's type signature. To call that function, the caller code must make some kind of decision about what to do when that Checked Exception arrives. [0] It's basically another return type for the method, married with the conventions and flow-control features of Exceptions.

[0] Ex: Let it bubble up unimpeded, adding it to your own function signature; catch it and wrap it in your own exception with a type more appropriate to the layer of abstraction; catch it and log it; catch it and ignore it... Alas, many caught it and wrapped it in a generic RuntimeException.

replies(13): >>42946899 #>>42946979 #>>42947054 #>>42947147 #>>42947485 #>>42947568 #>>42948130 #>>42948153 #>>42948666 #>>42951688 #>>42952999 #>>42953957 #>>42984777 #
dustingetz ◴[] No.42946899[source]
remind me why they don’t work? because “throws Exception” propagates virally to every method in the codebase?
replies(3): >>42947046 #>>42948691 #>>42949347 #
bluGill ◴[] No.42949347[source]
Because eventually someone will want to add another exception to a new leaf class and the proper place to handle it is 20 functions down the call tree and every single one of those 20 functions needs to now add that new exception to their signature even though only the handler function cares and you adjusted it.

I haven't done java in decades, but I imagine this would get really nasty if you are passing a callback to a third party library and now your callback throws the new exception.

Checked exceptions seem like a great idea, but what java did is wrong. I'm not sure if other implementations are better.

replies(1): >>42949801 #
zaphar ◴[] No.42949801[source]
The counter point here is that at least with Checked Exceptions you know only those 20 functions are part of the code path that can throw that exception. In the runtime exception case you are unaware about that 21'st function elsewhere that now throws it and it's not in the correct handling path anymore.

You have no way to assert that the error is always correctly handled in the codebase. You are basically crossing your fingers and hoping that over the life of the codebase you don't break the invariant.

What was missing was a good way to convert checked exceptions from code you don't own into your own error domain. So instead java devs just avoided them because it was more work to do the proper error domain modeling.

replies(1): >>42950023 #
1. bluGill ◴[] No.42950023[source]
Like I said, the implementation is wrong. Adding an exception to that 21st function, and then that whole call chain as well ends up being a lot of work. Sure you eventually find the place to handle it, but it was a lot of effort in the mean time.

It gets worse. Sometimes we can prove that the 21st function because of the way it is calling your function can never trigger that exception, but still it will need code to handle it. If that handler code if the 21st function changes to trigger the exception now should be propagated back down but since you handled the exception before checked exceptions won't tell you that you handled it in the wrong place.

I don't know how to implement checked exceptions right. On paper they have a lot of great arguments for them. However in practice they don't work well in large projects (at least for java)

replies(1): >>42952355 #
2. zaphar ◴[] No.42952355[source]
The Rust Result type with accompanying `?` operator and the `Try*` traits are how you implement exceptions correctly. It makes it easy to model the error domain once in your trait implementations and then the `?` does the rest of the work.

You could imagine something similar with Exceptions where there is simple and ergonomic way to rethrow the exception into your own error domain with little to no extra work on the part of the developer.