Wednesday, December 21, 2011

Amit Rathore's Introduction to Clojure: Week 2


This week we learned about Clojure's multimethods, which is Chapter 4 in Amit's book Clojure in Action. In this chapter we learn about polymorphism, the use of single dispatch in other languages, and the concept of multiple dispatch and then Clojure's multimethods.

So after reading the chapter I felt I needed to get really grounded with polymorphism. The book refers to the Cardelli/Wegner paper, linked below, but I also found this article by Dan Umbarger as a great way to get my mind in gear for thinking about types and polymorphism which connected new synapses readying me to learn more about the concept of dispatching.

Then I decided to look into the visitor pattern some more and see if I could get a better understanding of why you might use dispatch in a program. I found these papers and articles contained helpful information.

After reading all this I appreciate the simplicity with which Clojure's multimethods handle multiple dispatch, but I'll admit, I'm not sure when to make the design decision to implement them, so I've still got a little more learning to do.

This week Amit had us implement a fantasy banking system with some crazy specs and his example code is posted on Pastie.org. When I have time I'll come back to this post to dissect his code and get a better understanding of the code.

While I was able to implement my own version of a half-solution this week, I still did not complete the assignment and had a lot of difficulties. It definitely was easier than the first assignment though as I'm getting more familiar with Clojure. Here is what I've come up with thus far:

(def coin [:heads :tails])
(def acct-type [:checking :savings :moneymarket])
(def overdraft-acct {:overdraft {:balance 100000 :type :overdraft}})

(defn randomize [objects]
    (rand-nth objects))

(defn coin-flip []
    (randomize coin))

(defn rand-transaction []
    (rand-nth [:withdrawal :deposit]))

(defn create-accounts [n]
  (reduce (fn [mymap mykey] (assoc mymap mykey {:balance 500 :type (randomize acct-type) :transactions 0})) 
                      {} 
                      (for [x (range 1 (+ n 1))] (str "account" x) )))

(defn get-transaction []
  (randomize (filter #(zero? (mod % 10))
     (take (- 400 1) (iterate inc 100)))))

(defn get-account [n] 
  (randomize (keys (create-accounts n))))

(defn interest-amount [percentage acct]
  (float (* 0.01 percentage (acct :balance))))

(defmulti apply-interest :type)

(defmethod apply-interest :checking [acct]
  (interest-amount 0.02 acct))
(defmethod apply-interest :savings [acct]
  (interest-amount 0.04 acct))
(defmethod apply-interest :moneymarket [acct]
  (interest-amount 0.06 acct))

(defn update-trans [accts acct]
  (update-in accts [acct :transactions] + 1))

(defn check-interest-trans [accts acct]
  (let [myaccts (update-trans accts acct)]
  (if (= 0 (mod (get-in accts [acct :transactions]) 2))
      (update-in myaccts [acct :balance] * (apply-interest (myaccts acct)))
     myaccts)))

(defn process-trans [accts n]
  (let [acct (get-account n)
        myaccts (check-interest-trans accts acct)
        trans-type (rand-transaction)]
    (cond 
      (= :withdrawal trans-type)(update-in myaccts [acct :balance] - (get-transaction))
      (= :deposit trans-type) (update-in myaccts [acct :balance] + (get-transaction))
      :default (println "no match"))
      ))

(defn run-fantasy [n]
  (let [acct-count n]
             (loop [accts (create-accounts n)
                    transactions (* n 100)]
                     (if (= 0 transactions)
                       (println "\nloop done")
                       (do
                         (dotimes [x n]
                           (if (> (get-in accts [(str "account" (+ x 1)) :transactions]) 0)
                           (print
                             (str "\n trans" transactions ", acct" (+ x 1)": ")
                             (str "bal: ")(get-in accts [(str "account" (+ x 1)) :balance])
                             (str "tot-trans: ")(get-in accts [(str "account" (+ x 1)) :transactions]))))
                         (recur (process-trans accts acct-count) (dec transactions)))))))
               
(run-fantasy 10)

Stay tuned for next week: Jave interop and Concurrency, Chapters 5 & 6...

2 comments:

  1. hey Joshua, funky blog entries! I really like your blog and think the references you give, for example Brian Harvey lectures are very very interesting! I have to find the time to work through his lectures!
    I'm looking forward to week 4 of am it's course! so far its been for me a
    wonderful productive way of learning clojure.
    John

    ReplyDelete
  2. Thanks John!

    I'm also really looking forward to going back to Brian Harvey's lectures after our class finishes. I've achieved a good footing on Clojure basics through Amit's class so that I will be better able to translate the Scheme examples in Harvey's lectures and also see how things can then be furthered simplified using the many higher-order functions from Clojure's seuqence API.

    Keep Clojuring :)

    ReplyDelete