Last active
April 7, 2019 16:47
-
-
Save friedbrice/97c1bd2fc08a9349f5b126f190e94600 to your computer and use it in GitHub Desktop.
Java6-compatible algebraic data types via Church-Scott Encoding
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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