(use goo)
(dc <bank> (<any>))
(dp customers (<bank> => <col>) (vec))
(dc <customer> (<any>))
(dp accounts (<customer> => <col>) (vec))
(dp first-name (<customer> => <str>))
(dp last-name (<customer> => <str>))
(dc <account> (<any>))
(dp balance (<account> => <int>))
(dc <checking-account> (<account>))
(dc <savings-account> (<account>))
(dv *minimum-balance* 100) (dv *minimum-balance-for-no-fee* 1000)
(dv *monthly-fee* 10)
(dv *monthly-interest* 0.02)
(dm credit (account|<account> amount|<int>)
;; Fancy syntax
(opf (balance account) (+ _ amount))
;; Can also be written as:
;; (set (balance account) (+ (balance account) amount))
)
(dm debit (account|<account> amount|<int> => <log>)
(opf (balance account) (- _ amount))
)
(dm charge-monthly-fee (account|<account>)
(when (< (balance account) *minimum-balance-for-no-fee*)
(debit account *monthly-fee*)
)
)
(dm earn-interest (account|<account>)
(credit account (floor (* (account-balance account) *monthly-interest*)))
)
(dm earn-interest (account|<checking-account>)
;; Do nothing
)
(dm to-str (account|<account> => <str>)
(cat (if (isa? account <checking-account>)
"Checking "
"Savings ")
"Account: "
(to-str (balance account))
)
)
(dm new-customer (first-name|<str> last-name|<str> => <customer>)
(def customer (new <customer>))
;; We don't have to worry about binding masking here because the setter names
;; are transformed into first-name-setter, last-name-setter, etc.
(set (first-name customer) first-name)
(set (last-name customer) last-name)
;; Note that there is an alternative syntax where we specify the property
;; names and values in the 'new' clause, but the specific syntax for that
;; may change sometime soon (relative to the writing of this file), so
;; I'm avoiding it.
;; Don't forget to return the customer!
customer
)
(dm find-customer (customer-list|<col> prop-getter match-val|<any> => (t? <customer>))
(esc found-it
(do (fun (current-customer)
(when (= (prop-getter current-customer) match-val)
(found-it current-customer)
)
)
customer-list)
)
)
(dm to-str (customer|<customer> => <str>)
(def cat-pair-with-newline (fun (str1 str2)
(cat str1 "\n" str2)))
(def prepend-spaces (fun (str)
(cat " " str)))
(cat (last-name customer)
", "
(first-name customer)
"\n"
;; Some list-fun worthy of scheme:
;; - First, map over the list of accounts applying to-str, resulting in a list of strings
;; - Next, map over the strings with prepend-spaces, returning a new list of strings
;; - Finally, use fold+ to concatenate all the strings together (with newlines)
;; (fold+ operates like accumulate in SICP... (func z (func y (func w x))) for (w x y z))
(fold+ cat-pair-with-newline (map prepend-spaces (map to-str (accounts customer))))
)
)