* Haskell: pure function and non-pure (IO monads) looks different. * Rust: unsafe functions (or block) requires special markers.
* Haskell: pure function and non-pure (IO monads) looks different. * Rust: unsafe functions (or block) requires special markers.
In Rust, unsafe code can call safe code, and safe code can call unsafe code. Calling unsafe code in safe code requires an explicit unsafe block, but that's fairly normal and not a hack to get around function coloring.
A better example could be Rust async, though unlike JavaScript, you have the option to block the thread on an async function in a sync function.
It is code you need to write, tradeoffs you need to weigh, invariants you need to keep.
And in C#, you can just type `await` and call an async function from a sync function.
Calling unsafe requires an unsafe block from safe functions. That's essentially the same thing as async/await in many languages (Rust does things differently, of course, but that's even worse in my opinion).
Yes, but not in JavaScript.
Which is another problem with the article: it doesn't clearly define what counts as having the "color". The problematic dead-end situation exists in JS, but languages with cross-thread communication can work around it.
fn someFn() -> Result<T, E>
T someFn() throws E
fun someFn(): T | E // Kotlin's proposed error unions
Checked exceptions actually compose a little better when you have a function that can throw multiple types: T someFn() throws E, F, G
This is like a union type of E | F | G. I don't know about Rust, but most languages won't let you do that over generic types like Result<T, E | F | G>.The main problem for Java's checked exceptions is just how boilerplatey they are, especially when you can't handle something. In Java if you need to become "unchecked" or panic you need to:
try {
someFn();
} catch (SomeException ex) {
throw new RuntimeException(ex); // dunno panic
}
Ideally that would just be: someFn()!!!!; // shut up compile panic if this happensIt also provides an error interface so sometimes you don’t need the enum, if all the types return that interface.
i.e. as types you don't know about get introduced the compiler won't stop bad things from happening:
catch (Exception ex) {
switch (ex) {
case SomeException1 se1 -> ..
case SomeException2 se2 -> ..
default -> throw new IllegalStateException(ex); // panic
}
}It's true that if you return an open set of things, you'll have to handle cases you didn't explicitly account for.
Personally for me it’s not that big of a deal. The thing I want the most is having null in the type system.
This is exactly why Java is such a pain to work with.
Somehow, Java developers all decided to stop dealing with error conditions and just crash the stack whenever something weird happens.
The way Java developers seem to work these days has a lot in common with Rust beginners that just `?` or `.unwrap()` every single fallible method. Random crashes ("RuntimeException") are acceptable, so nobody even bothers doing error handling any more.
Even the base SDK doesn't really bother with handling exceptions (i.e. the story with streams + exceptions). It's an excellent language feature tainted by a combination of bad choices twenty years ago and a weird culture shift in error handling.