In future, could Kotlin have checked exception?

The trouble is there are at the very least three types of exceptions in Java which are intended for very different purposes.

RuntimeException class is intended for:

  • programmer error, almost always unrecoverable

Error class is intended for:

  • serious system or environmental error, almost always unrecoverable

Exception class (the only checked one) is intended for:

  • less likely conditions that will happen during the normal course of program operation

One of these clearly stick out like a sore thumb here.

It’s unfortunate in my opinion that RuntimeException is a subclass of Exception. RuntimeException was probably misnamed and might better have been a subclass of Error and called ProgrammerError, but I digress.

RuntimeException should never happen in a reasonable situation and should almost always crash your application, like for example IllegalArgumentException or ArrayIndexOutOfBoundsException. It’s really up to the developer of an application to decide when where and how to handle Exception (the checked one) but if an API throws it then it really should be handled and definitely shouldn’t be lumped in with RuntimeException.

A top level handler for checked exceptions may or may not make sense depending on the application, but it almost certainly doesn’t make sense for RuntimeExceptions like IllegalArgumentException or ArrayIndexOutOfBoundsException, programs that encounter those programmer errors are unlikely to continue functioning in any reasonable fashion.

Kotlin does not have a goal of “data safety”, it has a goal of safer programming practice; prevent developers from making programming mistakes which the compiler can easily catch.

I agree with the aim of preventing programmer error, I just have the experience that checked exceptions don’t quite help enough in practice, and have a bit more development overhead than the benefit they do give - like strong types can, in languages without type inference. Of course, your experience and others’ may differ, and I reserve the right to change my mind :wink:

I understand those three categories of exception - I came across a similar definition in other languages years back, before Java had any great mind-share. Even then, I found that some exceptional situations just aren’t clear-cut and/or, specially in the case of libraries, might have different significance in different circumstances. Is a “file not found” exception a serious error because it’s a critical platform resource, or a required config file? or programmer error because I constructed a path wrongly? or user error because they typed in a filename wrongly? or just hard to tell, because actually a virus scanner mistakenly quarantined it? Can and should the application do its best to continue nonetheless, because availability is important for, e.g., real-world safety reasons?

I’d love to have good mechanisms to find these problems early, I’ve just never been convinced that checked exceptions are such a mechanism.

So … it’s not my place to say you’re wrong, but personally I hope Kotlin doesn’t get (mandatory) checked exceptions.

There’s definitely competing things: Kotlin wants to be interoperable with Java, but they don’t want checked exceptions. I think the solution isn’t adding checked exceptions, it’s better tooling from JetBrains to warn you about the checked exceptions thrown by Java code.

1 Like

I believe one of the original motivating factors was how exceptions interact with lambdas.

Suppose Kotlin had checked exceptions, like Java. In a language structure like a loop, exceptions are transparent, so that if the code inside the loop could throw a checked exception, you could catch it either inside the loop:

for (i in list) {
    try {
        // Something risky
    } catch (x: SomeException) {
        // Handle it
    }
}

Or outside:

try {
    for (i in list) {
        // Something risky
    }
} catch (x: SomeException) {
    // Handle it
}

Either way around, the compiler knows which checked exception(s) need to be handled.

You want lambdas, and higher-order methods that accept them, to work just the same, e.g.:

try {
    list.filter { /* Something risky */ }
} catch (x: SomeException) {
    // Handle it
}

But that wouldn’t work! The lambda implements a function, and that function would need to declare its exception types. If it didn’t define any, then no lambda could throw any checked exception. On the other hand, if it was declared to throw Exception, then every lambda would need to be surrounded by a try…catch (or the caller would need to declare that it throws Exception too). Which is clearly impractical.

So making all exceptions unchecked was a way around that.

However, picking up on a couple of posts earlier in this discussion, I wonder whether there could be another solution…

Could Kotlin infer exception types? It infers parameter types and return types pretty well. If it could infer the exception types that a lambda or function could throw, then it could all work transparently, just like native structures: if a lambda could throw one particular checked exception, then the function would be inferred to throw it, and the higher-order method calling it could pass that back to the calling code in the same way, so the compiler would be able to check that the caller handled (or declared) it.

If that could work, we wouldn’t need to ditch checked exceptions in order for lambdas to work naturally. And it could remove much of the boilerplate that checked exceptions bring, while still keeping their strong typing – more of the same benefits as Kotlin’s existing type inference.

