4 ms·
The problem with exceptions is that they can come from any line of code and cause a "return". Rust's question mark solves the issue because it marks which lines
by nulld3v 3y ago
The problem with exceptions is that they can come from any line of code and cause a "return". Rust's question mark solves the issue because it marks which lines of code can cause a "return".
Therefore, you can always see see the control flow of a function.
Moreover, you can go even further if you really really really want a single control flow. You can write a clippy lint to disallow early returns (ban "return" keyword) and question marks. That said, I really don't think this is a good idea. The "return" keyword exists for a good reason.
- preseinger 3y ago? enables chaining, chaining subverts comprehensibility in exactly the ways i'm describing
- consilient 3y agoAll languages have chaining: that's what `;` is for. Chaining together transitions in an imperative state machine isn't simpler than chaining together `Result`s, you're just used to it.
- preseinger 3y agochaining means combining a sequence of expressions that each take the same input as they give as output ; doesn't do this, afaict when you're writing imperative code it's important that control flow (return) is explicitly visible
- consilient 3y ago> chaining means combining a sequence of expressions that each take the same input as they give as output > ; doesn't do this, afaict It's chaining together transitions in the state machine that is your program. > when you're writing imperative code it's important that control flow (return) is explicitly visible Control flow is visible with `?` or other do-notation variants. If I want to error out in a `Result` context, I explicitly return `Err(bad stuff)`. And if I don't, I explicitly return `Ok(return value)` instead. If I want to introduce a new asynchronous value in js, I explicitly call `new Promise`. And so on. What's not visible in a do-block is the implementation of control flow. Which is fine, because this isn't the code that controls it - `Ok(Err(x))` is reduced in exactly the same way no matter what `x` is or where it came from. Traditional imperative code is the same way: the runtime system always works the same way, no matter which statements you ask it to execute. If you do choose to expose the underlying mechanisms of your control flow everywhere, you get continuation-passing style, which is useful in small doses but more or less impossible to reason about at scale.
- preseinger 3y agobad: first()?.second()?.third()? good: a = first() if a failed, handle that error b = second(a) if b failed, handle that error c = third(b) if c failed, handle that error yield c