The Error Model in Midori
joeduffyblog.com
joeduffyblog.com
In particular, I enjoy that Rust just decided to change their "try" syntax for calling a function that could possibly fail to a simpler "?" suffix, and now we get Duffy praising how well "try" worked out for them. :P
- Midori (and Swift) uses effects instead of monadic types (probably good, since we dodge the interactions with generics described at the end, but the "throws" syntax is so sweet);
- Rust uses a return codes implementation instead of an unwinding implementation (for simplicity's sake; some Rust users really don't like unwinding, for legitimate reasons in many cases, but Midori's arguments in favor of unwinding are compelling);
- Rust lets you catch panics at thread boundaries (one of the most controversial decisions in Rust; I think it was the right one, but I definitely acknowledge that it's a big tradeoff).
With their Result type, Midori is really hammering home that Result/exception isomorphism. Is the interaction with generics really any worse than what we would have to write if we wanted a map function that optionally returns early if the closure returns Err?
fwiw, I always thought the biggest difference between Rust's scheme and regular exceptions were that in Rust you never unwind until a catch block since a `?` on its own at most returns from the current function, and now Midori goes and requires `try` on ever call of a possibly-throwing function and it ends up working out just the same...
(Unstable) Rust lets you catch panics anywhere. You have to be able to do that for sane FFI - as you know (but perhaps other readers don't), a panic that unwinds across the Rust/C FFI boundary causes undefined behavior, so AIUI extern functions in Rust should all look like:
pub extern "C" fn foo_the_quuxes(x: i32) -> i32 {
std::catch_panic(|| do_stuff_in_rust_land_that_may_panic(x))
.unwrap_or(-1)
}
(Actually, checking several libraries off the top of my head that expose C APIs, most don't do this -- probably because std::catch_panic is relatively new.)Let's hope developers don't abuse std::catch_panic for regular Rust code. Maybe a lint that warns about uses outside of an extern fn is worth having.
Note that you're replying to the original creator of Rust, he's most likely aware of that discussion.
It's still worth noting this point though, for the benefit of other readers.
> Let's hope developers don't abuse std::catch_panic for
> regular Rust code.
This has been taken into account on multiple occasions, and the developers have introduced mechanisms to prevent people from treating `recover` as a general error-handling mechanism: https://github.com/rust-lang/rfcs/pull/1323The Rust community did no such thing. The status of the RFC is currently that they will implement '?' as a gated feature (which is only available if you're on the nightly toolchain) and see if it is useful. If it's found to obfuscate the code like many people have suggested, then they will take it back out.
"I expect more of the world to adopt this philosophy as the shift to more distributed computing happens. In a cluster of microservices, for example, the failure of a single container is often handled seamlessly by the enclosing cluster management software (Kubernetes, Amazon EC2 Container Service, Docker Swarm, etc). As a result, what I describe in this post is possibly helpful for writing more reliable Java, Node.js/JavaScript, Python, and even Ruby services. The unfortunate news is you’re likely going to be fighting your languages to get there. A lot of code in your process is going to work real damn hard to keep limping along when something goes awry."
The author hammers home how important abandonment is in a light threaded system without any mention of all the work that Erlang has been doing for years in that space. Given how much reference there is to Rust, Go and a host of other languages I would at least expect Erlang to be mentioned.
Midori: 31
Go: 27
C++: 27
C: 18
Rust: 10
Swift: 03
D: 02
Scala: 02
None for Erlang! Could it be said that Joe Duffy is focussing on statically typed languages or systems programming languages? Interesting that Go is most referenced, not C++ :)This is true in the context of Midori but not in general. In a dynamic environment, you recompile your buggy function and continue working.
I would say that in any case, statistical correctness is all you can have. It's a bit of a nitpick, though, as it certainly doesn't mean that it can't be some orders of magnitude more likely.
(Which point the author seems to make, himself, later in the essay.)
Not a Java programmer, but isn't that what RuntimeException is for? Yeah, Java uses exceptions for both errors and recoverable errors, but it can use different exceptions.
So, they are a little different, but the tooling for them is pretty much the same, greatly diminishing the effect.
The article was interesting, as are all Joe's articles on Midori. Nonetheless the impression I got by the end of it is that they ended up in virtually the same place as Java, with a few minor tweaks that could be easily implemented on top of the existing Java/JVM toolchain.
They decided that the errors which in Java descend from Error should not be catchable and should abort the process. That's easy to do - just use an agent, classloader or other pre-verification step to verify that no class contains a try {} catch (Error e) {} type construct and then change the default exception handler to call System.exit() if such an exception propagates up to the top of a thread. Java scopes the failure domain to the thread instead of the process, because it runs in places with expensive processes. But on Midori your "process" is a bit closer to a thread anyway.
They ended up with Java style checked exceptions, with almost identical syntax, albeit a nice extension to allow generic propagation of errors through functions like map. It'd be nice indeed to have that everywhere.
The concept of a "keeper" is interesting, though I wonder how often it'd be useful.
I find "abort on error" as an un-overridable default to be too aggressive. I would have once agreed with this, but I'm not sure I agree with his dichotomy of "rapid app development people" and "must be reliable people". All devs want their software to be reliable. It's easy to implement abort-on-error in a language with classical exceptions: just forbid catching such errors anywhere with a lint or static analysis check, and then call System.exit().
But my experience testing out Kotlin over the past 12 months has been that the IDE plugin has been very buggy and throws exceptions frequently (it's got a lot better but is still not at the gold standard of the rest of the IDE yet) ... yet IntelliJ routinely recovers from these errors almost invisibly. I've yet to see any obvious corruption or problems caused by these exceptions. It seems like the IDE, although it's not exactly a nuclear power plant, is written in a very exception safe style and more often than not it can just catch an exception from inside a plugin and keep going, even though those exceptions invariably represent programming errors. It'd be a shame if the entire IDE restarted every time that happened. I probably wouldn't use Kotlin at all, if that was the case.
So I am tempted to say that actually, despite the theoretical problems, it's not really so hard for people to write apps that can recover from programmer errors in many cases and it's often useful to do so. In particular, it means you get a reportable stack trace, and crash reports that contain stack traces are infinitely more useful than "Segmentation fault".
I'd like to see some of Duffy's ideas make it into mainstream development. Kotlin has an almost identical approach to nullability as what he describes, with the same syntax and smart casting ability, except it uses !! instead of nonnull(). So that's a good start. It does not compile down to object wrappers, it does use real nulls so you cannot have a Foo?? type. It uses unchecked exceptions, with optional support for declaring them (for Java interop). There is a library that adds a Result<> type with helpers for auto-catching exceptions and converting them into return codes. IDE support for highlighting call sites that could throw exceptions would be great. OK, it's not syntax, but that's OK, I'll take the tradeoff of it being a bit harder to review code in non-IDE tools vs having to type "try X" everywhere.
Basically all it needs is a simple compiler flag that says "Treat catching Throwable/Error as an error" and then a default exception handler that calls System.exit() and you've got something close to Midori's approach.
[1] http://joeduffyblog.com/2015/11/03/blogging-about-midori/
They always use Macbook-like laptops for the images, I guess, because why would you not use the most beautiful or iconic laptop your software can run on? ElementaryOS works great on macbooks.
I don't understand how this type of name overlap happens. It's got to be of the, "Yeah, I know this obscure name is already used for an open source project and I just don't care" variety.
Edit: 2003 according to above comment. I got my year from "The project included [...] looking more like the Microsoft of today [...] than the Microsoft of 8 years ago when the project began" - dated 2015.
Curious how you jump to this conclusion. Not to burst your bubble, but the overwhelming majority of programmers even have likely never heard of the Midori browser. Additionally, the Midori browser did not invent this word, it is a Japanese word...and also the name of a band, the name of an olympic figure skater, the name of a liquor, all predating the browser.
Why on earth would people working on an internal Microsoft project go searching for obscure open source projects that might be using the same name before agreeing to move forward with the name?
Luckily nothing stops me from closing the article again and move on :)
You don't need to worry about name collisions/trademark infringements etc,and the otherwise interminable legal/bureaucratic/marketing/administrate concerns because it is not intended for external use.