Of course, I don’t know whether exception inference could be implemented. I suspect it wouldn’t be fully compatible with Java bytecode, for one thing; that would mean it would have to be sorted out at compile-time, and might not interoperate well. And it might need union types. That’s if it could be shoe-horned in at all.

And it doesn’t address some of the other problems with checked exceptions. (For example: the problems with translating exceptions between different layers of abstraction, or the tendency of bad developers to declare empty catch blocks simply to please the compiler.)

But it might be worth thinking about!

I like the idea of checked exceptions. But I hate the syntax for catching exceptions in Java and Kotlin so much that I’d rather not be forced to deal with it.

Maybe we can find a way that somehow integrates checked exceptions into the return value syntax of functions.

fun doSomethingSilently(block: () -> Unit) {
    try {
        block()
    }
    catch (e: Exception) {
    }
}

You can’t infer that a function that is passed a lambda that throws an exception will also throw that exception.

Hello.

I have been a heavy user of Java now for more than a decade, and I have never known that a throws clause was of any use until recently when my application needed a way to translate one type of exception into another when an exception had unwound the stack to a certain point.

I now feel that checked exceptions are an extremely important in some circumstances; perhaps the Kotlin steering committee should consider a special type of exception (CheckedException) and introduce a throws clause in its syntax so that extensions of a CheckedException can be subject to a throws clause as they are in Java. Kotlin would prove more of an interest to me if something like this were implemented.

As for lambdas; I have no advice, but I do believe the problem of lambdas and checked exceptions have seen programmatic solutions developed in Java. I would say there is no real impediment to these solutions working adequately in Kotlin.

There you go; my two bits.

I would say that Exceptions (checked is even worse) are actually problematic in all cases where you wish to recover from the exception. It is possible to do this correctly, but 99% of the code is not actually exception safe. What that means is that if an exception is thrown somewhere in the wrong place the state of objects/the program may actually easily become inconsistent/invalid.

Checked exceptions add the issue with exception nesting. This is when you have to wrap an exception into another one just to meet requirements of an API that does not allow throwing that exception (and the API is not under your control). I can’t count the amount of times I have written something like:

  try {
     // do something throwing a NastyCheckedException
  } catch (e: NastyCheckedException) {
    throw RuntimeException(e)
  }

Consensus seems that a Try monad (it sound scarier than it is) is the best (safest, clear, concise code). Despite having “monad” in the name.

1 Like

Just curious, has anyone submitted a formal request (for inspection option) by creating a ticket? Since my comment here over 2 years ago where I requested to have some sort of manual inspection option, I have continued to be bitten several times by the Android platform that throws exceptions left and right. The way you used to know about them, since it is very difficult to know all the exceptions thrown by Android framework, is that your Java code didn’t compile until your code addressed exceptions. Now, since Kotlin doesn’t check, I have had multiple production incidents.

I see there has been much continuous discussion in this post. I really couldn’t care for Kotlin to have checked exceptions, but I definitely still hope that the difficulty in developing Android apps gets addressed.

2 Likes

I agree some type of inspection that allows you to find any place throwing exceptions would be very very helpful.
Checked exceptions are a bad idea, but not knowing about reasonably likely exceptions is even worse. Seeing that many libraries are badly documented, not showing all @throws annotations or talking about it in the javadoc, this can be a real problem.

a Try monad is a clean solution (-> see Rust, Haskell, Elm, Purescript, and in some way even go, altough the double-return and if err != nil is not really a try-monad), but I fear that It would be nearly impossible to introduce the use of one as an exception-replacement by now.

What I could imagine happening is

  • allowing Result<T> to be used as a return type, and automatically returning error-values on Throw-statements in these functions, and returning the wrapped value automatically on normal returns, such as to make it possible to use Result<T> as a clean exception-replacement in self-written code. (Something like this is even mentioned in the KEEP for Result<T> so it’s not out of the question)
  • adding another Try monad-like and giving you the option to replace all checked exceptions that could be thrown by stdlib and java code with returning a wrapped try value via some compiler-option (This is unlikely to happen and i doubt that I’d actually like this)

TLDR: please use scrict typing.

to have some sort of manual inspection option

I can advice using sealed classes to create extended version of try monad.

