Live data from Hacker News

On Error Handling in Rust

lucumr.pocoo.org

51–60 of 82 posts

Re: On Error Handling in Rust

#51
post #37
post #31

In some ways it's heartening to see Rust working everything out for itself - but it's also painful to watch the language stumble on the same problems that we've already solved. You're going to hit the same problem again with async, with transactions, and with resource management; indeed some of the stuff I've already seen about borrowing and the like seems achingly close to the same pattern. Introducing new sigils li…

I have the same concern, but I'm not sure how efficiently you could get monads to compile. It would be possible (but tricky) to come up with a macro for haskell's do syntax that would translate monad!( sock to TcpStream::connect(host, port).map(|sock| { let parser = Parser::new(&mut sock as &mut Reader); parser.parse_value() }) but could you get that to compile to the same machine code as this? match TcpStream::conne…

The only difference between the two is inlining the map method, no? This may be the "sufficiently smart compiler" fallacy, but that really doesn't seem very hard.

Certainly I've been very impressed with the performance I've seen from Haskell (and Scala) on real-world problems, though I appreciate that's not quite the same thing, and a more realtime-suitable language that had the same level of expressiveness would definitely be a Good Thing.

Re: On Error Handling in Rust

#52
post #11

I hope we get some sugar here, the ? operator is a great idea. I like the trait implementation as well. I think Rust needs multiple dispatch though - a given type needs to support conversions of IOError to different custom results right? So you can use a struct in different library functions that may return a different custom error. Or maybe I am missing something here; but I do believe multiple dispatch is planned f…

Doesn't multiple dispatch require run-time type analysis? If so wouldn't that go against Rust's philosophy of zero-cost abstractions?

Rust's upcoming support for multidispatch traits is all strictly static.

Re: On Error Handling in Rust

#53
post #48

Earlier quoted context omitted.

Funnily enough, we actually used to have both Either and Result, until one day we went through and realized that no code in existence was using Either and decided to go all-in on Result instead.

Someday, we should have a "History of Rust" thing, kinda like folklore.org or something.

Stroustrup’s The Design and Evolution of C++ is a fascinating case study of trying to create a good programming language in the real world. I suspect that, once Rust has stabilised and the dust has settled, a retrospective from key contributors in a similar style would also make very interesting reading. I’m sure I’m not the only one following along and looking forward to 1.0 but not necessarily keeping up with the day-to-day decisions and all the detailed debate as the language evolves.

Re: On Error Handling in Rust

#54
post #48

Earlier quoted context omitted.

Funnily enough, we actually used to have both Either and Result, until one day we went through and realized that no code in existence was using Either and decided to go all-in on Result instead.

Someday, we should have a "History of Rust" thing, kinda like folklore.org or something.

I'd love to compile such a thing, but doing it properly would involve both cataloguing every change and recording the rationale for each. And because the rationale for changes is so dependent on the context of the-language-as-it-was-at-that-moment-in-time, random access of the archive would leave your understanding incomplete. For example, I just said that we removed `Either` because nobody was using it and because it was structurally a complete duplicate of `Result`... but I omitted the fact that `Result` was almost universally shunned at the time as well, in favor of `Option`. :P

Part of me wonders if the best way to do it wouldn't be to to start at Rust 1.0 and go on a tour backwards through history, i.e. "here's a great language, now let's all make terrible decisions to make it worse!" :)

Re: On Error Handling in Rust

#55
post #37
post #31

In some ways it's heartening to see Rust working everything out for itself - but it's also painful to watch the language stumble on the same problems that we've already solved. You're going to hit the same problem again with async, with transactions, and with resource management; indeed some of the stuff I've already seen about borrowing and the like seems achingly close to the same pattern. Introducing new sigils li…

I have the same concern, but I'm not sure how efficiently you could get monads to compile. It would be possible (but tricky) to come up with a macro for haskell's do syntax that would translate monad!( sock to TcpStream::connect(host, port).map(|sock| { let parser = Parser::new(&mut sock as &mut Reader); parser.parse_value() }) but could you get that to compile to the same machine code as this? match TcpStream::conne…

> but could you get that to compile to the same machine code as this?

Well not exactly because the second snippet is not equivalent to the first one (it doesn't wrap the result of parse_value() back into an Ok(), and thus I guess wouldn't compile), but aside from that yes: Result.map is trivially implemented in terms of match and (almost always) inlined: https://github.com/rust-lang/rust/blob/master/src/libcore/re... and the closure is itself inlined. So the resulting assembly should be the exact same between the two versions

Re: On Error Handling in Rust

#56
post #51
post #37

Earlier quoted context omitted.

I have the same concern, but I'm not sure how efficiently you could get monads to compile. It would be possible (but tricky) to come up with a macro for haskell's do syntax that would translate monad!( sock to TcpStream::connect(host, port).map(|sock| { let parser = Parser::new(&mut sock as &mut Reader); parser.parse_value() }) but could you get that to compile to the same machine code as this? match TcpStream::conne…

The only difference between the two is inlining the map method, no? This may be the "sufficiently smart compiler" fallacy, but that really doesn't seem very hard. Certainly I've been very impressed with the performance I've seen from Haskell (and Scala) on real-world problems, though I appreciate that's not quite the same thing, and a more realtime-suitable language that had the same level of expressiveness would def…

> The only difference between the two is inlining the map method, no? This may be the "sufficiently smart compiler" fallacy, but that really doesn't seem very hard.

It doesn't even rely on a smart compiler: https://github.com/rust-lang/rust/blob/master/src/libcore/re...

The #[inline] attribute is defined as a "standard (though very strong) inline hint" (there's also #[inline(never)] and #[inline(always)])

Re: On Error Handling in Rust

#57
post #35
post #31

In some ways it's heartening to see Rust working everything out for itself - but it's also painful to watch the language stumble on the same problems that we've already solved. You're going to hit the same problem again with async, with transactions, and with resource management; indeed some of the stuff I've already seen about borrowing and the like seems achingly close to the same pattern. Introducing new sigils li…

HKT is indeed on the long-term roadmap, but it remains to be seen whether a design can be devised that plays nicely with the fundamental features of Rust (note that the language reserves the unused `do` keyword for just this purpose). Doing this properly is very much research-project territory. And unless I'm reading the RFC incorrectly, I think this is more principled than you're making it out to be. `FromError` is…

`Carrier` is still pretty parochial; it has this normal/exception distinction hardwired into it, no? The RFC explicitly rules out using Vector with it, and it doesn't look possible to implement for async constructs, or STM transactions, or the like? I'd very much like to be wrong here.

FromError is not handled specially by the compiler, but it is handled specially by the try macro; does the signature of try make the relationship between the two more obvious? I haven't been following the state of Rust IDEs, but I'd hope that they can make it more obvious which typeclasses are involved than is clear from the text of the code (apologies for this awful sentence).

FWIW doing things this way doesn't rule out a dedicated syntax on top of it - see the recent SIP for async/await in scala.

Re: On Error Handling in Rust

#58
post #57
post #35

Earlier quoted context omitted.

HKT is indeed on the long-term roadmap, but it remains to be seen whether a design can be devised that plays nicely with the fundamental features of Rust (note that the language reserves the unused `do` keyword for just this purpose). Doing this properly is very much research-project territory. And unless I'm reading the RFC incorrectly, I think this is more principled than you're making it out to be. `FromError` is…

`Carrier` is still pretty parochial; it has this normal/exception distinction hardwired into it, no? The RFC explicitly rules out using Vector with it, and it doesn't look possible to implement for async constructs, or STM transactions, or the like? I'd very much like to be wrong here. FromError is not handled specially by the compiler, but it is handled specially by the try macro; does the signature of try make the…

  > `Carrier` is still pretty parochial; it has this 
  > normal/exception distinction hardwired into it, no?
To reiterate my earlier point, I'm personally fine with the existence of an entirely separate mechanism for error handling. Mind you, not that this invalidates your desire for a more general mechanism for async et al. Until/if we get HKTs, we'll probably continue to achieve this with bespoke macros as per today's `try!`.

  > FromError is not handled specially by the compiler, but it is handled specially by 
  > the try macro
The `try!` macro is just as non-special as `FromError` (and `Result` and `Option`). You're free to recreate the whole ecosystem in your own libs if you'd like (ignoring the `Carrier` proposal for the moment and its associated syntax).

Re: On Error Handling in Rust

#59
post #31

In some ways it's heartening to see Rust working everything out for itself - but it's also painful to watch the language stumble on the same problems that we've already solved. You're going to hit the same problem again with async, with transactions, and with resource management; indeed some of the stuff I've already seen about borrowing and the like seems achingly close to the same pattern. Introducing new sigils li…

I agree, I could not write it better myself. Going down one level and finding a better way to express the sugar around `map` and `flatmap` on Monads (and then `withFilter` in Scala) without resorting to compiler magic would be really cool and would let programmers add their own constructs that operate on a lot more than monads. Out of curiosity, do you know of another language that has something on the level of Resul…

Is Result not just a specialised Either? In what way is Either inferior?

Re: On Error Handling in Rust

#60
post #48

Earlier quoted context omitted.

Rust used to also have Either, and it was changed to Result for the same reasons. Name things what they mean. :)

Funnily enough, we actually used to have both Either and Result, until one day we went through and realized that no code in existence was using Either and decided to go all-in on Result instead.

Oh ok that makes sense. Either has a little more to it, as I am sure you are aware, specifically Type Disjunction, but a crappy version of Result is all anyone ever really uses it for as far as I have seen.

Real quick for those that don't understand, when I say Type Disjunctions (aka union types) I mean a type which has a value which the type system is guaranteeing is 1 of X different types. So Either[String,Int] is a specialized case where X=2 and the value is either a String or an Int. This ends up being really similar to Tuples, Tuples are often given their own syntax, typically, (A,B,...) and frequently in languages that don't support the tuple abstraction natively, things like std::pair pop up as a poor substitute special cased to X=2 element tuples.

This leads to my view that Either is to Type Disjunctions as something like std::pair is to Tuple, scratching the surface of a deeper and much richer abstraction. When it pops up in your language its a symptom of lacking type unions, just as std::pair is a symptom of lacking tuples.

Do you think Rust will ever have native support for type disjunctions?

I understand that it probably principly uses object hierarchys for this type of stuff, and in a lot of ways that makes a lot of sense, but often time when writing statically typed message passing code, aka Consumers / Producers, the more ad hoc representation + syntax support for unions eliminates huge traunches of boilerplate and provides static guarantees that all the cases are handled consider this code for example.

As real world, recurring pain in the neck example, in Scala to preserve type safety and exhaustiveness checking at my company we create a sealed trait hierarchy where each subclass wraps each different type in the disjunction.

It's a poor man's type disjunction and it takes A LOT of boilerplate.

Its still worth it to jump through these hoops because in a message passing system built around producers/consumers(aka, sources/sinks, emitters/handlers etc) we get the following important benefits:

a. Only messages that fall in a set of allowed types can be passed into a handler, guaranteeing that a message the handler can't handle won't be passed to it.

b. A handler is typically backed by a match expression which handles the different subtypes the handler handles. We used a sealed trait in scala pattern which guarantees that if someone adds another type to the hierarchy without adding it to all the handlers (which might be in different codebases) there is a compile error for a non-exhaustive match in the handler's implementation instead of a runtime blowup there when the unhandled type falls through the match.

Obviously this is just scratching the surface of the benefits that type disjunction can provide. Hopefully enough programmers and language designers will have an 'aha moment' and realize that just as adding lambda's to abbreviate anonymous functions opens up the world of higher order functions, and tuples just eliminate doing the same thing thousands of crappy different ways, and having an Option (aka Maybe) type eliminates null pointer exceptions, and hopefully standard library sanctioned Result type eliminates unhandled exceptions, having language support for type disjunctions will have benefits similar to all these other `essential` features.

TL;DR Type Disjunctions are really useful, and are missing fundamental in most type systems do you think Rust will support them?

Post reply on HN