| I think this comment shows a very fundamental lack of understanding how different languages handle errors. In Java (and Kotlin) -- ignoring the topic of exceptions as an alternative -- errors like "missing value" are communicated with null. So in Java you first have an error handling problem, you deal with it by returning null ... now you have a null handling problem! Kotlin tries to put some band-aid around nulls by making them more typed than in Java. In Scala, errors are handled with bog-standard library types like Option, Either, Try, Validation, not some special language built-in.
These types are not facilities to handle null (shown by the fact that all these types happily accept null as a valid value), they are facilities to handle errors. They handle errors better than Java or Kotlin, because these types allow developers to choose the appropriate type for a specific error case and retain the structure of a computation.
To expand on the second point: Nullable types cannot be nested, Scala's types can and regularly are. This allows Scala to compose operations while carrying errors up to the point where they can be handled easily. If you have an operation returning an Option[T], and want to run an option on the value returning an Either[S, T] then simply looking at the resulting value will tell you if things succeeded or where exactly things went wrong. None --> First operation did not result in a value
Some(Left(fail)) --> Second operation failed with cause "fail"
Some(Right(value)) --> All operations succeeded with result "value"
In Java and Kotlin all you would get would be a bare null, with a probability of people giving up on (typed) null completely and just throwing an (unchecked) exception instead.TL;DR: Kotlin tries to put some band-aid around Java's broken approach of handling errors with null, Scala deals with errors correctly in the first place. |
Now, there's an alternative school of thought that's gaining currency in languages like Rust, Go, and apparently Scala, and is how we used to handle them in C. That's to return special out-of-band values as part of the result type. The Either type in these languages (realistically, you wouldn't use an Option for any high-level API) in these languages makes this really easy, and sum types in general make pattern-matching on the specific error code & passing back diagnostic information a lot easier than it was in C.
I don't really want to get into a debate about error-by-value vs. error-by-exception, because there are good arguments on both sides of the debate and it's far from a settled question. Personally, if I were starting a new language today, I'd use return values for "expected" error conditions and some sort of panic/recover mechanism to indicate that the programmer forgot to handle a case.
But there's a really strong reason why Kotlin uses exceptions: interoperability with Java code. It's the same reason that Google bans exceptions in their C++ code: you can't really change the error handling mechanism of a large body of existing code, because the existing code's API all assumes certain language conventions, and if you break those conventions, you might as well rebuild the ecosystem from scratch.
So sure, if I were starting a language & ecosystem from the ground up, I'd probably use return values & pattern-matching. But I use Kotlin specifically because I want a "better Java", and need to integrate with Java libraries that may return null and will throw exceptions. If I didn't have those constraints, I'd use Rust instead.