I agree but even than I cant decide in LevelManager which Errors will be handled in LevelManager itself and which one will be passed to the UIScreen and will be handled there
a simple forcecatch annotation or something like that would be so nice just to tell the compiler here is an error this breaks my complete Architecture of Error Handling, in Java it was so nice you could always decide on which hierarchy you want to fix your Errors and push some of them into the higher hierarchy when needed
You can, but it requires yet another sealed class. I agree it is a bit more tiresome.
But even then I have to use When with expression all the time to cover up all possibilities I think Kotlin need a nicer way for Error Handling or at least some way to force the Method Caller to handle with possible Errors because dealing with Errors is really necessary in every Language
The union return type (with the sealed classes) approach to error handling is a functional approach. You donât have when
statements all over the place, instead you use a map/apply function that will only be invoked by the success path. Only when you want the end result do you need to handle the option of an error. But in this case your errors are predictable and you donât have to handle partially changed state etc. that you need for exception safety. You also donât have to handle âforeignâ exception types (or exception wrapping) which is one of the major issues with checked exceptions.
That is not the general finding of those that have studied the matter. The oft quoted counter to that is:
Examination of small programs leads to the conclusion that requiring exception specifications could both enhance developer productivity and enhance code quality, but experience with large software projects suggests a different result â decreased productivity and little or no increase in code quality.
Ask yourself, why other languages have not adopted checked exceptions.
To have the Option is always a good thing. I still believe with many other People that the ability to force checked Exceptions would gain huge benefits.
I saw this multiple time in my career and many bugs and problems could be saved on that way.
A optional force operator would be amazing.
Ask yourself, why other languages have not adopted checked exceptions.
Kotlin is not other languages. Kotlin claims to have seamless interop with Java APIs. Many Java APIs are written with the expectation that the consumer of those libraries will get a compiler error if they do not catch the checked exceptions. This makes those libraries highly error prone when calling from Kotlin.
At the very least, it would be great to support checked exceptions at the Java-interop level as a compiler error, with an associated @Suppress annotation for disabling the compile error per line/class/etc.
If unchecked exceptions are the only variety of exceptions in Kotlin then they should never be used to handle non-fatal sitations. Now proof by counter example. Imagine I have written an HTTP client in kotlin that offers a synchronous âgetâ method that throws an exception upon network error. Correctly written code in kotlin should look like so:
try {
client.httpGet(..)
} catch (e: NetworkException) {
// show error to user
}
The Kotlin code must be written exactly like Java to be correct but the Kotlin compiler is not helpful. If this were Java and the user forgot to put a try/catch the compiler reminds them. In Kotlin the compiler fails to remind the programmer if they forget the try/catch then their program crashes on network failures.
If one of Kotlinâs goals is to get programmers to write safer code that is less likely to crash then the authors of Kotlin took one step forward by forcing null to be handled safely everywhere so programs almost never crash with NullPointerException and then one step backwards by allowing any other kind of exception to crash your program without help from the compiler.
Observe that Ktor does use exceptions when invoking get: https://api.ktor.io/1.2.6/io.ktor.client.request/get.html
The only way to know it uses exceptions is to read the documentation and we still donât know which exception types will it throw? If I catch plain Exception then I catch fatal usage errors such as IllegalArgumentException, ArrayIndexOutOfBoundsException, etc which have nothing to do with network.
Ideally Kotlin would disallow catching exceptions completely (or possibly treat them like Unix signals and just jump to a single global exception catcher), that would force exceptions to only be used in fatal situations. Then for Java interop Kotlin would require any java library that uses exceptions for normal operation to be wrapped in a java wrapper that converts non-fatal exceptions into some other kind of error handling model.
I should be able to write, for example, a fun wordCount(src: CharSequence): Int
I should be able to write a file-backed implementation of CharSequence
, say FileCharSequence
I should be able to pass a FileCharSequence
to wordCount
to count words.
Using a file, though, implies the possibility of IO errors. That should certainly not be handled with a checked exception, because the CharSequence
interface should not have special handling for anticipated IO-backed implementations. There are too many kinds of implementations for it to anticipate, anyway. (Look how many IOException
subclasses have nothing at all to do with IO!)
But, an exception is absolutely required, because if FileCharSequence
encounters an error, then wordCount
cannot proceed.
However, this exception certainly isnât fatal, because the code that up-casts FileCharSequence
to CharSequence
knows about the potential IO errors, even though wordCount
does not. That code can catch the exception and handle it appropriately.
It would be nice if some language included the ability to declare these exceptions and ensure that they are caught (e.g., class FileCharSequence : CharSequence except IOException {...}
), but Javaâs way is a broken anti-pattern that is best ignored.
In my opinion, the best way would be some way to tunnel checked exceptions through code that doesnât know about these exceptions, especially through lambda expressions. For example a function that takes a lambda expression should just be able to say, âwhen you execute me, Iâll possibly throw the same exceptions the lambda expression throwsâ, and then the lambda expression throws a checked exception that is automatically detected at the call site of the method that takes the lambda. Example:
fun readStringFromFile() : String throws IOException = TODO()
fun doSomething() throws IOException {
with(something) {
readStringFromFile();
}
}
In this case Kotlin would allow you to catch the exception either as usual where you call the method, or outside of the with
call, because with
would silently pass through the exception or you could just pass the exception to the caller of the doSomething
method.
Technically you can achieve this using generics. Hereâs Java example:
interface CharSequenceX<E extends Exception> {
int next() throws E;
}
class StringCharSequence implements CharSequenceX<RuntimeException> {
@Override
public int next() {
return 1; // just an example
}
}
class ReaderCharSequence implements CharSequenceX<IOException> {
@Override
public int next() throws IOException {
throw new IOException("Disk error");
}
}
static int stringWordCount() {
return wordCount(new StringCharSequence()); // does not require throws
}
static int readerWordCount() throws IOException {
return wordCount(new ReaderCharSequence()); // requires exception handling or throwing
}
static <E extends Exception> int wordCount(CharSequenceX<E> seq) throws E {
return seq.next(); // just an example
}
but itâs more of a gimmick rather than proper solution:
- Itâs not possible to declare multiple exception types, e.g. IOException, SQLException without declaring multiple generic type parameters.
- Itâs necessary to write this boilerplate for every higher order function.
But itâs just an idea which could be extended and provide proper solution for this particular problem.
PS Iâm not arguing that checked exceptions are a good idea. I donât think they are. Every language ditched them, including even Java in practice. But this particular problem could be solved.
I would argue that the âproperâ way to implement an HTTP get method in Kotlin is for it to return a sealed class, with one subclass for a successful result and one for errors (I might even split the error class into one for connection errors and one for HTTP errors). The we get something like:
val result = client.httpGet(...)
when (result) {
is Success -> // handle success
is ConnectionError -> // handle connection error
is HttpError -> // handle HTTP error
}
What you are asking for is to use a monad. That is probably the cleanest way to handle errors, and even C++ seems to be moving that way. The Arrow library for example has the Try
type to do this.
The standard library actually has the Result
type instead (although Iâm not sure if it is stable - it was introduced for coroutines, and it is an inline class)
If you look at the Error-handling style and exceptions section of the Result KEEP, they suggest using a sealed class instead of Result for something like this.
Thanks, your argument that returning a sealed class is the proper way appears to be backed-up by the example of FindUserResult
at KEEP/result.md at master · Kotlin/KEEP · GitHub
That being said thereâs nothing enforcing that pattern and the authors of the Kotlin language left checked exceptions in Kotlin and freely let them crash applications. If exceptions arenât supposed to be used for basic error handling in Kotlin then there should be something to prevent their use in that way or at very least strong warnings to developers at compile time. Imagine if IKEA shipped their furniture with an assembly tool that simply damages your furniture and then in some tiny print somewhere says you shouldnât use that tool. Expect to see a lot of damaged furniture.
I can freely write the following code and neither the compiler nor the IDE warns me that this will likely crash my app by an exception if the file doesnât exist:
fun readByteFromFile(s : String): Int {
val x = FileInputStream(s)
return x.read()
}
The question isnât what is the right way to write this code, the question is how can the authors of Kotlin prevent developers from writing the wrong code. A lot of time and effort was put in to preventing NPEs in Kotlin so clearly safeguarding developers is a goal of the language.
I can freely write the following code and neither the compiler nor the IDE warns me that this will likely crash my app by an exception if the file doesnât exist:
This function can raise error because of a at least the following causes:
- No enough operating memory
- Thread/Coroutine are terminated
- Input string is not integer
- IO: file is missing, device was ejected, handle was closed externally, network timeout (for network FS), access denied, etc.
- Method is not implemented/supported
- Stack Overflow
Question: what details (and how) do you want to receive at compile time? And why exact these? And how can we support new errors in future?
You should really expect that anything can throw an exception, unless you have a very good reason to think otherwise, so consider yourself pre-warned, and be thankful that the language has default error handling that returns the errors up to the caller.
I guess my argument is still not really clear. If Kotlin didnât interop with Java in the way it does today I wouldnât be making any claims. My argument is that many Java APIs use checked exceptions to handle situations that are expected and should not be considered fatal during the course of normal program operation in situations API designer deems recoverable. Programmers working with these particular APIs in Java absolutely must try-catch these particular exceptions because that is the only valid way to interact with these particular APIs. Because Kotlin allows for and is designed for Java interop these same rules apply to Kotlin.
We are accustomed to file based IO always succeeding but there are instances where it might fail and you probably donât want your program to simply crash. Lets say I inserted a memory stick into my computer with an essay on it and I was editing that essay in my text editor, I finished my edits after an hour and hit save but realized I had accidentally remove the memory stick earlier and now my program crashed and my work was lost. This was a somewhat rare but recoverable situation that the Java API designers foresaw and intended the developer to catch and handle which is why they throw the checked IOException when reading and writing IO streams. To use the Java stream API correctly the developer should be forced to consider the checked exception.
I will give another example, take the following code:
val signature = Signature.getInstance("SHA256withRSA")
signature.initVerify(publicKey)
signature.update(message)
return signature.verify(signature)
It looks completely reasonable, the verify method returns a boolean telling the caller if the signature is valid or not. But what the developer forgets when writing this code in Kotlin is the API designer added a third possibility, the verify method will throw SignatureException (a checked exception) if there is anything malformed about the signature. For example if the signature is supposed to be 256 bytes in length but you pass in 255 bytes then it throws SignatureException instead of false. This is highly possible if infact there is any kind of data corruption in message transmission (very normal in the course of operation) or an attacker is trying break the system. The point here is that the API designer intends the developer to use checked exceptions during the normal course of program operation. The API developer does not intend for your program to crash if the signature format is invalid, and the developer almost certainly doesnât want their program to crash either. Kotlin unfortunately makes that possibility quite likely.
One could make the argument that the use of SignatureException makes for a poor API and messy code and I wouldnât disagree but this is simply what the API designers chose and using this API correctly requires the developer to go along with it despite stylistic opposition. Again Iâm not saying checked exceptions are good or bad. My point is merely that they are part of the existing API design and that Kotlin is supposed to allow developers to work with these API. The philosophy of Kotlin is also distinct from other languages such as C and C++ in that Kotlin aims to be a safer language and prevent knowable coding mistakes. This is why the designers of the Kotlin language went through such pains to prevent NPE. Thus we arrive at the mismatch between the stated goals of Kotlin and the actual implementation of the language with regards to the handling of checked exceptions.
OutOfMemoryError and StackOverflow are not really easily recoverable and are not part of any typical API interface, also they arenât checked. I am taking about only checked exceptions that are declared by APIs and are clearly recoverable by the user of the API.
Again none of this would be an issue if Kotlin didnât allow interop with Java APIs in the way that it does today.
In my experience, though, a lot of exceptions end up being propagated to some top-level handler (web app controller, GUI event loop, etc.) which logs and/or displays any error, then carries on - so work is not lost. If lower levels make sure app state remains consistent when any exceptions happen, all is well. They probably need to do that anyway, because there are plenty of runtime exceptions which can happen in reasonable situations - or are thrown by unreasonable but useful/mandated libraries in use. So, I donât think checked exceptions really buy you data safety, and may give a false sense of security. Also, in the world of interacting processes, just handling your own internal exceptions may not be enough to keep data safe anyway!