78 lines
1.9 KiB
Plaintext

; A ledger that applies transfers between accounts. A transfer that would
; overdraw signals, and the caller picks a restart: skip it, cap it at what
; the account holds, or allow an overdraft up to a limit it supplies.
struct Overdraft :parent Error
account: i32
short: i64
struct Audit
account: i32
amount: i64
once balances: [4 i64]
once audits: i32
once log: i64
; Every transfer leaves a digit in the log when it is done with, however it
; ended: 1 applied, 2 left through a restart.
fn note(d: i64) -> ()
log = log * 10 + d
fn withdraw(account: i32, amount: i64) -> i64
let have = balances[account]
if amount > have
let short = amount - have
restart-case
error(Overdraft{.account account, .short short})
restart skip()
return 0
restart cap()
return withdraw(account, have)
restart allow(limit: i64)
if short > limit
return 0
if amount >= 100
signal(Audit{.account account, .amount amount})
balances[account] -= amount
amount
fn transfer(from: i32, to: i32, amount: i64) -> i64
let applied: i64 = 0
defer note(if applied == amount then 1 else 2)
applied = withdraw(from, amount)
balances[to] += applied
applied
fn run(policy) -> ()
balances = [100 50 0 10]
log = 0
handler-bind
transfer(0, 2, 30)
transfer(1, 2, 80)
transfer(3, 0, 25)
transfer(0, 1, 100)
on Overdraft(o)
match policy
:skip -> invoke-restart('skip)
:cap -> invoke-restart('cap)
_ -> invoke-restart('allow, i64(20))
on Audit(a)
audits += 1
println(policy, slice(balances), "log", log)
fn main() -> i32
run(:skip)
run(:cap)
run(:allow)
println("audited", audits)
let caught =
handler-case
balances = [0 0 0 0]
withdraw(2, 5)
on Overdraft(o)
println("unhandled overdraft on", o.account, "short by", o.short)
-1
println("caught", caught)
0