Java tried to fix that, but doesn't let you generalize over exceptions, "my method throws FooException and whatever exceptions method Bar on the instance of Glorbable you're passing me throws".
Nulls have the same problem. Every time you get an instance of Foo you don't know if it's a null or an object. C# is trying to migrate to explicit nullability, but it's hard to convert an established ecosystem with idiosyncratic nullability rules.
Whether a method throws an exception or not doesn't matter as much as people think it does. You can rarely recover from an exception in the immediate parent calling method anyway. More than likely the error will occur in a stack 20 layers deep and recovery is to restart the entire operation from the start.
Nulls are useful but I totally agree that they should be opt-in in the type system.
Nope, Java picked the idea from CLU, Modual-3 and C++, in what seemed the way forward to declare exceptions.
It does provide both checked and unckecked exceptions, unfortunely it suffers from bad learnings and quick hacks to shut the compiler instead of thinking it through when designing class libraries (yes even the std suffers from this).
Dart has just done that, though admitedly, it did not have an ecosystem as large as C#'s.
Except nothing else in the language forces you to handle the optional case, so you end up with either null checks everywhere, or your code blows up with NullPointerExceptions, or sometimes both.
Ever been deep in a Java call stack, and wondered if you've already checked for null in all cases? In a language with (only) Option types, you can simply declare that variable to be T, and if it's actually Option<T> (i.e. you haven't checked for absence along some path) you'll get an error. A good compiler will even tell you where in the code the None is sneaking in, so you can fix it there, or pattern-match on Some and None where the error is, depending on which is correct for your logic.
I'm ignoring the exceptions question, because the post doesn't even consider a condition/restart system, which means it's not-even-wrong.
An Option type means you must specify Some (pointer) or could-be-None (null, NULL pointer) and the compiler will enforce the distinction. Generally this comes with exhaustive pattern matching, so you can turn Some<T> into <T>, and handle None by raising an error or assigning a default or blowing demons out your nose.
None and null are the same thing, the name isn't the important part. Zig, for example, has optional types as ?*Foo, and calls the null pointer "null".
Additionally you can no longer say Thing is Thing, it's always Thing or null - which results in special handling
if (foo === null){} everywhere and when you don't boom NullPointerException.
Exceptions cause problems in other ways (especially when they are allowed to escape up the stack), business logic shouldn't be handling FileNotFoundException that was thrown 6 layers away.
So instead all 6 layers should have to know about it in addition to business logic? In most cases you can't really ignore the fact that a file didn't exist in your business logic.
No, that's the job of whoever requests a file to be read. But what they may do in reaction to this, or discovering the file is empty, or has malformed data, is to throw a DataUnavailable exception, a subtype of RequestError. This is something appropriate places of the business logic up the stack may be interested in. Maybe some code few layers up the call stack can recover from DataUnavailable by attempting to pull from an alternate data source. Maybe not. Maybe that too will fail. Further up still, something else will intercept RequestError and report a problem to the user.
But, unlike with Result types, only those three places in this piece of business logic have to ever care about some file not being read. Everything else can just assume the operation succeeded.
Exception types follow the same rules as "regular" user-defined types: they're relevant in a subsection of a program, and shouldn't propagate beyond that subsection.
It's easier to be adaptive with error handling with exceptions, and who's going to (correctly) test for all possible errors on every call?
I would have the same question.
Sounds nice.