Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/314.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell数据类型到Java(OO)_Java_Haskell - Fatal编程技术网

Haskell数据类型到Java(OO)

Haskell数据类型到Java(OO),java,haskell,Java,Haskell,我正在尝试将一个简单的Haskell数据类型和一个函数转换为OO。 但是我很困惑 具有以下用于算术计算的Haskell类型: data Expr = Lit Int | Add Expr Expr | deriving Show --Turn the expr to a nice string showExpr :: Expr -> String showExpr (Lit n) = show n showExpr (Add e1 e2) = "(" ++ show

我正在尝试将一个简单的Haskell数据类型和一个函数转换为OO。 但是我很困惑

具有以下用于算术计算的Haskell类型:

data Expr = Lit Int | 
      Add Expr Expr |
   deriving Show

--Turn the expr to a nice string
showExpr :: Expr -> String 
showExpr (Lit n) = show n
showExpr (Add e1 e2) = "(" ++ showExpr e1 ++ "+" ++ showExpr e2 ++ ")"
现在我正试着改变

public interface Expr {
  String showExpr(String n);
} 

// Base case
public class Lit implements Expr {
  String n;

  public Lit(String n) {
    this.n = n;
  }

  @Override
  public String ShowExpr() {
    return n;
  }
}

public class Add implements Expr {
  Expr a;
  Expr b;

  public Add(Expr aExpr, Expr bExpr) {
    this.a = aExpr;
    this.b = bExpr;
  }

  public String ShowExpr() {
    return "(" + a.ShowExpr() + "+" + b.ShowExpr() + ")";
  }

  public static void main(String[] args) {
    Lit firstLit  = new Lit("2");
    Lit secLit = new Lit("3");
    Add add = new Add(firstLit,secLit);
    System.out.println(add.ShowExpr());
  }
}
这将导致“(2+3)”,这是正确的答案

但是。。我不确定。。这是在OO中思考和建模它的正确方式吗


这不是Haskell数据类型的良好表示吗

让我们尽可能地复制代码

