The account problem in Java and
            Clojure




          Alf Kristian Støyle
public class Account {

     private long balance;
      private final int accountNo;


    public void debit(long debitAmount) {

    
     this.balance -= debitAmount;

    }


    public void credit(long creditAmount) {

    
     this.balance += creditAmount;

    }


    public long getBalance() {

    
     return this.balance;

    }


    public void getAccountNo() {

    
     return this.accountNo;

    }
}
public class Account {

     private volatile long balance;
      private final int accountNo;


    public synchronized void debit(long debitAmount) {

    
     this.balance -= debitAmount;

    }


    public synchronized void credit(long creditAmount) {

    
     this.balance += creditAmount;

    }


    public synchronized long getBalance() {

    
     return this.balance;

    }


    public void getAccountNo() {

    
     return this.accountNo;

    }
}
public void transfer(Account fromAccount, Account toAccount,
                     long amount) throws Exception {

    if (fromAccount.getBalance() < amount) {
        throw new Exception("Not enough money!");
    }
    fromAccount.debit(amount);
    toAccount.credit(amount);
}
public void transfer(Account fromAccount, Account toAccount,
                     long amount) throws Exception {

    synchronized (fromAccount) {
        synchronized (toAccount) {

           if (fromAccount.getBalance() < amount) {
               throw new Exception("Not enough money!");
  
         }
  
         fromAccount.debit(amount);

           toAccount.credit(amount);

       }
    }
}
public void transfer(Account fromAccount, Account toAccount,
                     long amount) throws Exception {

    Object mutex1 = toAccount;
    Object mutex2 = fromAccount;
    if (fromAccount.getAccountNo() > toAccount.getAccountNo()) {
        mutex1 = fromAccount;
        mutex2 = toAccount;
    }
    synchronized (mutex1) {
        synchronized (mutex2) {
            if (fromAccount.getBalance() < amount) {
                throw new Exception("Not enough money!");
            }
            fromAccount.debit(amount);
            toAccount.credit(amount);
        }
    }
}
public class Account {
             private volatile long balance;
             private final int accountNo;

             public synchronized void debit(long debitAmount) {
                 this.balance -= debitAmount;
             }
         
             public synchronized void credit(long creditAmount) {
                 this.balance += creditAmount;
             }

             public synchronized long getBalance() {
                 return this.balance;
             }

             public void getAccountNo() {
                 return this.accountNo;
             }
         }
public void transfer(Account fromAccount, Account toAccount,
                     long amount) throws Exception {

    Object mutex1 = toAccount;
    Object mutex2 = fromAccount;
    if (fromAccount.getAccountNo() > toAccount.getAccountNo()) {
        mutex1 = fromAccount;
        mutex2 = toAccount;
    }
    synchronized (mutex1) {
        synchronized (mutex2) {
            if (fromAccount.getBalance() < amount) {
                throw new Exception("Not enough money!");
            }
            fromAccount.debit(amount);
            toAccount.credit(amount);
        }
    }
}
Clojure
Clojure
• Pure functional
Clojure
• Pure functional
• Managed references
 • Ref
 • ...
Clojure
• Pure functional
• Managed references
 • Ref
 • ...
• Software transactional memory
(defn transfer [from-account to-account amount]
  (dosync
    (if (> amount @from-account)
      (throw (Exception. "Not enough money!")))
    (alter from-account - amount)
    (alter to-account + amount)))
(defn transfer [from-account to-account amount]
  (dosync
    (if (> amount @from-account)
      (throw (Exception. "Not enough money!")))
    (alter from-account - amount)
    (alter to-account + amount)))




   OMG it’s a Lisp!
(defn transfer [from-account to-account amount]
  (dosync
    (if (> amount @from-account)
      (throw (Exception. "Not enough money!")))
    (alter from-account - amount)
    (alter to-account + amount)))
(defn transfer [from-account to-account amount]
  (dosync
    (if (> amount @from-account)
      (throw (Exception. "Not enough money!")))
    (alter from-account - amount)
    (alter to-account + amount)))


(def account-a (ref 1000))
(def account-b (ref 800))
(defn transfer [from-account to-account amount]
  (dosync
    (if (> amount @from-account)
      (throw (Exception. "Not enough money!")))
    (alter from-account - amount)
    (alter to-account + amount)))


(def account-a (ref 1000))
(def account-b (ref 800))


(transfer account-a account-b 300)

@account-a
=> 700
(defn transfer [from-account to-account amount]
  (dosync
    (if (> amount @from-account)
      (throw (Exception. "Not enough money!")))
    (alter from-account - amount)
    (alter to-account + amount)))


(def account-a (ref 1000))
(def account-b (ref 800))


(alter account-a - 100)
=> java.lang.IllegalStateException:
   No transaction running
The pragmatic programmers




“Learn at least one new language every year”

