Julia Samól
11/09/2022, 6:06 PM@Throws
• not to overlook functions annotated with @Throws
and handle them properly?ephemient
11/09/2022, 6:13 PMephemient
11/09/2022, 6:17 PMephemient
11/09/2022, 6:19 PM() -> Unit
is a @Throws(E::class) () -> Unit
but you can't allow vice versa, if you want to be able to track exceptions through lambda blocks (which you'd have to, for consistency with existing Kotlin standard library usage at least)ephemient
11/09/2022, 6:20 PM@Throws(E::class, F::class)
and @Throws(F::class, E::class)
are equivalentephemient
11/09/2022, 6:22 PM<T...> @Throws(T::class...)
, maybe with union types but that's a whole other challenge to get rightCasey Brooks
11/09/2022, 6:25 PMCasey Brooks
11/09/2022, 6:28 PMJulia Samól
11/09/2022, 10:12 PMhow does that not make it equivalent to checked exceptions in the language?Fair point. I guess the difference would be that by default it would stay as it is and only if you really wanted or had to, would you turn it on so the compiler could warn you or abort with an error (again, similarly to how the explicit API mode works). So technically, you could say they’re unchecked unless you explicitly ask otherwise. Other than that, part of me just wanted to start a discussion on the topic and see what others have to say about it and what a better way than throw in an idea that can be dismantled and, hopefully, improved? 🙂
ephemient
11/09/2022, 10:24 PMephemient
11/09/2022, 10:31 PMfun f(block: () -> Unit) { block() }
fun g(block: () -> Unit) { callbacks += block }
fun main() {
f { throw Exception() }
g { throw Exception() }
}
assuming f
and g
are code in some external library, clearly calling f
should tell you to add @Throws(Exception::class)
to main
and calling g
shouldn't, but we can't actually analyze that accuratelyephemient
11/09/2022, 10:37 PMJulia Samól
11/09/2022, 10:38 PMI think the better approach, rather than adjusting the language, is to adjust your thinking about the runtime safety of your program [...]I agree with what you’ve said there... mostly. The only thing I don’t agree with is the part where you said that “you have to assume”. Why should we assume? This is exactly the reason software usually doesn’t work as intended, because a developer assumed something incorrectly, whether it was an access to a null reference in Java, a property missing in a JavaScript object or an overlooked exception in Kotlin. I’ve always thought that the goal is to improve how we write code, leaving less and less space for human error and busting the quality of code or even the experience of writing it by reducing the amount of redundancy that needs to be added due to some uncertainties. Kotlin did great on many levels, but, disputably, exceptions seem to be left behind or maybe it took a step back with them by making the code depending on them more error-prone.
Just assume that your code could throw an error, and make sure there’s a centralized error handler near the top of your application, and now you don’t need to worry about the vast majority of exceptions that get thrown.True, but I would argue that, depending on a framework you use or the purpose of your application, it could be impossible to have only one truly centralized handler and rather you’d find yourself scoping your error handling between different parts of your applications. The more places where you have to remember to put your handler to, the more likely it gets that eventually you miss one, exposing yourself to a threat of a crash. I haven’t put that much thought into this yet, but I’m also expecting that it can get even more interesting when we bring asynchronous code into this. In general, I’m not looking for a way to write code as people would in Java, I’m looking for a way of improving the compile-time safety of my Kotlin code. And as long as someone can throw an exception in a 3rd party library I’m using and the compiler won’t, at least, warn me about it, I can’t be sure if each line of my code is truly covered by my handlers.
Julia Samól
11/09/2022, 10:51 PMephemient
11/09/2022, 11:24 PMException
, then
▪︎ you can't write Swift-like higher-order functions (rethrows
) that are generic over both without further tradeoffs, and
▪︎ it doesn't tell you which exception types are rethrown, so Java interoperability is still bad
◦ if you have function types for non-throwing and for throwing a generic T
, then
▪︎ you still have the problem of generic higher-order functions
▪︎ you can't write lambdas that throw multiple exception types without losing type information
◦ if you have N function types, one for every set of possible thrown exception types, then
▪︎ congratulations, you are trying to tackle union types on JVM super early on in a language's life, good luck with that
• if you don't have checked exceptions, then
◦ you only need one function type, JVM doesn't even care about declaring checked exceptions, everything is much simpler
so regardless of whether you think checked exceptions are a good idea or a bad idea, I think this was the only reasonable path to Kotlin 1.0. now that the language is more mature, there's a potential for the Kotlin team to revisit past decisions, but I'm not sure how anything will pan out (especially since compatibility back to 1.0-compiled code is guaranteed)ephemient
11/09/2022, 11:26 PMI actually wasn’t expecting that any work on exception handling was underwaythe existence of a YouTrack issue doesn't mean there's any work under way, it just means somebody filed an issue
Julia Samól
11/10/2022, 8:13 AMthe existence of a YouTrack issue doesn’t mean there’s any work under way, it just means somebody filed an issueoh, you’re right, I took a too quick look yesterday and must have felt under wrong impression
Alejandro Serrano Mena
11/10/2022, 8:33 AMephemient
11/10/2022, 8:08 PM