Busch
Busch

Reputation: 959

Manipulating Clojure closures?

I'm new with Clojure and I'm working on creating a simple bank account function that returns a closure. I have figured out how to initialize the variables, but I can't seem to figure out how to manipulate the balance. I would like to create a :withdrawal and :deposit to perform their respective functions, but I can't seem to figure out how.

(defn account 
  [name initial-balance password]
  (fn [a & args] 
    (condp = a
      :name name
      :balance initial-balance
      :authenticate (= password (first args)))))

Upvotes: 0

Views: 164

Answers (2)

jbm
jbm

Reputation: 2600

Here's another approach which still represents the account as a closure but doesn't mutate balance. Instead, deposit and withdraw return a new account closure.

(defn account
  [name balance password]
  (fn [msg & args]
    (case msg
      :name name
      :balance balance
      :deposit (account name (- balance (first args)) password)
      :withdraw (account name (+ balance (first args)) password)
      :authenticate (= password (first args)))))

Since e.g. (person :deposit 50) returns a new closure rather than the new balance you would need to follow it up with a :balance call/message to see what the resulting balance was.

(def person (account "name" 100 "password"))

(person :deposit 50) ; returns a new closure
;=>  #<user$account$fn__85647 user$account$fn__85647@884327>

(person :balance) ; our original `person` still has its original balance
;=> 100

((person :deposit 50) :balance) ; but a new closure can have a new balance
;=> 150

Upvotes: 2

DaoWen
DaoWen

Reputation: 33019

The Clojure idioms usually discourage mutable state. When you do want to mutate something like your bank account balance, the usual way to do that would be with a thread-safe atom. Here's an example:

(defn account 
  [name initial-balance password]
  (let [balance (atom initial-balance)]
    (fn [a & args] 
      (case a
        :name name
        :balance @balance
        :deposit (swap! balance + (first args))
        :withdraw (swap! balance - (first args))
        :authenticate (= password (first args))))))

Upvotes: 3

Related Questions