You signed in with another tab or window.Reload to refresh your session.You signed out in another tab or window.Reload to refresh your session.You switched accounts on another tab or window.Reload to refresh your session.Dismiss alert
A right-biased implementation of Haskell'sEither a b for Java, using Java 8 for mapping/folding and type inference.
AnEither<A,B> is used to "condense" two distinct options down to a single choice.
"Right-biased" means that, like Haskell'sEither a b, when anEither<A, B> has both options available, it prefersthe one on the "right" (that is,B). The mnemonic often used in Haskell to remember/explain this "bias" is that anEither (usually used for error-checking), gives youeither theright answer, or else whatever'sleft.
Wait, what?
Yes, polymorphism is great in Java.Animal.speak(),Dog.speak(),Cat.speak(), and so on.
But sometimes you want to be REALLY EXPLICIT about the possible types of objects you're dealing with, either (ha ha)because they don't logically make sense in the same inheritance structure or because you want to limit yourpossible cases to exactly two classes.
For example, let's say I'm dealing with my code and some third-party library that still usesjava.util.Date (a classthat has been largely deprecated since JDK 1.1, and is almost always a strictly-worse choice than using either the newerjava.time apis, or Joda Time) .
Terrible, I know, but there's alwayshorrible legacy code out there in the world.
I don't want to further propagate this old usage of a terrible deprecated class, so I, a Modern Java Developer, preferto usejava.time.LocalDateTime in my code.
Except great, now I've gotta deal with grabbing aList<LocalDateTime> fromthese and aList<Date> fromthose andthen do a bunch of gnarly conversion everywhere.
UsingEither.java, I can instead easily build aList<Either<Date,LocalDatetime>>. This tells me (and the compiler)that I'm dealing with a bunch of things that areeither a Date (yuck) or a LocalDateTime.
I can even nicely convert them all into LocalDateTimes:
Boom. Now you have a list of LocalDateTimes, and it's explicit from the code that some of the people had deprecatedDates, while others had nice, shiny new LocalDateTimes. Even the compiler can tell!
Other common use-cases for anEither include capturing and handling errors gracefully usingEither<SomeKindOfException, SuccessfulResultClass>. As mentioned earlier, the convention in the Haskell world (fromwhich I totally "borrowed" the Either) is that an Either gives you "either the Right answer or whatever's Left" -- thatis, errors on the left, expected output on the right.
For this reason, this Either is right-biased; if you give itEither.either(()->42, ()->"Hello, World!");, you'll get aRight containing"Hello, World!", not aLeft containing42. I swear, it's not a political thing; there justneeds to be a predictable rule-of-thumb for how to handle it when the Either getsboth a left valueand a rightvalue (after all, it's called anEither, not aBoth).
Result
Because this errors-to-the-left, results-to-the-right idiom is so common, this library also includes aResult<T> class,which instead of beingLeft<L,R> orRight<L,R> isErr<T> orOk<T>. Instead ofEither.either(() -> "left", () -> 42),the constructor method you'll want isResult.attempt(() -> someMethodThatMightThrowAnException()). You can even chaina series of possibly-failing functions usingmap:
If you're working withcollections of Eithers (for example, you're performing a series of validations, all of which caneither succeedor return a validation error message), you'll probably be interested inEitherCollectors, with its.toLeftBiased() and.toRightBiased() methods for use with Java Streams:
// Let's say you have this variable already defined from your validations:// List<Either<ValidationError, ValidationSuccess>> validationResults // And you want to fail the user's request if there's *any* error, and return *all* the errors if so. EitherCollectors can help!Either<List<ValidationError>, List<ValidationSuccess>> errorsOrSuccesses = validationResults.stream().collect(EitherCollectors.toLeftBiased());// If there's even *one* left-hand ValidationError in our list, then we'll get a Left containing *all* the ValiidationErrors.// If not, we'll get a Right containing any ValidationSuccess results.// So we can do this:return errorsOrSuccesses.fold((List<ValidationError> errors) -> new UserFacingErrorsResponse(errors),(List<ValidationSuccess successes) -> new SuccessResponse(successes));
So what else can it do?
Wanna see more? Check out the unit tests for a run-down of how Eithers behave. If those tests aren't descriptive enough,or you think they should behave differently, open a Github issue! I built this because it didn't look like anyone elsehad built it for Java yet, and I may have lost something in the translation. I'm totally open for feedback.
Cool! How do I get it for my project?
Simple! It's in Maven Central, so just add this to your pomfile (or the equivalent for gradle/sbt/lein/whatever):