The account problem in Java and Clojure

  • 1.
    The account problemin Java and Clojure Alf Kristian Støyle
  • 2.
    public class Account{ private long balance; private final int accountNo; public void debit(long debitAmount) { this.balance -= debitAmount; } public void credit(long creditAmount) { this.balance += creditAmount; } public long getBalance() { return this.balance; } public void getAccountNo() { return this.accountNo; } }
  • 3.
    public class Account{ private volatile long balance; private final int accountNo; public synchronized void debit(long debitAmount) { this.balance -= debitAmount; } public synchronized void credit(long creditAmount) { this.balance += creditAmount; } public synchronized long getBalance() { return this.balance; } public void getAccountNo() { return this.accountNo; } }
  • 4.
    public void transfer(AccountfromAccount, Account toAccount, long amount) throws Exception { if (fromAccount.getBalance() < amount) { throw new Exception("Not enough money!"); } fromAccount.debit(amount); toAccount.credit(amount); }
  • 5.
    public void transfer(AccountfromAccount, Account toAccount, long amount) throws Exception { synchronized (fromAccount) { synchronized (toAccount) { if (fromAccount.getBalance() < amount) { throw new Exception("Not enough money!"); } fromAccount.debit(amount); toAccount.credit(amount); } } }
  • 6.
    public void transfer(AccountfromAccount, Account toAccount, long amount) throws Exception { Object mutex1 = toAccount; Object mutex2 = fromAccount; if (fromAccount.getAccountNo() > toAccount.getAccountNo()) { mutex1 = fromAccount; mutex2 = toAccount; } synchronized (mutex1) { synchronized (mutex2) { if (fromAccount.getBalance() < amount) { throw new Exception("Not enough money!"); } fromAccount.debit(amount); toAccount.credit(amount); } } }
  • 7.
    public class Account{ private volatile long balance; private final int accountNo; public synchronized void debit(long debitAmount) { this.balance -= debitAmount; } public synchronized void credit(long creditAmount) { this.balance += creditAmount; } public synchronized long getBalance() { return this.balance; } public void getAccountNo() { return this.accountNo; } } public void transfer(Account fromAccount, Account toAccount, long amount) throws Exception { Object mutex1 = toAccount; Object mutex2 = fromAccount; if (fromAccount.getAccountNo() > toAccount.getAccountNo()) { mutex1 = fromAccount; mutex2 = toAccount; } synchronized (mutex1) { synchronized (mutex2) { if (fromAccount.getBalance() < amount) { throw new Exception("Not enough money!"); } fromAccount.debit(amount); toAccount.credit(amount); } } }
  • 8.
  • 9.
  • 10.
    Clojure • Pure functional •Managed references • Ref • ...
  • 11.
    Clojure • Pure functional •Managed references • Ref • ... • Software transactional memory
  • 12.
    (defn transfer [from-accountto-account amount] (dosync (if (> amount @from-account) (throw (Exception. "Not enough money!"))) (alter from-account - amount) (alter to-account + amount)))
  • 13.
    (defn transfer [from-accountto-account amount] (dosync (if (> amount @from-account) (throw (Exception. "Not enough money!"))) (alter from-account - amount) (alter to-account + amount))) OMG it’s a Lisp!
  • 14.
    (defn transfer [from-accountto-account amount] (dosync (if (> amount @from-account) (throw (Exception. "Not enough money!"))) (alter from-account - amount) (alter to-account + amount)))
  • 15.
    (defn transfer [from-accountto-account amount] (dosync (if (> amount @from-account) (throw (Exception. "Not enough money!"))) (alter from-account - amount) (alter to-account + amount))) (def account-a (ref 1000)) (def account-b (ref 800))
  • 16.
    (defn transfer [from-accountto-account amount] (dosync (if (> amount @from-account) (throw (Exception. "Not enough money!"))) (alter from-account - amount) (alter to-account + amount))) (def account-a (ref 1000)) (def account-b (ref 800)) (transfer account-a account-b 300) @account-a => 700
  • 17.
    (defn transfer [from-accountto-account amount] (dosync (if (> amount @from-account) (throw (Exception. "Not enough money!"))) (alter from-account - amount) (alter to-account + amount))) (def account-a (ref 1000)) (def account-b (ref 800)) (alter account-a - 100) => java.lang.IllegalStateException: No transaction running
  • 18.
    The pragmatic programmers “Learnat least one new language every year”

Editor's Notes

  • #8 * One mutable field -&gt; real app * Reason about -&gt; honestly * Difficult test * Inherently difficult with locks
  • #9 everything is immutable. -&gt; List
  • #10 everything is immutable. -&gt; List
  • #11 everything is immutable. -&gt; List
  • #12 Claim a lot simpler than the Java version * easier to reason * easier to test -&gt; no deadlocking
  • #13 Claim a lot simpler than the Java version * easier to reason * easier to test -&gt; no deadlocking
  • #14 Claim a lot simpler than the Java version * easier to reason * easier to test -&gt; no deadlocking
  • #15 Claim a lot simpler than the Java version * easier to reason * easier to test -&gt; no deadlocking
  • #16 Claim a lot simpler than the Java version * easier to reason * easier to test -&gt; no deadlocking
  • #17 Claim a lot simpler than the Java version * easier to reason * easier to test -&gt; no deadlocking
  • #18 Good reason to look at Clojure.