Remix.run Logo
pwdisswordfishz 7 months ago

In most languages with exceptions:

• they may propagate automatically from any point in code, potentially breaking atomicity invariants and preventing forward progress, and have to be caught to be transformed or wrapped – Result requires an explicit operator for propagation and enables restoring invariants and transforming the error before it is propagated.

• they are an implicit side-channel treated in the type system like an afterthought and at best opt-out (e.g. "noexcept") – Result is opt-in, visible in the return type, and a regular type like any other, so improvements to type system machinery apply to Result automatically.

• try…catch is a non-expression statement, which means errors often cannot be pinpointed to a particular sub-expression – Result is a value like any other, and can be manipulated by match expressions in the exact place you obtain it.

Sure, if you syntactically transform code in an exception-based language into Rust you won’t see a difference – but the point is to avoid structuring the code that way in the first place.

quotemstr 7 months ago | parent [-]

> they may propagate automatically from any point in code, potentially breaking atomicity invariants and preventing forward progress

A failure can propagate in the same circumstances in a Rust program. First, Rust has panics, which are exceptions. Second, if any function you call returns Result, and if propagate any error to your caller with ?, you have the same from-anywhere control flow you're complaining about above.

Programmers who can't maintain invariants in exceptional code can't maintain them at all.

> try…catch is a non-expression statement,

That's a language design choice. Some languages, like Common Lisp, have a catch that's also an expression. So what?

> they are an implicit side-channel treated in the type system like an afterthought an

Non-specific criticism. If your complaint is that exceptions don't appear in function signatures, you can design a language in which they do. The mechanism is called "checked exceptions"

Amazing to me that the same people will laud Result because it lifts errors into signatures in Rust but hate checked exceptions because they lift errors into signatures in Java.

Besides, in the real world, junior Rust programmers (and some senior ones who should be ashamed of themselves) just .unwrap().unwrap().unwrap(). I can abort on error in C too, LOL.

Ygg2 7 months ago | parent | next [-]

> A failure can propagate in the same circumstances in a Rust program.

What do you consider failure? A result or a panic? Those are worlds apart.

> First, Rust has panics, which are exceptions.

That's not how exceptions work in Java. Exceptions are meant to be caught (albeit rarely). Panics are meant to crash your program. They represent a violation of invariants that uphold the Safety checks.

Only in extreme cases (iirc Linux kernel maintainers) was there a push to be able to either "catch panics" or offer a fallible version of many Rust operations.

The Rust version of Java Exceptions are Results. And they are "checked" by default. That said, Exceptions in Java are huge, require gathering info and slow as molases compared to returning a value (granted you can do some smelly things like raising static exceptions)[1].

[1] https://shipilev.net/blog/2014/exceptional-performance/

> Non-specific criticism. If your complaint is that exceptions don't appear in function signatures, you can design a language in which they do. The mechanism is called "checked exceptions"

And everyone hates checked exceptions. Because rather than having a nice pipe that encapsulates errors like Result, you list every minute Exception possible, which, if we followed the mantra of "every exception is a checked exception" would make, for example, Java signatures into an epic rivaling Iliad.

> Besides, in the real world, junior Rust programmers (and some senior ones who should be ashamed of themselves) just .unwrap().unwrap().unwrap().

If this is a game who can write worse code, you'll never find Java wanting. As a senior Java dev, I've seen things... Drinking helps, but the horrors remain.

pwdisswordfishz 7 months ago | parent | prev [-]

> Rust has panics, which are exceptions.

Panics are fatal errors that the program is not expected to recover from, and it might as well terminate on the spot – as if you cut off power, which you have to be ready for no matter what. In fact, abort-on-sight-without-unwinding is one of the ways they can be configured to work. It’s the case where an operation fails, but the program attempts to continue running that is the most fragile. This is where you’re likely to enter an invalid state.

> you have the same from-anywhere control flow you're complaining about above

Not from literally anywhere – from the places where the error-propagation operator is used. I will never have myself thinking “oh wait, F could have thrown before G is invoked and break my invariants!” months after writing that code. C++ is chock-full of such problems: https://www.youtube.com/watch?v=b9ZYM0d6htg

> Some languages, like Common Lisp, have a catch that's also an expression. So what?

So more power to them! That is actually a major improvement compared to your typical dynamic ALGOL, where you find yourself stuck in situations like:

    def foo():
        try:
            return d1[k1] + d2[k2]
        except KeyError as e:
            """ now what? which lookup failed? """
and have to resort to clumsy workarounds.

> Amazing to me that the same people will laud Result because it lifts errors into signatures in Rust but hate checked exceptions because they lift errors into signatures in Java.

I don’t think you can write a generic over a checked exception list in Java, which means higher-order functions cannot deal with them. Modern implementations just don’t bother. Meanwhile, when errors are values, improvements to the type system in the happy path will apply to the error path as well, because they don’t use distinct language facilities that have to be separately considered and specified.