Last active
September 26, 2024 10:38
-
-
Save brendanzab/04d7ee4cd0e2c96440e939048e6d08f0 to your computer and use it in GitHub Desktop.
Bank account interface as a codata type in Polarity (see: https://polarity-lang.github.io)
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
-- UML Class diagram: | |
-- | |
-- ┌───────────────────────────────────┐ | |
-- │ BankAccount | | |
-- ├───────────────────────────────────┤ | |
-- │ - owner : String | | |
-- │ - balance : Dollars = 0 | | |
-- ├───────────────────────────────────┤ | |
-- │ + getOwner() : String | | |
-- │ + getBalance() : Dollars | | |
-- │ + deposit( amount : Dollars ) | | |
-- │ + withdrawal( amount : Dollars ) | | |
-- └───────────────────────────────────┘ | |
-- | |
-- Based on https://en.wikipedia.org/wiki/File:BankAccount1.svg | |
-- Polarity does not have strings, so just use some hard-coded names | |
data Owner { | |
Alice, | |
Sally, | |
} | |
data Dollars { | |
Z, | |
S(pred: Dollars) | |
} | |
def Dollars.add(other: Dollars): Dollars { | |
Z => other, | |
S(x) => S(x.add(other)) | |
} | |
def Dollars.monus(other: Dollars): Dollars { | |
Z => Z, | |
S(pred) => other.match { | |
Z => S(pred), | |
S(pred') => pred.monus(pred') | |
} | |
} | |
data Option(a: Type) { | |
None(a: Type): Option(a), | |
Some(a: Type, x: a): Option(a) | |
} | |
-- | Bank account interface | |
codata BankAccount { | |
-- | The owner of the account | |
.getOwner: Owner, | |
-- | The current balance in the account | |
.getBalance: Dollars, | |
-- | Deposit some money into the account | |
.deposit(a: BankAccount, amount: Dollars): BankAccount, | |
-- | Withdraw some money from the account, returning `None` in the case of an overdraft | |
.withdrawal(a: BankAccount, amount: Dollars): Option(BankAccount), | |
} | |
-- | Create an account given the owner and the current balance | |
codef AccountState(owner: Owner, balance: Dollars): BankAccount { | |
.getOwner => owner, | |
.getBalance => balance, | |
.deposit(a, amount) => AccountState(owner, balance.add(amount)), | |
.withdrawal(a, amount) => balance.match { | |
Z => None(_), | |
S(_) => Some(_, AccountState(owner, balance.monus(amount))) | |
} | |
} | |
-- | Create a new bank account for a given owner | |
let createAccount(owner: Owner): BankAccount { | |
AccountState(owner, 0) | |
} |
Yes. This would require local recursive (co)definitions. This is not inherently difficult with de-/refunctionalization, as we lift all local (co)matches of a given type to top-level (co)definitions before de-/refunctionalizing that type. The same would have to apply to local recursive (co)definitions. Still, it would be some effort to implement :)
This is related to this issue but depending on how we design it, it might not be sufficient for this use case.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
It would be neat if I could make
WithState
AccountState
local to thelet
binding to encapsulate the state (as in the UML diagram), but I understand that might be difficult with the defunctionalisation/refunctionalisation stuff in Polarity. This is how I sketched it out in psuedocode: