Java 标记的 union/sum 类型

标签 java haskell algebraic-data-types

有没有办法在 Java 中定义 sum 类型? Java 似乎天生就直接支持乘积类型,我认为枚举可能允许它支持 sum 类型,而继承看起来也许可以做到,但至少有一种情况我无法解决。 详细地说,sum 类型是一种可以恰好具有一组不同类型中的一个的类型,例如 C 中的标记联合。 就我而言,我正在尝试在 Java 中实现 haskell 的 Either 类型:

data Either a b = Left a | Right b

但在基础级别,我必须将其实现为产品类型,而忽略其中一个字段:

public class Either<L,R>
{
    private L left = null;
    private R right = null;

    public static <L,R> Either<L,R> right(R right)
    {
        return new Either<>(null, right);
    }

    public static <L,R> Either<L,R> left(L left)
    {
        return new Either<>(left, null);
    }

    private Either(L left, R right) throws IllegalArgumentException
    {
        this.left = left;
        this.right = right;
        if (left != null && right != null)
        {
            throw new IllegalArgumentException("An Either cannot be created with two values");
        }
        if (left == right)
        {
            throw new IllegalArgumentException("An Either cannot be created without a value");
        }
    }

    .
    .
    .
}

我尝试通过继承来实现这一点,但我必须使用通配符类型参数或等效参数,这是 Java 泛型不允许的:

public class Left<L> extends Either<L,?>

我没有太多使用 Java 的 Enums,但是虽然它们似乎是次佳候选,但我并不抱希望。
在这一点上,我认为这可能只能通过类型转换 Object 值来实现,我希望完全避免这种情况,除非有一种方法可以安全地做到这一点,并且能够使用它适用于所有总和类型。

最佳答案

制作 Either一个没有字段且只有一个构造函数(私有(private)、无参数、空)的抽象类,并将您的“数据构造函数”(leftright 静态工厂方法)嵌套在类中,以便他们可以看到私有(private)构造函数,但没有别的可以,有效地密封类型。

使用抽象方法 either 模拟详尽的模式匹配,在静态工厂方法返回的具体类型中适本地覆盖。根据 fromLeft 实现便利方法(如 fromRight bimap first second either ) .

import java.util.Optional;
import java.util.function.Function;

public abstract class Either<A, B> {
    private Either() {}

    public abstract <C> C either(Function<? super A, ? extends C> left,
                                 Function<? super B, ? extends C> right);

    public static <A, B> Either<A, B> left(A value) {
        return new Either<A, B>() {
            @Override
            public <C> C either(Function<? super A, ? extends C> left,
                                Function<? super B, ? extends C> right) {
                return left.apply(value);
            }
        };
    }

    public static <A, B> Either<A, B> right(B value) {
        return new Either<A, B>() {
            @Override
            public <C> C either(Function<? super A, ? extends C> left,
                                Function<? super B, ? extends C> right) {
                return right.apply(value);
            }
        };
    }

    public Optional<A> fromLeft() {
        return this.either(Optional::of, value -> Optional.empty());
    }
}

愉快又安全!没办法搞砸。因为类型是有效密封的,所以您可以放心,只有两种情况,并且每个操作最终都必须根据 either 定义。方法,它强制调用者处理这两种情况。

关于您尝试解决的问题 class Left<L> extends Either<L,?> ,考虑签名<A, B> Either<A, B> left(A value) .类型参数B不会出现在参数列表中。因此,给定某个类型的值 A ,您可以获得Either<A, B>对于任何类型B .

关于Java 标记的 union/sum 类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48143268/

相关文章:

java - 公共(public)函数不返回值

java - Azure Blob存储: CloudBlockBlob. downloadToByteArray(byte[] buffer, int bufferOffset)如何设置缓冲区大小

haskell - 在函数类型签名中替换 => 代替 ->

haskell - Haskell 有没有办法表达一个自由点函数来编辑数据类型的属性?

haskell - 为什么使用模式匹配构造的函数具有 Eq 类型约束,但在使用数据构造函数时没有?

java - 执行 native 更新查询后,检索到的实体中仍然存在旧值

java - 我可以在创建类(class)后将它们组织到文件夹中并且它们仍然有效吗?

macos - 在 Mac 上安装 Haskell 软件包

haskell - putStrLn 函数中美元符号的用法

types - Agda 中列表的定义