I use this in my code. Just as an example, for network request operation you can draw something like this:

sealed class Response<TResult> {
     data class Success <TResult>(val data: TResult): Response<TResult>()

     sealed class Error<TResult>: Response<TResult>() {
            data class Deserialization<TResult>(val message: String, val first1000CharsFromRequest: String): Error<TResult>()

            data class ServerResponse<TResult>(val message: String, val code: Int): Error<TResult>()

            data class NetworkError<TResult>(val exception: IOException): Error<TResult>()
     }
}

Next you can observe all possible scenarios in your code. This paradigm is used in Rust as primary (for example).

Moreover, please don’t rely on Throwable response from api, because it is just recommendation. It means, that if you compile your code with version1 of library, then version2 of the same library can have different response (e.g. in runtime you can work with a bit different api). It means, that your code can get unexpected exception on next Android versions.

Finally: I really advice you to take a look on sealed-based results. You are able to create really type-safe code and cover the most of scenarios in compile time, not exception-only, but success too.

This is the obvious solution for your own code, but that code is not the main problem (You hopefully know which exceptions you’re throwing, and if not, are hopefully already using sealed-class based result types.)

The big Problem is JVM-code that is out of your control. Android libraries are notoriously using Exceptions to notify you about anything even slightly unnormal, and many JVM-libaries rely on (checked) exceptions for their error paths too. If they are badly documented (which they shouldn’t be, but many still are, and we won’t be able to change that) you have no idea what could be thrown before trying out every possibility.

Even Kotlin libaries abuse exceptions in this way.
I still don’t know if I’m missing some exceptions that ktor's webclient can throw for some situations. I absolutely HATE that I have to do

