Skip to content

Instantly share code, notes, and snippets.

@friedbrice
Last active April 7, 2019 16:47
Show Gist options
  • Save friedbrice/97c1bd2fc08a9349f5b126f190e94600 to your computer and use it in GitHub Desktop.
Save friedbrice/97c1bd2fc08a9349f5b126f190e94600 to your computer and use it in GitHub Desktop.
Java6-compatible algebraic data types via Church-Scott Encoding
module Payments where
data Customer = Customer { name :: String, age :: Int } deriving (Eq, Ord, Show)
-- I know partial record fields is an anti-pattern, but who's counting?
data Payment
= Cash { customer :: Customer, amount :: Double }
| Credit { customer :: Customer, amount :: Double, cardNumber :: Int }
| Check { customer :: Customer, amount :: Double, routingNumber :: Int, accountNumber :: Int }
deriving (Eq, Ord, Show)
main = do
putStrLn "Hello, World!"
let daniel = Customer "Daniel" 34
let danielToo = Customer "Daniel" 34
let notDaniel = Customer "David" 31
putStrLn $ "daniel = " <> show daniel
putStrLn $ "daniel = danielToo ? " <> show (daniel == danielToo)
putStrLn $ "daniel = notDaniel ? " <> show (daniel == notDaniel)
let beersAtKingsHeadPub = Cash daniel 100
putStrLn $ "beersAtKingsHeadPub = " <> show beersAtKingsHeadPub
putStrLn $
(\x y -> "The payment method was " <> x <> " and the amount was " <> show y)
(case beersAtKingsHeadPub of
Cash _ _ -> "cash"
Credit _ _ _ -> "credit"
Check _ _ _ _ -> "check"
)
(amount beersAtKingsHeadPub)
/*
* module Payments where
*
*/
final class Payments {
/*
* data Customer = Customer { name :: String, age :: Int } deriving (Eq, Ord, Show)
*/
public static final class Customer {
public final String name;
public final int age;
public Customer(String name, int age) {
this.name = name;
this.age = age;
}
@Override public String toString() {
return String.format("Customer {name = \"%s\", age = %d}", name, age);
}
@Override public int hashCode() {
return toString().hashCode();
}
@Override public boolean equals(Object o) {
return o instanceof Customer && o.toString().equals(toString());
}
}
/*
* -- I know partial record fields is an anti-pattern, but who's counting?
* data Payment
* = Cash { customer :: Customer, amount :: Double }
* | Credit { customer :: Customer, amount :: Double, cardNumber :: Int }
* | Check { customer :: Customer, amount :: Double, routingNumber :: Int, accountNumber :: Int }
* deriving (Eq, Ord, Show)
*/
public static final class Payment {
public final Customer customer;
public final double amount;
private final int cardNumber;
private final int routingNumber;
private final int accountNumber;
private final Tag tag;
private enum Tag {
Cash, Credit, Check;
}
private Payment( Customer customer,
double amount,
int cardNumber,
int routingNumber,
int accountNumber,
Tag tag ) {
this.customer = customer;
this.amount = amount;
this.cardNumber = cardNumber;
this.routingNumber = routingNumber;
this.accountNumber = accountNumber;
this.tag = tag;
}
public static Payment cash(Customer customer, double amount) {
return new Payment(customer, amount, 0, 0, 0, Tag.Cash);
}
public static Payment credit(Customer customer, double amount, int cardNumber) {
return new Payment(customer, amount, cardNumber, 0, 0, Tag.Credit);
}
public static Payment check( Customer customer,
double amount,
int routingNumber,
int accountNumber ) {
return new Payment(customer,amount,0,routingNumber,accountNumber,Tag.Check);
}
public static interface Cases<A> {
A cash(Customer customer, double amount);
A credit(Customer customer, double amount, int cardNumber);
A check(Customer customer,double amount,int routingNumber,int accountNumber);
}
public <A> A match(Cases<A> cases) {
switch(tag) {
case Cash: return cases.cash(customer, amount);
case Credit: return cases.credit(customer, amount, cardNumber);
case Check: return cases.check(customer,amount,routingNumber,accountNumber);
}
return panic("Incomplete pattern match");
}
@Override public String toString() {
return match(new Cases<String>() {
public String cash(Customer customer, double amount) {
return String.format(
"Cash {customer = %s, amount = %f}",
customer.toString(),
amount
);
}
public String credit(Customer customer, double amount, int cardNumber) {
return String.format(
"Credit {customer = %s, amount = %f, cardNumber = %d}",
customer.toString(),
amount,
cardNumber
);
}
public String check( Customer customer,
double amount,
int routingNumber,
int cardNumber ) {
return String.format(
"Check {customer = %s, amount = %f, routingNumber = %d, accountNumber = %d}",
customer.toString(),
amount,
routingNumber,
accountNumber
);
}
});
}
@Override public int hashCode() {
return toString().hashCode();
}
@Override public boolean equals(Object o) {
return o instanceof Payment && o.toString().equals(toString());
}
}
/* main = do
* putStrLn "Hello, World!"
*
* let daniel = Customer "Daniel" 34
* let danielToo = Customer "Daniel" 34
* let notDaniel = Customer "David" 31
*
* putStrLn $ "daniel = " <> show daniel
* putStrLn $ "daniel = danielToo ? " <> show (daniel == danielToo)
* putStrLn $ "daniel = notDaniel ? " <> show (daniel == notDaniel)
*
* let beersAtKingsHeadPub = Cash daniel 100
*
* putStrLn $ "beersAtKingsHeadPub = " <> show beersAtKingsHeadPub
*
* putStrLn $
* (\x y -> "The payment method was " <> x <> " and the amount was " <> show y)
* (case beersAtKingsHeadPub of
* Cash _ _ -> "cash"
* Credit _ _ _ -> "credit"
* Check _ _ _ _ -> "check"
* )
* (amount beersAtKingsHeadPub)
*/
public static void main(String[] args) {
System.out.println("Hello, World!");
Customer daniel = new Customer("Daniel", 34);
Customer danielToo = new Customer("Daniel", 34);
Customer notDaniel = new Customer("David", 31);
System.out.println(String.format(
"daniel = %s",
daniel.toString()
));
System.out.println(String.format(
"daniel == danielToo = %s",
daniel.equals(danielToo)
));
System.out.println(String.format(
"daniel == notDaniel = %s",
daniel.equals(notDaniel)
));
Payment beersAtKingsHeadPub = Payment.cash(daniel, 100.0);
System.out.println(String.format(
"beersAtKingsHeadPub == %s",
beersAtKingsHeadPub.toString()
));
System.out.println(String.format(
"The payment method was %s, the amount was %f",
beersAtKingsHeadPub.match(new Payment.Cases<String>() {
public String cash(Customer customer, double amount) {
return "cash";
}
public String credit(Customer customer, double amount, int cardNumber) {
return "credit";
}
public String check( Customer customer,
double amount,
int routingNumber,
int accountNumber ) {
return "check";
}
}),
beersAtKingsHeadPub.amount
));
}
private static <A> A panic(String msg) {
RuntimeException err = new RuntimeException(msg);
err.printStackTrace();
System.exit(1);
throw err;
}
private Payments() {
panic("Payments is a module and should not be instantiated.");
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment