如何在Java中模拟Haskell的“要么a b”

2022-09-01 06:29:20

如何编写一个类型安全的 Java 方法,返回类 a 或类 b?例如:

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

最清洁的方式是什么?

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

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

)


答案 1

我用于模拟代数数据类型的一般公式是:

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

答案 2

这是一个经过静态检查的类型安全解决方案;这意味着您无法创建运行时错误。请按其含义阅读前一句。是的,您可以以某种方式引发异常...

这很冗长,但是嘿,它是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);
                    }
                });
    }
}

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

这是在函数式Java中实现的链接。在我的示例中,该示例称为此处。他们有一个更复杂的版本,能够返回一个值(这似乎适合函数式编程风格)。Eitherfoldeitherfold


推荐