I'm trying to convert a simple Haskell datatype and a function to OO. But I'm confused..
Have the following Haskell type for arithmetic calculation:
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 ++ ")"
Now I'm trying to convert..
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());
  }
}
This will result in "(2+3)", it's correct answer.
But.. I'm not sure.. is this the right way to think about it and model it in OO ?
Is't a good representation of the Haskell datatype ?
Let's copy the code as closely as we can.
Here are some properties that the Haskell data structure has:
Expr and two constructors Lit and Add
So, if we want these properties to stay true in the Java version, you should do it like this:
public abstract class Expr {
    // So that you can't add more subclasses outside this block
    private Expr() {}
    // Simulate pattern matching:
    // (This CAN be done with instanceof, but that's ugly and not OO)
    public boolean isLit() {
        return false;
    }
    public boolean isAdd() {
        return false;
    }
    public Lit asLit() {
        throw new UnsupportedOperationException("This is not a Lit");
    }
    public Add asAdd() {
        throw new UnsupportedOperationException("This is not an Add");
    }
    public static class Lit extends Expr {
        public final int n;
        public Lit(int n) {
            this.n = n;
        }
        @Override
        public boolean isLit() {
            return true;
        }
        @Override
        public Lit asLit() {
            return this;
        }
    }
    public static class Add extends Expr {
        public final Expr a, b;
        public Lit(Expr a, Expr b) {
            this.a = a;
            this.b = b;
        }
        @Override
        public boolean isAdd() {
            return true;
        }
        @Override
        public Add asAdd() {
            return this;
        }
    }
}
Now, to convert showExpr:
public static String showExpr(final Expr expr) {
    if(expr.isLit()) {
        return Integer.toString(expr.asLit().n);
    } else if(expr.isAdd()) {
        return "(" + expr.asAdd().a + "+" + expr.asAdd().b + ")";
    }
}
You can put showExpr as a static method in the Expr class. I would not make it an instance method, because that strays further from the Haskell version.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With