[1] See e.g. https://literatejava.com/exceptions/checked-exceptions-javas..., but also Java 8+ API's moving away from them.
[1] See e.g. https://literatejava.com/exceptions/checked-exceptions-javas..., but also Java 8+ API's moving away from them.
In particular:
> Even with the introduction of typed throws into Swift, the existing (untyped) throws remains the better default error-handling mechanism for most Swift code. The section "When to use typed throws" describes the circumstances in which typed throws should be used.
https://github.com/swiftlang/swift-evolution/blob/main/propo...
To illustrate, Swift has a nice `rethrows` feature that helps with function composition.
If your function takes a function parameter that can throw, it can use 'rethrows' to say "I only throw if this parameter does". Then when passed a function that doesn't throw, your function need not be invoked with `try`.
This plays nicely with generics over throwing type, since the bounds propagate back to the function type. If the function parameter only throws a given type, that's what the function will throw.
Also helpful for reducing error boilerplate, `try` has the convenience form `try?` which means "just swallow the exception and return nil", and applies to a whole chain: `let f = try? this() ?? that() ?? "skip"` means f will be the result of either (throwing) function or a literal otherwise.
So something like
`func foo() throws -> Result`
Is actually
`func foo() -> Result | Error`
The compiler also forces you to handle any returned errors using `try`. So to call our example `foo`, you'd do:
`let result = try foo()`
You must either handle any throws error or include this call in an enclosing throwing function.
Implementation detail.
The two features are equivalent.
But the main difference is it's not hidden by the language; you have to 'try' any method that can return an error. Straight-line control flow in an imperative language is a good thing IMO. …but too bad about those defer statements.
I will repeat, whether exceptions are implemented as "nonlocal returns" (like setjmp/longjmp with stack unwinding) or as syntax sugar with sum return types, is completely irrelevant; an implementation detail. The generated machine code is different, but the behavior, the user experience is exactly the same.
>Otherwise you need all that "exception safe code" stuff.
In both cases, you need to write "exception safe code". Example of unsafe code in Java (a language that implements exceptions as non-local returns):
void bar() throws Exception { ... }
void foo() throws Exception {
mutex.lock();
bar();
mutex.unlock();
}
Example of unsafe code in Swift (a language that transforms errors into sum return types): func bar() throws { ... }
func foo() throws {
mutex.lock()
try bar()
mutex.unlock()
}
>But the main difference is it's not hidden by the language; you have to 'try' any method that can return an error. Straight-line control flow in an imperative language is a good thing IMO. …but too bad about those defer statements.Whether the language makes you prefix throwing calls with "try" is completely orthogonal to how they're implemented (nonlocal return vs sum return type). It's just a matter of syntax.
Or they catch and wrap everything in some CustomSysException type so they only have to list one type and it’s not Exception, but then that’s really the same thing isn’t it?
I think it’s kind of a combination of not dealing with things and throwing them up the stack combined with too many exception types and maybe using exceptions when a result type would just be easier.
Compare to ATDs, where any generic function taking a T can also be used with Result<T> exactly the same. (Not a perfect comparison, but there are lots of other scenarios where ATDs just compose better)
And in the end many errors can't be handled automatically, the best you can do is just show them to the user.
* - according to a couple of loudmouths on the internet
Also JVM guest languages like Kotlin and Scala treat all exceptions like runtime.
“throws Exception” on your method.
And then in main:
try {
// whatever
} catch (Exception e) {
System.out.println("Oops!");
}Forced checks on sum types with automatic unwinding are another way of doing checked exceptions, that apparently those haters love so much.