Java 如何模拟Haskell';s";要么是“b”;在爪哇
如何编写一个类型安全的Java方法来返回类a或类b?例如:Java 如何模拟Haskell';s";要么是“b”;在爪哇,java,haskell,Java,Haskell,如何编写一个类型安全的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 {
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));
}
}
)您可以通过编写一个泛型类
或,在两种类型L
和R
上使用两个构造函数(一个接受L
,一个接受R
)和两个方法L getLeft()
和R getRight())与Haskell建立密切的对应关系
这样它们要么返回构造时传递的值,要么抛出异常。更改您的设计,这样您就不需要这个相当荒谬的功能。对返回值执行的任何操作都需要某种if/else构造。那会非常非常难看
通过快速的谷歌搜索,我觉得Haskell's Orther唯一常用的功能就是错误报告,所以看起来异常实际上是为了纠正替换。我能想到的最接近的方法是在两个值周围都有一个包装器,它可以让你检查设置了哪个值并检索它:
class Either<TLeft, TRight> {
boolean isLeft;
TLeft left;
TRight right;
Either(boolean isLeft, TLeft left1, TRight right) {
isLeft = isLeft;
left = left;
this.right = right;
}
public boolean isLeft() {
return isLeft;
}
public TLeft getLeft() {
if (isLeft()) {
return left;
} else {
throw new RuntimeException();
}
}
public TRight getRight() {
if (!isLeft()) {
return right;
} else {
throw new RuntimeException();
}
}
public static <L, R> Either<L, R> newLeft(L left, Class<R> rightType) {
return new Either<L, R>(true, left, null);
}
public static <L, R> Either<L, R> newRight(Class<L> leftType, R right) {
return new Either<L, R>(false, null, right);
}
}
class Main {
public static void main(String[] args) {
Either<String,Integer> foo;
foo = getString();
foo = getInteger();
}
private static Either<String, Integer> getInteger() {
return Either.newRight(String.class, 123);
}
private static Either<String, Integer> getString() {
return Either.newLeft("abc", Integer.class);
}
}
类{
布尔isLeft;
左撇子;
三权;
或者(布尔值isLeft、TLeft left1、TRight right){
isLeft=isLeft;
左=左;
这个。右=右;
}
公共布尔值isLeft(){
返回isLeft;
}
公共图书馆{
if(isLeft()){
左转;
}否则{
抛出新的RuntimeException();
}
}
公共TRight getRight(){
如果(!isLeft()){
返还权;
}否则{
抛出新的RuntimeException();
}
}
公共静态或newLeft(L left,类rightType){
返回新的(true、left、null);
}
公共静态或newRight(类leftType,R right){
返回新的(false、null、right);
}
}
班长{
公共静态void main(字符串[]args){
要么是福;
foo=getString();
foo=getInteger();
}
私有静态getInteger(){
返回.newRight(String.class,123)之一;
}
私有静态getString(){
返回.newLeft(“abc”,Integer.class);
}
}
重要的是不要试图用一种语言写作,而用另一种语言写作。通常,在Java中,您希望将行为放在对象中,而不是让“脚本”在外部运行,而get方法破坏了封装。这里没有提出这种建议的背景
处理这个小片段的一种安全方法是将其作为回调编写。类似于一个非常简单的访问者
public interface Either {
void string(String value);
void integer(int value);
}
public void either(Either handler, boolean b) throws IntException {
if (b) {
handler.string("test");
} else {
handler.integer(new Integer(1));
}
}
您可能希望使用纯函数实现,并向调用上下文返回一个值
public interface Either<R> {
R string(String value);
R integer(int value);
}
public <R> R either(Either<? extends R> handler, boolean b) throws IntException {
return b ?
handler.string("test") :
handler.integer(new Integer(1));
}
公共接口{
R字符串(字符串值);
R整数(int值);
}
public R other(other既然您已经标记了Scala,我将给出Scala的答案。只需使用现有类即可。下面是一个示例用法:
def whatIsIt(flag: Boolean): Either[Int,String] =
if(flag) Left(123) else Right("hello")
//and then later on...
val x = whatIsIt(true)
x match {
case Left(i) => println("It was an int: " + i)
case Right(s) => println("It was a string: " + s)
}
这是完全类型安全的;您不会有擦除或类似的问题。。。
如果您无法使用Scala,至少可以将此作为一个示例,说明如何实现自己的或类。这里是一个静态检查的类型安全解决方案;这意味着您无法创建运行时错误。请按其意思阅读前一句话。是的,您可以以某种方式引发异常
这很冗长,但嘿,这是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);
}
});
}
}
public class或者托尼·莫里斯
是指向在Functional Java中实现Order
的链接。我的示例中的fold
在那里被称为Order
。它们有一个更复杂的fold
,能够返回一个值(这似乎适合于函数式编程风格).已经提供的建议虽然可行,但并不完整,因为它们依赖于一些null
引用,并有效地将“要么”伪装成一个值元组。不相交和显然是一种或另一种类型
我建议以“s的实现为例。我以类似于Scala的方式用以下方式实现了它。它有点冗长(毕竟是Java:),但它是类型安全的
public interface Choice {
public enum Type {
LEFT, RIGHT
}
public Type getType();
interface Get<T> {
T value();
}
}
public abstract class Either<A, B> implements Choice {
private static class Base<A, B> extends Either<A, B> {
@Override
public Left leftValue() {
throw new UnsupportedOperationException();
}
@Override
public Right rightValue() {
throw new UnsupportedOperationException();
}
@Override
public Type getType() {
throw new UnsupportedOperationException();
}
}
public abstract Left leftValue();
public abstract Right rightValue();
public static <A, B> Either<A, B> left(A value) {
return new Base<A, B>().new Left(value);
}
public static <A, B> Either<A, B> right(B value) {
return new Base<A, B>().new Right(value);
}
public class Left extends Either<A, B> implements Get<A> {
private A value;
public Left(A value) {
this.value = value;
}
@Override
public Type getType() {
return Type.LEFT;
}
@Override
public Left leftValue() {
return Left.this;
}
@Override
public Right rightValue() {
return null;
}
@Override
public A value() {
return value;
}
}
public class Right extends Either<A, B> implements Get<B> {
private B value;
public Right(B value) {
this.value = value;
}
@Override
public Left leftValue() {
return null;
}
@Override
public Right rightValue() {
return this;
}
@Override
public Type getType() {
return Type.RIGHT;
}
@Override
public B value() {
return value;
}
}
}
或者,在使用它而不是异常的典型情况下
public <Error, Result> Either<Error,Result> doSomething() {
// pseudo code
if (ok) {
Result value = ...
return Either.right(value);
} else {
Error errorMsg = ...
return Either.left(errorMsg);
}
}
// somewhere in the code...
Either<Err, Res> result = doSomething();
switch(result.getType()) {
case Choice.Type.LEFT:
// Handle error
Err errorValue = result.leftValue().value();
break;
case Choice.Type.RIGHT:
// Process result
Res resultValue = result.rightValue().value();
break;
}
public或doSomething(){
//伪码
如果(确定){
结果值=。。。
返回其中一个。右(值);
}否则{
错误errorMsg=。。。
返回其中一个.left(errorMsg);
}
}
//在代码的某个地方。。。
任一结果=剂量测定();
开关(result.getType()){
case Choice.Type.LEFT:
//处理错误
errerrorvalue=result.leftValue().value();
打破
case Choice.Type.RIGHT:
//过程结果
Res resultValue=result.rightValue().value();
打破
}
希望有帮助。我模拟代数数据类型的一般公式是:
- 类型是一个抽象基类,构造函数是它的子类
- 每个构造函数的数据在每个子类中定义。(这允许具有不同数量数据的构造函数正确工作。它还消除了维护不变量的需要,例如只有一个变量是非空的或类似的东西)
- 子类的构造函数用于为每个构造函数构造值李>
- 要解构它,可以使用
instanceof
检查构造函数,并向下转换到适当的类型以获取数据
因此,对于或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
}
import junit.framework.TestCase;
public class Test extends TestCase {
public void testSum2() {
assertEquals("Case1(3)", longOrDoubleToString(new Sum2.Case1<>(3L)));
assertEquals("Case2(7.1)", longOrDoubleToString(new Sum2.Case2<>(7.1D)));
}
private String longOrDoubleToString(Sum2<Long, Double> longOrDouble) {
return longOrDouble.match(new Sum2.Matcher<Long, Double, String>() {
public String match1(Long value) {
return "Case1(" + value.toString() + ")";
}
public String match2(Double value) {
return "Case2(" + value.toString() + ")";
}
});
}
}
抽象类{}
类左扩展了它
public abstract class Either<A, B> {
private Either() { } // makes this a safe ADT
public abstract boolean isRight();
public final static class Left<L, R> extends Either<L, R> {
public final L left_value;
public Left(L l) { left_value = l; }
public boolean isRight() { return false; }
}
public final static class Right<L, R> extends Either<L, R> {
public final R right_value;
public Right(R r) { right_value = r; }
public boolean isRight() { return true; }
}
}
public class Either<L, R> {
private L left_value;
private R right_value;
private boolean right;
public L getLeft() {
if(!right) {
return left_value;
} else {
throw new IllegalArgumentException("Left is not initialized");
}
}
public R getRight() {
if(right) {
return right_value;
} else {
throw new IllegalArgumentException("Right is not initialized");
}
}
public boolean isRight() {
return right;
}
public Either(L left_v, Void right_v) {
this.left_value = left_v;
this.right = false;
}
public Either(Void left_v, R right_v) {
this.right_value = right_v;
right = true;
}
}
Either<String, Integer> onlyString = new Either<String, Integer>("string", null);
Either<String, Integer> onlyInt = new Either<String, Integer>(null, new Integer(1));
if(!onlyString.isRight()) {
String s = onlyString.getLeft();
}
import java.util.function.Function;
@Data
public abstract class Either<A, B> {
Either(){}
/**
* The catamorphism for either. Folds over this either breaking into left or right.
*
* @param left The function to call if this is left.
* @param right The function to call if this is right.
* @return The reduced value.
*/
public abstract <X> X either(Function<A, X> left, Function<B, X> right);
}
Either<String, Integer> either1 = Either.ofLeft("foo");
Either<String, Integer> either2 = Either.ofRight(23);
String result1 = either1.join(String::toUpperCase, Object::toString);
String result2 = either2.join(String::toUpperCase, Object::toString);
<dependency>
<groupId>com.codepoetics</groupId>
<artifactId>ambivalence</artifactId>
<version>0.2</version>
</dependency>
import junit.framework.TestCase;
public class Test extends TestCase {
public void testSum2() {
assertEquals("Case1(3)", longOrDoubleToString(new Sum2.Case1<>(3L)));
assertEquals("Case2(7.1)", longOrDoubleToString(new Sum2.Case2<>(7.1D)));
}
private String longOrDoubleToString(Sum2<Long, Double> longOrDouble) {
return longOrDouble.match(new Sum2.Matcher<Long, Double, String>() {
public String match1(Long value) {
return "Case1(" + value.toString() + ")";
}
public String match2(Double value) {
return "Case2(" + value.toString() + ")";
}
});
}
}