Hello everyone!
Now-a-days I am writing code in several different languages : Rust, C , JavaScript, Python. While all of these have their own use ...
For further actions, you may consider blocking this person and/or reporting abuse
Good article.
#[must_use]is a great aid. I agree with you.However, we can use an exact equivalent of
Resultin Java/Scala. It is called 'Either'. Perhaps, you already know about it. When instantiated, a value ofEithercan be of either the 'L' type - conventionally, indicating an error - or the 'R' type, which is the value, we are otherwise looking for.Scala has built in Either type. In Java 8+ (I have used till Java 17), by using the fantastic 'vavr' library, 'Either' can be put to use, including the pattern-matching idioms, map(), flatMap() etc, much like Rust lets us.
I don't know Kotlin, but I assume Kotlin has something similar. Experts can comment.
Hey, thanks for the comment! I have updated the article to mention this. I don't work much with Java/Scala so didn't know that. Does the
Eithertype also have similar ergonomics as rust's Result? I know that types like Rust'senum(I think they are called additive data types?) exist in several other languages, but I didn't knew those have similar ergonomics as Rust's results have.As for ergonomics, @omidrad has already elaborated. the difference between Rust and Java. I differ slightly from his PoV and I have stated that in my response to him.
But that's different.
When you are in the ecosystem and use some dependencies, some deps use
Eitherand some use exception. It makes things more complicated and ugly and at some point you need to handle both at the same time!Eitherdoes exist in Rust (as library, and removed long time ago from the std lib), but IMO to use it as a way to handle errors is lame.On top of these, there is no difference between L and R in Either, there is no Ok and Err. A library may see L as Err and another one R as Err. Also what about
?in Rust?Anyway, they are not the same.
Not same, of course. But equivalent. :-)
I have steadfastly - one may say determinedly - stayed away from all checked exceptions for last few years in all my Java applications. Wherever pressed, I have wrapped the exception-throwing methods into
Eitherreturning methods.Not easy always, as you have correctly pointed out - but demonstrably doable.
The other point you raise about L and R being not a fixed type, you are right. The convention of course, is that L is for errors and R is for acceptable values. However, nothing stops me from making L a subtype of an master Error type. The compiler does the rest.
I would say they are not equivalent also.
That's a misuse of Either to handle errors on other languages. Or let's say the best way they can do so. There are a lot around
Resultin Rust. There is nothing specific for error handling forEither.The only common thing is that, both
ResultandEitherare two-variant enums! (I don't know how exactly they called them in Java)It's like people saying Java also has
Option. But that's different between when the whole ecosystem only hasOptionor there is a possibility of usingOptions. They are not the same or equivalent.We don't have these kinds of inconsistency, because it's a younger language. But I hope we don't have it in future also.
Scala
Eithers are not exactly as blank as that, because they are "right-biased"; meaning that, in all comprehensive operations, it considersRightvalues to be present andLeftvalues to not be. This is why you can chainflatMaps ofEithers together without having to designate what the types mean; for a ScalaEither, theRightis always the default.It's also why an
Eitherwith an error in theLefttype is a simple implementation of theResulttype; youmap/flatMapover them, and if you end up with aLeft, you got an error.That said, as I said elsewhere Scala also has the Try type which I think works better, if for no other reason than the semantics around it more clearly indicate you're dealing with errors.
(Scala doesn't have the
?operator, it's true, because IIRC the style prefers keeping values inside the container and applying transformations to it, instead of extracting the value or returning early. YMMV)Scala doesn't have anything related to errors for its
Either. Does it haveis_errfor example (sure you can check if the left is there or right)? Does it havemap_err? Does it haveexpect? Does it haveor_Xfunctions? Does it haveunwrapfunctions? and so on...There can be a library which can add these functionalities to the language. But as I mentioned many times, it's not the same!
When you can use different ways of having errors (in this case) and you don't follow the language standard/default way. It creates inconsistency in the ecosystem, specially as a library developer or when you use external libraries!
If I was a Java/Scala developer, and wanted to develop a library, I would never use
Eitherto handle/expose errors.But
Tryis something similar to Rust'sResult. The only bad part is that now devs need to handleEither,TryandExceptions (and maybe more).I am not into splitting hairs on semantic niceties! :-)
A concept exists in Scala / Java ( In Elixir and Haskell too, my FP guru friends tell me), to indicate that a function can return either of two possibilities but never, both.
If a function can return two different types of SUCCESS indicators, and one decides to use Either for that, it is fine! But, that's not what the common sense dictates.
The equivalence that I pointed out was about a facility in the type system to disassociate an ERROR from an ACCEPTABLE value that a function returns: much more easier to deal with, say
errnoof my C days or randomExceptionsthrown in a JVM-based code. That's about it.You are right, Rust has the advantage of being younger than many other languages and is offering a cleaner way to disassociate ERROR values from ACCEPTABLE values. Compiler's watchful eyes point out any inconsistencies earlier in the cycle. That helps a lot, too.
My point too.
And, there is a reason why
Tryin Scala / Java ( vavr gives the equivalentTrytype in Java), has methods liketoEither()andtoOption().Scala also has the Try type, which is the more exact analogue to a Rust Result, and it's had it since 2.10.
Some older Scala code uses
Eithersince it came out first, but most of the Scala code I've read and written (admittedly, it's been a few years) useTry[T]overEither[T, Error].I haven't tried it, but it looks like Arrow is the Kotlin FP batteries included library and has Either (Kotlin can also just use vavr afaik)
arrow-kt.io/learn/typed-errors/eit...
With all my hate to
if err!=nil{}, Go has error wrapping:See rollbar.com/blog/golang-wrap-and-u...
Hey, I have updated the article to mention this. I had only seen the
err != nilkind of error handling, and haven;t worked with go much, so didn't know that.I checked the link, but I'm not sure. Does the wrapping-unwrapping provide similar ergonomics as Rust Results too?
Hell not. I hate it not without a reason. I still find Rust's error handling superior to all the other languages I use(d).
Also when you are in the ecosystem and use some dependencies, some deps use unwrapped version and some use wrapped version. It makes things more complicated!