如何在Java中模拟Haskell的"Either a b"

Bas*_*stl 35 java haskell

我怎样才能编写一个类型安全的Java方法来返回类a或类b的东西?例如:

public ... either(boolean b) {
  if (b) {
    return new Integer(1);
  } else {
    return new String("hi");
  }
}
Run Code Online (Sandbox Code Playgroud)

什么是最干净的方式?

(我唯一想到的就是使用明显不好的异常,因为它滥用了一般语言功能的错误处理机制......

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

)

new*_*cct 35

我模拟代数数据类型的通用公式是:

  • 类型是抽象基类,构造函数是它的子类
  • 每个构造函数的数据在每个子类中定义.(这允许具有不同数据数量的构造函数正常工作.它还消除了维护不变量的需要,例如只有一个变量是非null或类似的东西).
  • 子类的构造函数用于构造每个构造函数的值.
  • 要解构它,可以使用instanceof检查构造函数,并向下转换为适当的类型以获取数据.

所以Either a b,它会是这样的:

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
}
Run Code Online (Sandbox Code Playgroud)

  • 这与使用`isLeft`,`getAsLeft`和`getAsRight`方法的方法大致相同.它不是类型安全的(它依赖于正确的客户端代码):如果你在客户端弄乱它,你可以在转换时有异常. (6认同)

zig*_*tar 25

这是一种静态检查的类型安全解决方案; 这意味着您无法创建运行时错误.请按照其含义阅读上一句.是的,你可以用某种方式激起例外......

它非常冗长,但是嘿,这是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);
                    }
                });
    }
}
Run Code Online (Sandbox Code Playgroud)

您可能想看看Functional Java和Tony Morris的博客.

Either功能Java 实现的链接.将fold在我的例子被称为either那里.它们有一个更复杂的版本fold,它能够返回一个值(这似乎适合函数式编程风格).

  • @Bastl`left` /`right`是构造函数.我不是Haskell的人,并且不太明白你的意思是"绑定类型".`Function`只是一个Helper类(用于第一类函数),可以在其他地方定义.这里没什么可空的.诀窍是`fold`只调用正确类型的函数.调用代码总是必须为`A`和`B`提供处理函数. (3认同)
  • 就 Haskell 而言,你在这里所做的是利用同构 `(∀ r . (a -&gt; r) -&gt; r)` ≅ `a` 使得 `Either ab` ≅ `(∀ r . (Either ab -&gt; r) -&gt; r)` ≅ `(∀ r . (a -&gt; r, b -&gt; r) -&gt; r)`。 (2认同)

Ric*_* T. 9

您可以通过编写一个通用类与哈斯克尔密切的对应Either,参数两种类型L,并R具有两个构造函数(在一个了结L,以及一个回吐的R)和两种方法L getLeft(),并R getRight()使得他们要么返回施工时传递的值,或抛出一个例外.

  • 使`Either`摘要并声明`L getLeft()`和`R getRight()`和`bool isRight()`.现在,使用正确的构造函数创建`LeftEither`和`RightEither`,以避免两个构造函数之间的类型擦除冲突.[Darn you,键入擦除!] (2认同)

gpa*_*ara 7

已经提供的建议虽然可行,但并不完整,因为它们依赖于一些null参考文献并有效地使"Either"伪装成值的元组.不相交的总和显然是一种类型或另一种.

我建议在看看执行FunctionalJavaEither作为例子.