-
-
Save puffnfresh/8a6d8f60379a54c139432b660f35ba5a to your computer and use it in GitHub Desktop.
Java6-compatible algebraic data types via Church-Scott Encoding
This file contains hidden or 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 hidden or 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
import fj.*; | |
/* | |
* 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 interface Payment { | |
public <A> A fold( | |
F2<Customer, Double, A> cash, | |
F3<Customer, Double, Integer, A> credit, | |
F4<Customer, Double, Integer, Integer, A> check | |
); | |
} | |
public static Payment cash(Customer customer, double amount) { | |
return new Payment() { | |
public <A> A fold( | |
F2<Customer, Double, A> cash_, | |
F3<Customer, Double, Integer, A> _credit, | |
F4<Customer, Double, Integer, Integer, A> _check | |
) { | |
return cash_.f(customer, amount); | |
} | |
}; | |
} | |
public static double paymentAmount(Payment payment) { | |
return payment.fold( | |
new F2<Customer, Double, Double>() { | |
public Double f(Customer customer, Double amount) { | |
return amount; | |
} | |
}, | |
new F3<Customer, Double, Integer, Double>() { | |
public Double f(Customer customer, Double amount, Integer cardNumber) { | |
return amount; | |
} | |
}, | |
new F4<Customer, Double, Integer, Integer, Double>() { | |
public Double f( Customer customer, | |
Double amount, | |
Integer routingNumber, | |
Integer accountNumber ) { | |
return amount; | |
} | |
} | |
); | |
} | |
public static String paymentToString(Payment payment) { | |
return payment.fold( | |
new F2<Customer, Double, String>() { | |
public String f(Customer customer, Double amount) { | |
return String.format( | |
"Cash {customer = %s, amount = %f}", | |
customer.toString(), | |
amount | |
); | |
} | |
}, | |
new F3<Customer, Double, Integer, String>() { | |
public String f(Customer customer, Double amount, Integer cardNumber) { | |
return String.format( | |
"Credit {customer = %s, amount = %f, cardNumber = %d}", | |
customer.toString(), | |
amount, | |
cardNumber | |
); | |
} | |
}, | |
new F4<Customer, Double, Integer, Integer, String>() { | |
public String f( Customer customer, | |
Double amount, | |
Integer routingNumber, | |
Integer accountNumber ) { | |
return String.format( | |
"Check {customer = %s, amount = %f, routingNumber = %d, accountNumber = %d}", | |
customer.toString(), | |
amount, | |
routingNumber, | |
accountNumber | |
); | |
} | |
} | |
); | |
} | |
/* 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 = cash(daniel, 100.0); | |
System.out.println(String.format( | |
"beersAtKingsHeadPub == %s", | |
paymentToString(beersAtKingsHeadPub) | |
)); | |
System.out.println(String.format( | |
"The payment method was %s, the amount was %f", | |
beersAtKingsHeadPub.fold( | |
new F2<Customer, Double, String>() { | |
public String f(Customer customer, Double amount) { | |
return "cash"; | |
} | |
}, | |
new F3<Customer, Double, Integer, String>() { | |
public String f(Customer customer, Double amount, Integer cardNumber) { | |
return "credit"; | |
} | |
}, | |
new F4<Customer, Double, Integer, Integer, String>() { | |
public String f( Customer customer, | |
Double amount, | |
Integer routingNumber, | |
Integer accountNumber ) { | |
return "check"; | |
} | |
} | |
), | |
paymentAmount(beersAtKingsHeadPub) | |
)); | |
} | |
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