Skip to content

Instantly share code, notes, and snippets.

@SegFaultAX
Last active May 15, 2018 15:56
Show Gist options
  • Save SegFaultAX/553af96cf7e1272039a97cf9e59c88ec to your computer and use it in GitHub Desktop.
Save SegFaultAX/553af96cf7e1272039a97cf9e59c88ec to your computer and use it in GitHub Desktop.
Pattern matching via `Coproduct<A, B>` [Java]
package com.mkbernard.functional.examples;
import java.util.function.Function;
public abstract class Coproduct<A, B> {
public abstract boolean isLeft();
public abstract boolean isRight();
public abstract Left<A, B> left();
public abstract Right<A, B> right();
public abstract <T> T match(Function<Left<A, B>, T> ifLeft, Function<Right<A, B>, T> ifRight);
public static <A, B> Coproduct<A, B> left(A value) {
return new Left<>(value);
}
public static <A, B> Coproduct<A, B> right(B value) {
return new Right<>(value);
}
public static final class Left<A, B> extends Coproduct<A, B> {
private final A value;
Left(A value) {
this.value = value;
}
public A get() {
return value;
}
@Override
public boolean isLeft() {
return true;
}
@Override
public boolean isRight() {
return false;
}
@Override
public Left<A, B> left() {
return this;
}
@Override
public Right<A, B> right() {
throw new IllegalStateException("not a right-value: " + value);
}
@Override
public <T> T match(Function<Left<A, B>, T> ifLeft, Function<Right<A, B>, T> ifRight) {
return ifLeft.apply(this);
}
}
public static class Right<A, B> extends Coproduct<A, B> {
private final B value;
Right(B value) {
this.value = value;
}
public B get() {
return value;
}
@Override
public boolean isLeft() {
return false;
}
@Override
public boolean isRight() {
return true;
}
@Override
public Left<A, B> left() {
throw new IllegalStateException("not a left-value: " + value);
}
@Override
public Right<A, B> right() {
return this;
}
@Override
public <T> T match(Function<Left<A, B>, T> ifLeft, Function<Right<A, B>, T> ifRight) {
return ifRight.apply(this);
}
}
public abstract static class Expr {
public abstract Coproduct<Val, Coproduct<Add, Mul>> toEither();
public static Expr val(int v) {
return new Val(v);
}
public static Expr add(Expr a, Expr b) {
return new Add(a, b);
}
public static Expr mul(Expr a, Expr b) {
return new Mul(a, b);
}
public static class Val extends Expr {
private final int value;
public Val(int value) {
this.value = value;
}
public int getValue() {
return value;
}
@Override
public Coproduct<Val, Coproduct<Add, Mul>> toEither() {
return Coproduct.left(this);
}
}
public static class Add extends Expr {
private final Expr a;
private final Expr b;
public Add(Expr a, Expr b) {
this.a = a;
this.b = b;
}
public Expr getA() {
return a;
}
public Expr getB() {
return b;
}
@Override
public Coproduct<Val, Coproduct<Add, Mul>> toEither() {
return Coproduct.right(Coproduct.left(this));
}
}
public static class Mul extends Expr {
private final Expr a;
private final Expr b;
public Mul(Expr a, Expr b) {
this.a = a;
this.b = b;
}
public Expr getA() {
return a;
}
public Expr getB() {
return b;
}
@Override
public Coproduct<Val, Coproduct<Add, Mul>> toEither() {
return Coproduct.right(Coproduct.right(this));
}
}
}
public static int eval(Expr e) {
Coproduct<Expr.Val, Coproduct<Expr.Add, Expr.Mul>> v = e.toEither();
if (v.isLeft()) {
return v.left().get().getValue();
} else {
Coproduct<Expr.Add, Expr.Mul> v1 = v.right().get();
if (v1.isLeft()) {
Expr.Add add = v1.left().get();
return eval(add.getA()) + eval(add.getB());
} else {
Expr.Mul mul = v1.right().get();
return eval(mul.getA()) * eval(mul.getB());
}
}
}
public static void main(String[] args) {
Expr e1 = Expr.val(1);
Expr e2 = Expr.add(Expr.val(1), Expr.val(2));
Expr e3 = Expr.mul(Expr.val(1), Expr.val(2));
Expr e4 = Expr.mul(Expr.add(Expr.val(2), Expr.val(4)), Expr.val(10));
Expr e5 = Expr.mul(Expr.add(e1, e2), e4);
System.out.println(eval(e5));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment