Java标记的联合/总和类型 [英] Java tagged union / sum types

查看:163
本文介绍了Java标记的联合/总和类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Is there any way to define a sum type in Java? Java seems to naturally support product types directly, and I thought enums might allow it to support sum types, and inheritance looks like maybe it could do it, but there is at least one case I can't resolve. To elaborate, a sum type is a type which can have exactly one of a set of different types, like a tagged union in C. In my case, I'm trying to implement haskell's Either type in Java:

data Either a b = Left a | Right b

但是在基本级别上,我必须将其实现为产品类型,而忽略其字段之一:

but at the base level I'm having to implement it as a product type, and just ignore one of its fields:

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泛型不允许使用此参数:

I tried implementing this with inheritance, but I have to use a wildcard type parameter, or equivalent, which Java generics won't allow:

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

我使用Java的Enums的次数不多,但是尽管它们似乎是次佳的候选者,但我并不抱有希望.
在这一点上,我认为只有通过类型转换Object值才有可能实现这一点,我希望完全避免这种情况,除非有一种方法可以安全地一次执行此操作,并且能够将其用于所有求和类型.

I haven't used Java's Enums much, but while they seem the next best candidate, I'm not hopeful.
At this point, I think this might only be possible by type-casting Object values, which I would hope to avoid entirely, unless there's a way to do it once, safely, and be able to use that for all sum types.

推荐答案

使Either为一个没有字段且只有一个构造函数(私有,无参数,空)的抽象类,并嵌套您的数据构造函数"(right静态工厂方法),以便他们可以看到私有构造函数,但其​​他什么也看不到,有效地密封了类型.

Make Either an abstract class with no fields and only one constructor (private, no-args, empty) and nest your "data constructors" (left and right static factory methods) inside the class so that they can see the private constructor but nothing else can, effectively sealing the type.

使用抽象方法 来模拟详尽的模式匹配,并适当地覆盖由静态工厂方法返回的具体类型.实施便捷方法(例如 fromLeft fromRight bimap first second )根据either.

Use an abstract method either to simulate exhaustive pattern matching, overriding appropriately in the concrete types returned by the static factory methods. Implement convenience methods (like fromLeft, fromRight, bimap, first, second) in terms of 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方法进行定义,这将迫使调用者处理这两种情况.

Pleasant and safe! No way to screw it up. Because the type is effectively sealed, you can rest assured that there will only ever be two cases, and every operation ultimately must be defined in terms of the either method, which forces the caller to handle both of those cases.

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

Regarding the problem you had trying to do class Left<L> extends Either<L,?>, consider the signature <A, B> Either<A, B> left(A value). The type parameter B doesn't appear in the parameter list. So, given a value of some type A, you can get an Either<A, B> for any type B.

这篇关于Java标记的联合/总和类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