try {
  val response =client.request<HttpResponse>(url)
  if (response.isOk()) {
    ...
  }
} catch(e: Whatever) {
  // handle error 1
} catch(e: Whatever2 {
  // handle  error 2
}

instead of just having

val response = client.request<HttpResponse>(url)
when {
  response is ValidResponse && response.isOk() -> handleValidResponse(response)
  else -> handleInvalidResponse(response)
}

this is especially anoyying since for my main usecase i actually fully expect there to be many errors that would be represented as exceptions (Unknown Host stuff), and I want to handle these as data too. Having exceptions is anoyying. And yes, i DO define my own wrapper type and function.
But that is a lot of work if needed in many places (-> see android), and it makes the code harder to get in to as someone new needs to first understand all your custom error-representation logic

Actually I don’t know, what exception (and with what text, inner exceptions, etc.) will be thrown. And sometimes I need to parse exception text to determine real case (and, unfortunately, I have to write tests to verify this).

So in real life code can throw CancellationException, because of timeout or because of client close. And I have to determine this (please note, that type is exact the same).

Therefore I have DMZ-like layer to split scenarios to useful for me. For example, code above doesn’t know to differ TCP-like errors and TLS-like ones. All of them are “network-related”. And because of all these facts I advice writing intermediate code between library and your system. So you will be on the safe side in your code, and you will able to cover everything useful in this layer.

We do need some tool in the language which handles recoverable failures. This doesn’t have to be checked exceptions in particular and it doesn’t need to force developers to handle exceptions, but it needs to do the following:

  1. Warn the developer about potential recoverable conditions arising from a function call;
  2. Allow to unwind the stack automatically if the developer opts to not handle them at the call site.

Currently Kotlin has taken away the tool we had and given us nothing to replace it. The only option mentioned was sealed classes, but they are a poor replacement since they can’t help you if you call Java code which relies on exceptions, they are cumbersome to write for each function which can fail, and they don’t have automatic stack unwinding. Documenting thrown exceptions is something that compiler doesn’t help with in any way, it’s easy to omit something, and the documentation often becomes obsolete very quickly.

Checked exceptions don’t mix well with functional code, but Kotlin doesn’t have functional exception handling either. Rust has ? to unwind the stack when encountering Err, and it can transform one type of exceptions into another. Haskell has the do notation. Kotlin has neither, and even if it had, it also had to have some way to map Java calls into that solution, otherwise interoperation becomes difficult to achieve.

large stable systems with several tiers and multiple modules have been built in languages which do not include any form of checked exceptions

And they have also been written in languages without null-safety, does that mean we don’t need it? This fact proves nothing.

The correct point to handle exceptions is a central exception handler that normalizes how to treat them.

No, the correct way to handle exceptions is to handle them at an appropriate layer. If it is in the wrong layer, the error handling code doesn’t have the context it needs to react appropriately. Reacting to exceptions at a level too high is just as difficult as if it is too low.

This is a quote from Eckel if I’m not mistaken, who quoted some C# developer on why C# doesn’t include checked exceptions. The original source is long gone. Which projects were investigated, what the methodology was, and what exact numbers they had we’ll probably never know. We need to rely on verifiable facts which reflect the current experience, not something someone said some time ago in unknown circumstances.

2 Likes

The only thing that Kotlin does is not check that you handled all exceptions that could potentially occur. It supports exceptions, even the ones that are checked for the Java language. It merely treats all exceptions as unchecked. The biggest interop issue with Java is if you throw an exception from Kotlin that Java code would not have been able to throw, and this exception is then not handled properly by the original caller.

Yes, and that takes away the ability to reliably handle recoverable failures while not giving an alternative.

1 Like

I really like this summary of the situation.
Kotlin, the language that had the goal of bringing null-safety and better compiler guarantees to the JVM (see sealed classes, null-safety, even coroutines to some extent), took away a core part of how Java deals with failures: Forcing you to recover from them, or at least handle them. This would be okay if Kotlin somehow replaced it. But it doesn’t.

Checked exceptions are bad, but they are also better than nothing. Not even having compiler generated Throws annotations is… well it’s stupid. The compiler should at least TELL us when we can expect an exception to be thrown. And it should allow us to handle them in a good way.

I love the approach of introducing something like do-notation and then adding a Result-type that functions more like Rust or Haskells types do than the current “pretty much just try-catch, but as a stdlib-function, not a language-feature”. Introduce such a type, add Do-notation (maybe together with KEEP-87, make this a “typeclass”, so we all can use it). Then generate Throw-annotations by the compiler and warn against non-handled exceptions. keep the current runCatching-concept, but let people specify the type(s) of the exceptions that should be caught. This would work well with the proposed Union types (for multiple exceptions).

THEN, and only THEN will kotlin have usable error handling. as it is right now, this is by far the worst weak spot of kotlin, and it’s the only place i could see people saying “I will keep using java” and actually have a POINT.

2 Likes

Eckel quoted it but did not originate it and is not the only one. Here is a link to another source, which is also quoted in Wikipedia, but also references it through J.R. Kiniry. Without a clear source, I simply said it was “oft quoted”

I know he didn’t, that’s exactly what I said in the first sentence further down, I only didn’t find the page behind the link he used. Having read that, it strengthens my argument even more: apparently there never was a proper scientific research, just some internal investigations at Microsoft conducted in 2000 or before that when Java was far from mature.

Basically the only argument in that post relevant to the current discussion is that checked exceptions increase robustness at the expense of productivity when writing code and can be abused. Both points hardly apply to Kotlin since the language does favor robustness like null-safety and is not afraid of giving the programmers tools which can be abused like operator overloading. And if Kotlin emphasizes robustness, it would only make sense to do what increases it and not follow other languages.

Also that post assumes that support of both checked and unchecked exceptions in Java is a flaw, whereas it is a crucial feature as some errors are programmer’s errors, and some are expected and recoverable. It’s definitely not impossible to distinguish between these two cases as the post author claims.

I was primarily responding to the insinuation that I should have attributed it to Eckel.

But since you seem to demand response to the wider argument, I can say that as a Java programmer, that I whole heartedly agree with the quote.

The problem with them is not just lost productivity. You could have made that case in the early days of Java when everything was imperative coding. But when you enter the world of functional programming, where you have higher level functions like map, filter, forEach, etc. that take other functions, checked exceptions are major issue.

We have generics that allow you to parameterize the return value, but nothing like that for parameterizing exceptions. Forcing callers to deal with errors instead of totally ignoring them is a good thing, but the mechanism of checked exceptions is not a good way to do it. The real problem isn’t with forcing callers to deal with exceptions its the way that Java’s checked exceptions do that.

What after all are checked exceptions? In reality they are alternative return values for the function but they do so in a way that is like a GOTO. The best way to force handling of errors is making them part of the return value using something like Kotlin’s Result type or one of the many alternatives see Arrow’s types and some more explanation on the problem with exceptions