Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Haskell datatype to Java(OO)

Tags:

java

haskell

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 ?

like image 444
Anton Smith Avatar asked Feb 29 '12 18:02

Anton Smith


1 Answers

Let's copy the code as closely as we can.

Here are some properties that the Haskell data structure has:

  1. It has the type Expr and two constructors Lit and Add
  2. You cannot add or remove constructors from the "outside"

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.

like image 159
dflemstr Avatar answered Oct 10 '22 00:10

dflemstr