Zoe*_*wll 40 java haskell algebraic-data-types
有没有办法在Java中定义sum类型?Java似乎直接支持产品类型,我认为枚举可能允许它支持和类型,并且继承看起来可能它可以做到,但至少有一个我无法解决的情况.详细说明,sum类型是一种类型,它可以恰好具有一组不同类型中的一种,例如C中的标记联合.在我的例子中,我试图在Java中实现haskell的Either类型:
data Either a b = Left a | Right b
Run Code Online (Sandbox Code Playgroud)
但是在基础级别我不得不将其作为产品类型实现,而忽略其中一个字段:
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");
}
}
.
.
.
}
Run Code Online (Sandbox Code Playgroud)
我尝试使用继承来实现它,但是我必须使用Java泛型不允许的通配符类型参数或等效参数:
public class Left<L> extends Either<L,?>
Run Code Online (Sandbox Code Playgroud)
我没有太多使用Java的Enums,但是虽然它们似乎是次佳的候选者,但我并不抱希望.
在这一点上,我认为这可能只能通过类型转换Object值来实现,我希望完全避免这种情况,除非有一种方法可以安全地执行,并且能够将其用于所有和类型.
gde*_*ohn 52
做Either一个私人的构造函数和嵌套你的"数据构造"(一个抽象类left和right静态工厂方法)的类里面,使他们可以看到私有的构造,但没有别的可以有效地密封型.
使用抽象方法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<>() {
@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<>() {
@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());
}
}
Run Code Online (Sandbox Code Playgroud)
愉快又安全!没办法搞砸了.
关于您尝试过的问题either,请考虑签名class Left<L> extends Either<L,?>.type参数<A, B> Either<A, B> left(A value)不会出现在参数列表中.因此,考虑到某种类型的值B,你可以得到一个A对任何类型Either<A, B>.
Jon*_*rdy 19
编码和类型的标准方法是Boehm-Berarducci编码(通常由其堂兄的名称,Church编码),它代表代数数据类型作为其消除器,即执行模式匹配的函数.在Haskell:
left :: a -> (a -> r) -> (b -> r) -> r
left x l _ = l x
right :: b -> (a -> r) -> (b -> r) -> r
right x _ r = r x
match :: (a -> r) -> (b -> r) -> ((a -> r) -> (b -> r) -> r) -> r
match l r k = k l r
-- Or, with a type synonym for convenience:
type Either a b r = (a -> r) -> (b -> r) -> r
left :: a -> Either a b r
right :: b -> Either a b r
match :: (a -> r) -> (b -> r) -> Either a b r -> r
Run Code Online (Sandbox Code Playgroud)
在Java中,这看起来像访问者:
public interface Either<A, B> {
<R> R match(Function<A, R> left, Function<B, R> right);
}
public final class Left<A, B> implements Either<A, B> {
private final A value;
public Left(A value) {
this.value = value;
}
public <R> R match(Function<A, R> left, Function<B, R> right) {
return left.apply(value);
}
}
public final class Right<A, B> implements Either<A, B> {
private final B value;
public Right(B value) {
this.value = value;
}
public <R> R match(Function<A, R> left, Function<B, R> right) {
return right.apply(value);
}
}
Run Code Online (Sandbox Code Playgroud)
用法示例:
Either<Integer, String> result = new Left<Integer, String>(42);
String message = result.match(
errorCode -> "Error: " + errorCode.toString(),
successMessage -> successMessage);
Run Code Online (Sandbox Code Playgroud)
为方便起见,您可以创建一个工厂来创建Left和Right值,而不必每次都提到类型参数; 您也可以添加一个match接受的版本,Consumer<A> left, Consumer<B> right而不是Function<A, R> left, Function<B, R> right如果您想要模式匹配选项而不产生结果.
好吧,所以继承解决方案绝对是最有希望的.我们想要做的是class Left<L> extends Either<L, ?>,由于Java的通用规则,我们遗憾地无法做到这一点.但是,如果我们做出让步Left或者Right必须编码"替代"可能性的让步,我们就可以做到这一点.
public class Left<L, R> extends Either<L, R>`
Run Code Online (Sandbox Code Playgroud)
现在,我们希望能够转换Left<Integer, A>为Left<Integer, B>,因为它实际上并没有使用第二个类型参数.我们可以定义一种在内部进行此转换的方法,从而将该自由编码到类型系统中.
public <R1> Left<L, R1> phantom() {
return new Left<L, R1>(contents);
}
Run Code Online (Sandbox Code Playgroud)
完整的例子:
public class EitherTest {
public abstract static class Either<L, R> {}
public static class Left<L, R> extends Either<L, R> {
private L contents;
public Left(L x) {
contents = x;
}
public <R1> Left<L, R1> phantom() {
return new Left<L, R1>(contents);
}
}
public static class Right<L, R> extends Either<L, R> {
private R contents;
public Right(R x) {
contents = x;
}
public <L1> Right<L1, R> phantom() {
return new Right<L1, R>(contents);
}
}
}
Run Code Online (Sandbox Code Playgroud)
当然,你要添加一些功能实际上访问的内容,以及用于检查值是否Left还是Right这样你就不必洒instanceof和显式转换无处不在,但是这应该是足够的上手,在最最小.
| 归档时间: |
|
| 查看次数: |
5501 次 |
| 最近记录: |