以下是Haskell数据结构的一些属性:

  • 它有类型
    Expr
    和两个构造函数
    Lit
    Add
  • 不能从“外部”添加或删除构造函数
  • 因此,如果我们希望这些属性在Java版本中保持为真,您应该这样做:

    公共抽象类Expr{
    //所以你不能在这个块之外添加更多的子类
    私有表达式(){}
    //模拟模式匹配:
    //(这可以通过instanceof完成,但这很难看,而且不是OO)
    公共布尔isLit(){
    返回false;
    }
    公共布尔值isAdd(){
    返回false;
    }
    公共照明{
    抛出新的UnsupportedOperationException(“这不是一个Lit”);
    }
    公共添加asAdd(){
    抛出新的UnsupportedOperationException(“这不是添加”);
    }
    公共静态类Lit扩展Expr{
    公共最终int n;
    公共照明(int n){
    这个,n=n;
    }
    @凌驾
    公共布尔isLit(){
    返回true;
    }
    @凌驾
    公共照明{
    归还这个;
    }
    }
    公共静态类Add扩展Expr{
    公开期末考试a、b;
    公共照明(出口a、出口b){
    这个a=a;
    这个.b=b;
    }
    @凌驾
    公共布尔值isAdd(){
    返回true;
    }
    @凌驾
    公共添加asAdd(){
    归还这个;
    }
    }
    }
    
    现在,要转换
    showExpr

    公共静态字符串showExpr(最终Expr){
    if(expr.isLit()){
    返回整数.toString(expr.asLit().n);
    }else if(expr.isAdd()){
    返回“(“+expr.asAdd().a+”+“+expr.asAdd().b+”)”;
    }
    }
    

    您可以将
    showExpr
    作为静态方法放在
    Expr
    类中。我不会让它成为一个实例方法,因为它离Haskell版本更远。

    我会稍微改变一下:每个Haskell类都变成一个Java接口,
    实例…其中
    (或
    派生
    )变成
    实现

    所以,
    类显示a
    成为

    interface Showable {
        String show();
    }
    
    interface Expr extends Showable {}
    
    那么,
    Expr
    数据类型做什么呢?它将各种Haskell构造函数(又名Java类)组合成一个单一类型,并表示它们都包含在Haskell类(即实现Java接口)中。因此,我会说数据类型
    Expr
    变为

    interface Showable {
        String show();
    }
    
    interface Expr extends Showable {}
    
    例如,
    Lit
    构造函数变成:

    class Lit implements Expr {
        @Override
        String show() {
            ...
        }
    }
    
    现在,您可以拥有包含
    Lit
    Add
    实例(例如
    List
    )的Java集合,并且对于任何
    Expr
    ,您都知道
    实现了可显示的

    请注意,通过将
    Showable
    作为自己的接口(而不是像您那样将其方法直接放在
    Expr
    上),我允许其他完全无关的Java类也实现它。这类似于任何人如何定义
    实例。。。其中
    为您定义的任何Haskell类

    最后,这里的开放世界和封闭世界政策之间存在一些不匹配。Haskell的
    实例。。。其中
    是开放的世界,而Java的
    实现
    是封闭的。但是Haskell的每种类型的构造函数都是封闭的,而Java的
    扩展
    大多是开放的

    关于关闭
    实现
    你没什么办法,但是你可以用一个抽象类和一个私有构造函数关闭
    扩展
    ,所以这就是我要做的(如果你想要那种行为的话)。稍微调整一下上述内容,我们得到:

    public abstract class Expr implements Showable {
        private Expr() {
            // hide the ctor from the outside world, so nobody else
            // can extend this class
        }
    
        public static class Lit extends Expr {
            ...
        }
    
        public static class Add extends Expr {
            ...
        }
    }
    

    您的代码看起来不错,但可能会更地道一点。例如,可以使用
    final
    使字段不可变。您还可以将
    showExp
    替换为
    toString
    (在
    Object
    中声明),除非希望
    toString
    执行其他操作。在我看来,使用字符串比连接几个字符串要简洁一些。最后,我会让
    Lit
    存储一个
    int
    ,因为它更紧凑,类型更安全

    以下是我将如何翻译它:

    abstract class Exp {
      // Force subclasses to override Object.toString.
      public abstract String toString();
    }
    
    class Lit extends Exp {
      final int value;
    
      Lit(int value) {
        this.value = value;
      }
    
      public String toString() {
        return Integer.toString(value);
      }
    }
    
    class Add extends Exp {
      final Exp a, b;
    
      Add(Exp a, Exp b) {
        this.a = a;
        this.b = b;
      }
    
      public String toString() {
        return String.format("(%s + %s)", a, b);
      }
    }
    
    class Main {
      public static void main(String[] args) {
        Lit firstLit = new Lit(2);
        Lit secLit = new Lit(3);
        Add add = new Add(firstLit, secLit);
        System.out.println(add);
      }
    }
    

    我会这样翻译:

    public abstract class Expr {
    
        public static Expr Lit(final int n) {
            return new Expr() {
                public String showExpr() {
                    return "" + n;
                }
            };
        }
    
        public static Expr Add(final Expr e1, final Expr e2) {
            return new Expr() {
                public String showExpr() {
                    return String.format("(%s+%s)", e1.showExpr(), e2.showExpr());
                }
            };
        }
    
        public abstract String showExpr();
    
        public static void main(String[] args) {
            Expr firstLit = Lit(2);
            Expr secLit = Lit(3);
            Expr add = Add(firstLit, secLit);
            System.out.println(add.showExpr());
        }
    }
    

    在Haskell
    Lit
    Add
    中没有子类型(Haskell中没有这样的东西),只有
    Expr
    会话。不需要在Java中公开子类,这就是为什么我可以使用隐藏在某些方法中的匿名类的原因。只要您不需要模式匹配(这在Java中很难建模,因为简单的
    instanceof
    test很快就会被更复杂的示例弄得一团糟),这就很好了。

    我认为您的转换非常好。我唯一能说明的是Expr应该是一个抽象类而不是一个接口。不要用java解决这类问题,因为haskell在这方面做得更好。我看不出
    if(e.isLit()){Lit l=e.asLit();}
    if(e instanceof Lit){Lit l=(Lit)e;}
    之间有什么真正的区别。Haskell样式的模式匹配本质上不是面向对象的,因此试图用模拟
    instanceof
    和强制转换的方法来隐藏这一事实对代码没有任何影响。@yshavit,你当然是对的,但我认为我所介绍的样式使代码更易于阅读,并且减少了像
    if(foo instanceof foo){((Bar)foo).bla();}由于重构(
    Foo!=Bar
    )。我可能也会