Haskell数据类型到Java(OO)
我正在尝试将一个简单的Haskell数据类型和一个函数转换为OO。 但是我很困惑 具有以下用于算术计算的Haskell类型: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
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
公共抽象类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());
}
}
在HaskellLit
和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
)。我可能也会