Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I simulate Haskell's "Either a b" in Java

Tags:

java

haskell

How can I write a typesafe Java method that returns either something of class a or something of class b? For example:

public ... either(boolean b) {
  if (b) {
    return new Integer(1);
  } else {
    return new String("hi");
  }
}

What is the cleanest way?

( The only thing that comes to my mind is using exceptions which is obviously bad, as it is abusing a error-handling mechanism for a general language feature ...

public String either(boolean b) throws IntException {
  if (b) {
    return new String("test");
  } else {
    throw new IntException(new Integer(1));
  }
}

)

like image 840
Bastl Avatar asked Apr 02 '12 11:04

Bastl


4 Answers

My general formula for simulating algebraic data types is:

  • The type is an abstract base class, and the constructors are subclasses of that
  • The data for each constructor are defined in each subclass. (This allows constructors with different numbers of data to work correctly. It also removes the need to maintain invariants like only one variable is non-null or stuff like that).
  • The constructors of the subclasses serve to construct the value for each constructor.
  • To deconstruct it, one uses instanceof to check the constructor, and downcast to the appropriate type to get the data.

So for Either a b, it would be something like this:

abstract class Either<A, B> { }
class Left<A, B> extends Either<A, B> {
    public A left_value;
    public Left(A a) { left_value = a; }
}
class Right<A, B> extends Either<A, B> {
    public B right_value;
    public Right(B b) { right_value = b; }
}

// to construct it
Either<A, B> foo = new Left<A, B>(some_A_value);
Either<A, B> bar = new Right<A, B>(some_B_value);

// to deconstruct it
if (foo instanceof Left) {
    Left<A, B> foo_left = (Left<A, B>)foo;
    // do stuff with foo_left.a
} else if (foo instanceof Right) {
    Right<A, B> foo_right = (Right<A, B>)foo;
    // do stuff with foo_right.b
}
like image 135
newacct Avatar answered Oct 15 '22 22:10

newacct


Here is a statically checked type-safe solution; this means you cannot create runtime errors. Please read the previous sentence in the way it is meant. Yes, you can provoke exceptions in some way or the other...

It's pretty verbose, but hey, it's Java!

public class Either<A,B> {
    interface Function<T> {
        public void apply(T x);
    }

    private A left = null;
    private B right = null;
    private Either(A a,B b) {
        left = a;
        right = b;
    }

    public static <A,B> Either<A,B> left(A a) {
        return new Either<A,B>(a,null);
    }
    public static <A,B> Either<A,B> right(B b) {
        return new Either<A,B>(null,b);
    }

    /* Here's the important part: */
    public void fold(Function<A> ifLeft, Function<B> ifRight) {
        if(right == null)
            ifLeft.apply(left);
        else
            ifRight.apply(right);
    }

    public static void main(String[] args) {
        Either<String,Integer> e1 = Either.left("foo");
        e1.fold(
                new Function<String>() {
                    public void apply(String x) {
                        System.out.println(x);
                    }
                },
                new Function<Integer>() {
                    public void apply(Integer x) {
                        System.out.println("Integer: " + x);
                    }
                });
    }
}

You might want to look at Functional Java and Tony Morris' blog.

Here is the link to the implementation of Either in Functional Java. The fold in my example is called either there. They have a more sophisticated version of fold, that is able to return a value (which seems appropriate for functional programming style).

like image 26
ziggystar Avatar answered Oct 15 '22 22:10

ziggystar


You can have a close correspondence with Haskell by writing a generic class Either, parametric on two types L and R with two constructors (one taking in an L, and one taking in an R) and two methods L getLeft() and R getRight() such that they either return the value passed when constructing, or throw an exception.

like image 9
Riccardo T. Avatar answered Oct 15 '22 23:10

Riccardo T.


The suggestions already provided, although feasible, are not complete as they rely on some null references and effectively make "Either" masquerade as a tuple of values. A disjoint sum is obviously one type or the other.

I'd suggest having a look at the implementation of FunctionalJava's Either as an example.

like image 7
gpampara Avatar answered Oct 15 '22 23:10

gpampara