Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there an equivalent of Scala's Either in Java 8?

Tags:

java

java-8

scala

Just like java.util.Optional<T> in Java 8 is (somewhat) equivalent to Scala's Option[T] type, is there an equivalent to Scala's Either[L, R]?

like image 858
HRJ Avatar asked Oct 02 '14 13:10

HRJ


People also ask

Does Scala only work with Java 8?

If you don't have Java 8, you can't use it. It doesn't matter if you are using Scala 2.11. 5, Scala 2.11. 8, Scala 2.12, Scala 1.0, Clojure, Groovy, Kotlin, Ceylon, Fantom, Ruby, Python, JavaScript, PHP, Scheme, CommonLisp, Java, or any other language.

Does Java have an Either type?

What Is Either? In a functional programming world, functional values or objects can't be modified (i.e. in normal form); in Java terminology, it's known as immutable variables. Either represents a value of two possible data types. An Either is either a Left or a Right.


3 Answers

There is no Either type is Java 8, so you need to create one yourself or use some third-party library.

You may build such a feature using the new Optional type (but read to the end of this answer):

final class Either<L,R>
{
    public static <L,R> Either<L,R> left(L value) {
        return new Either<>(Optional.of(value), Optional.empty());
    }
    public static <L,R> Either<L,R> right(R value) {
        return new Either<>(Optional.empty(), Optional.of(value));
    }
    private final Optional<L> left;
    private final Optional<R> right;
    private Either(Optional<L> l, Optional<R> r) {
      left=l;
      right=r;
    }
    public <T> T map(
        Function<? super L, ? extends T> lFunc,
        Function<? super R, ? extends T> rFunc)
    {
        return left.<T>map(lFunc).orElseGet(()->right.map(rFunc).get());
    }
    public <T> Either<T,R> mapLeft(Function<? super L, ? extends T> lFunc)
    {
        return new Either<>(left.map(lFunc),right);
    }
    public <T> Either<L,T> mapRight(Function<? super R, ? extends T> rFunc)
    {
        return new Either<>(left, right.map(rFunc));
    }
    public void apply(Consumer<? super L> lFunc, Consumer<? super R> rFunc)
    {
        left.ifPresent(lFunc);
        right.ifPresent(rFunc);
    }
}

Example use case:

new Random().ints(20, 0, 2).mapToObj(i -> (Either<String,Integer>)(i==0?
  Either.left("left value (String)"):
  Either.right(42)))
.forEach(either->either.apply(
  left ->{ System.out.println("received left value: "+left.substring(11));},
  right->{ System.out.println("received right value: 0x"+Integer.toHexString(right));}
));

In retrospective, the Optional based solution is more like an academic example, but not a recommended approach. One problem is the treatment of null as “empty” which contradicts the meaning of “either”.

The following code shows an Either that considers null a possible value, so it’s strictly “either”, left or right, even if the value is null:

abstract class Either<L,R>
{
    public static <L,R> Either<L,R> left(L value) {
        return new Either<L,R>() {
            @Override public <T> T map(Function<? super L, ? extends T> lFunc,
                                       Function<? super R, ? extends T> rFunc) {
                return lFunc.apply(value);
            }
        };
    }
    public static <L,R> Either<L,R> right(R value) {
        return new Either<L,R>() {
            @Override public <T> T map(Function<? super L, ? extends T> lFunc,
                                       Function<? super R, ? extends T> rFunc) {
                return rFunc.apply(value);
            }

        };
    }
    private Either() {}
    public abstract <T> T map(
      Function<? super L, ? extends T> lFunc, Function<? super R, ? extends T> rFunc);

    public <T> Either<T,R> mapLeft(Function<? super L, ? extends T> lFunc) {
        return this.<Either<T,R>>map(t -> left(lFunc.apply(t)), t -> (Either<T,R>)this);
    }
    public <T> Either<L,T> mapRight(Function<? super R, ? extends T> lFunc) {
        return this.<Either<L,T>>map(t -> (Either<L,T>)this, t -> right(lFunc.apply(t)));
    }
    public void apply(Consumer<? super L> lFunc, Consumer<? super R> rFunc) {
        map(consume(lFunc), consume(rFunc));
    }
    private <T> Function<T,Void> consume(Consumer<T> c) {
        return t -> { c.accept(t); return null; };
    }
}

It’s easy to change that to a strict rejection of null by simply inserting an Objects.requireNonNull(value) at the beginning of both factory methods. Likewise, adding support for an empty either would be imaginable.

like image 137
Holger Avatar answered Oct 13 '22 18:10

Holger


At the time of writing, vavr (formerly javaslang) is probably the most popular functional Java 8 library. It is pretty similar to lambda-companion's Either in my other answer.

Either<String,Integer> value = compute().right().map(i -> i * 2).toEither();
like image 21
whirlwin Avatar answered Oct 13 '22 19:10

whirlwin


There is no Either in the Java Standard Library. However there is an implementation of Either in FunctionalJava, along with many other nice classes.

like image 18
Ravi Kiran Avatar answered Oct 13 '22 19:10

Ravi Kiran