Hacker News new | ask | show | jobs
by blastonico 641 days ago
Isn't it based on ML family? I mean, I see Rust error handling heavy inspired in monads used in languages like OCaml and Haskell.

Is Rust doing something different?

2 comments

Rust's error handling isn't at all like using a monad. The entire point of being able to express the monad for something like the behavior you expect from error handling is that you write code which automatically propagates the error and in the same breadth prevents you from ever being able to see the error. The result is essentially exactly exceptions: you program the happy path and it entirely hides errors from you, as that's the point of the monad, which for this purpose you can pretty much conceptualize as programmatic flow control (or, even more crudely, an overloaded semicolon / statement separator operator).

Rust is using the data structure, but doesn't have a way to express or use the monad, so you have to deal with and manually propagate the error. But like, if you do not have the monad, the data structure is just awkward... the only reason the data structure for this monad even exists at all is to support the monad, and the reason for the monad is to get a syntax similar to exceptions! In a language with a ton of hard-coded syntax for all of these things you'd use a monad for in Haskell--whether it's error handling, asynchronous execution, scope allocation... whatever floats your boat--you should just use exceptions.

You can choose to work with Rust's Result in a monadic way, that's what methods like Result::and_then and Result::or_else and so on are for.

Because it's just another type you could also do whatever else you like, unlike with the Exceptions in typical languages which have them where too bad, we bolted the information to the control flow so now we're going on a journey.

If you want to bolt control flow to some information in Rust that's fine, feel free to define a function which returns ControlFlow::Break for success if that suits you, the try operator understands what you meant, early success is fine. Actually you can see this reflected in the larger language because break 'label value; exists in Rust unlike for example C++.

What makes Monad interesting is that it is a trait that you have implemented, so you can work generically any monads. I thereby feel like saying manually calling these methods is "monadic" kind of misses the point of why a monad was interesting in the first place.

Haskell's error handling isn't you sitting around calling monad methods: you implement the Monad trait so you don't have to, and it then all gets hidden behind do notation, with the result that you get the same control flow that you'd get just using exceptions.

It thereby is just constantly strange to me that people talk about any of these languages as if they learned something from Haskell... Haskell clearly wanted things like state and exceptions and such, but wanted to do so on top of a lazy pure functional core language.

The trick they came up with is thereby to define this trait called Monad, which lets you program into the control flow all of these bespoke behaviors you get from the imperative languages: state, exceptions, scopes, asynchronicity, list comprehensions... you name it.

But the end result is not in any way "manual": the end code doesn't involve destructuring an Either every time you make a call, but it also doesn't involve calling methods to deal with the errors. The end result is, as best as they could implement, exception handling!

And like, in the same way that people can make mistakes with manual memory allocation, so we prefer scope allocation, people can also make mistakes with manual error propagation, and so you'd expect we would prefer exception unwinding: the monad enforces consistency.

In that light, the behavior of most languages with respect to many of these behaviors is to just have hardcoded every function to be in a standard set of stacked monads for basic things everyone takes for granted: exceptions are just hardcoded monadic error handling.

But Rust? That isn't monadic error handling: that's just manual error propagation. If we are going to call Rust's manual error management regime "monadic", we should also call C's manual resource management regime "monadic". If you are doing it manually, it isn't monadic.

And sure, calling the methods of the monad kind of makes it look a bit less manual, but that's like moving on from C and now saying that Go's manual-ish resource management (defer) is monadic. If you aren't forced to do it the standard/correct way, it isn't monadic.

At least Haskell and OCaml also offer exceptions as alternatives.