Skip to content

Instantly share code, notes, and snippets.

@dmcclory
Created September 8, 2025 12:41
Show Gist options
  • Select an option

  • Save dmcclory/e005ccae58fd3ce9bc72743d9542c644 to your computer and use it in GitHub Desktop.

Select an option

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
;; 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