Created
September 8, 2025 12:41
-
-
Save dmcclory/e005ccae58fd3ce9bc72743d9542c644 to your computer and use it in GitHub Desktop.
toy bank model based ideas from on David Nolan's The Power of Toys
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
| ;; tring out some ideas from David Nolan's talk [The Power of Toys](https://www.youtube.com/watch?v=fgRt0ecWfEE) | |
| ;; 1) a toy language, eg: `{:action :deposit :acc-id "acc-2" :amount 600}` | |
| ;; 2) a model where every function takes in the state of the world & returns an updated state of the world | |
| ;; (the talk is also about using simple property-based testing to generate programs written in the toy language, I haven't tried that) | |
| ;; src/bank-model/core.clj | |
| (ns bank-model.core) | |
| (defn increment-time [world] | |
| (let [updated (+ 1 (get-in world [:time] 0))] | |
| (assoc-in world [:time] updated))) | |
| (defn get-time [world] (get-in world [:time])) | |
| (defn deposit [world step] | |
| (let [acc-id (step :acc-id) | |
| amount (step :amount) | |
| t (get-time world) | |
| transactions (world :transactions []) ] | |
| (assoc-in world [:transactions] (conj transactions | |
| {:kind :deposit | |
| :acc acc-id | |
| :amount amount | |
| :time t})))) | |
| (defn withdraw [world step] | |
| (let [acc-id (step :acc-id) | |
| amount (step :amount) | |
| t (get-time world) | |
| transactions (world :transactions []) ] | |
| (assoc-in world [:transactions] (conj transactions | |
| {:kind :withdrawal | |
| :acc acc-id | |
| :amount amount | |
| :time t})))) | |
| (defn create-account [world step] | |
| (let [acc-id (step :acc-id) | |
| amount (step :amount) | |
| t (get-time world) | |
| accounts (world :accounts {}) ] | |
| (assoc-in world [:accounts] (merge accounts | |
| {acc-id {:acc acc-id | |
| :balance amount | |
| :time t}})))) | |
| (defn current-balance [world acc-id] | |
| (let [account (get-in world [:accounts acc-id]) | |
| transactions (world :transactions) | |
| initial-amount (account :balance) | |
| acc-transactions (filter (fn [t] (= (account :acc) (t :acc))) transactions)] | |
| (reduce (fn [a b] | |
| (+ a | |
| (if (= (b :kind) :withdrawal) | |
| (* -1 (b :amount)) | |
| (b :amount)))) | |
| initial-amount | |
| acc-transactions))) | |
| (defn history [] | |
| [ {:action :create-account :acc-id "acc-2" :amount 400} | |
| {:action :create-account :acc-id "acc-1" :amount 1000} | |
| {:action :deposit :acc-id "acc-2" :amount 600} | |
| {:action :increment-time} | |
| {:action :deposit :acc-id "acc-2" :amount 200} | |
| {:action :increment-time} | |
| {:action :deposit :acc-id "acc-1":amount 800} | |
| {:action :increment-time} | |
| {:action :withdraw :acc-id "acc-1" :amount 300}]) | |
| (defn eval-history [world history] | |
| (reduce (fn [w h-step] | |
| (let [action (h-step :action)] | |
| (case action | |
| :deposit (deposit w h-step) | |
| :withdraw (withdraw w h-step) | |
| :increment-time (increment-time w) | |
| :create-account (create-account w h-step) | |
| w))) | |
| world | |
| history)) | |
| ;; example | |
| bank-model.core=> (eval-history {} (history)) | |
| { :accounts | |
| {"acc-2" {:acc "acc-2", :balance 400, :time nil}, | |
| "acc-1" {:acc "acc-1", :balance 1000, :time nil}}, | |
| :transactions [ | |
| {:kind :deposit, :acc "acc-2", :amount 600, :time nil} | |
| {:kind :deposit, :acc "acc-2", :amount 200, :time 1} | |
| {:kind :deposit, :acc "acc-1", :amount 800, :time 2} | |
| {:kind :withdrawal, :acc "acc-1", :amount 300, :time 3}], | |
| :time 3 | |
| } | |
| bank-model.core=> (current-balance (eval-history {} (history)) "acc-2") | |
| 1200 | |
| bank-model.core=> (current-balance (eval-history {} (history)) "acc-1") | |
| 1500 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment