Last active
October 10, 2024 01:26
-
-
Save williamcotton/b67ff641eeec767313171583d7873b7e to your computer and use it in GitHub Desktop.
F# Money Types
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
open System | |
// Define units of measurement | |
[<Measure>] type USD | |
[<Measure>] type BTC | |
[<Measure>] type percent | |
let btcUsdRate = 62500m<USD/BTC> | |
// Money type alias | |
type Money<[<Measure>] 'currency> = decimal<'currency> | |
type Percentage = decimal<percent> | |
// Money module | |
module Money = | |
let increaseBy<[<Measure>] 'currency> (p: Percentage) (m: Money<'currency>) : Money<'currency> = | |
m * (1m + decimal p / 100m) | |
let decreaseBy<[<Measure>] 'currency> (p: Percentage) (m: Money<'currency>) : Money<'currency> = | |
m * (1m - decimal p / 100m) | |
let allocate<[<Measure>] 'currency> (m: Money<'currency>) (count: int) : Money<'currency> list = | |
let baseAllocation = m / decimal count | |
let remainder = decimal m % decimal count | |
[1..count] | |
|> List.map (fun i -> | |
if i <= int remainder | |
then baseAllocation + LanguagePrimitives.DecimalWithMeasure<'currency> 1m | |
else baseAllocation) | |
let allocateByRatios<[<Measure>] 'currency> (m: Money<'currency>) (ratios: Percentage list) : Money<'currency> list = | |
let totalRatio = ratios |> List.sumBy (fun r -> decimal r / 100m) | |
if Math.Abs(1m - totalRatio) > 0.0001m then | |
failwith "Sum of ratios must be 100%" | |
let allocations = | |
ratios | |
|> List.map (fun r -> m * (decimal r / 100m)) | |
let totalAllocated = allocations |> List.sum | |
let remainder = m - totalAllocated | |
allocations | |
|> List.mapi (fun i a -> | |
if i = 0 then a + remainder else a) | |
// USD examples | |
let usdPrice = 100m<USD> | |
let usdShipping = 5m<USD> | |
let usdSubtotal = usdPrice + usdShipping | |
let discount = 10m<percent> | |
let usdTotal = Money.decreaseBy discount usdSubtotal | |
let ratios = [60m<percent>; 40m<percent>] | |
let usdEqualAllocation = Money.allocate usdTotal 2 | |
let usdRatioAllocation = Money.allocateByRatios usdTotal ratios | |
// Print USD results | |
printfn "USD Price: %A" usdPrice | |
printfn "USD Shipping: %A" usdShipping | |
printfn "USD Subtotal: %A" usdSubtotal | |
printfn "Discount: %A%%" discount | |
printfn "USD Total: %A" usdTotal | |
printfn "USD Equal allocation: %A" usdEqualAllocation | |
printfn "USD Ratio allocation: %A" usdRatioAllocation | |
// BTC examples | |
let btcPrice = 0.01607580m<BTC> | |
let transactionFee = 1.25m<percent> | |
let btcTotal = Money.increaseBy transactionFee btcPrice | |
let btcInstallments = Money.allocate btcTotal 3 | |
// Print BTC results | |
printfn "\nBTC Price: %A" btcPrice | |
printfn "BTC Total with fee: %A" btcTotal | |
printfn "BTC Installments: %A" btcInstallments | |
// Demonstration of type safety | |
let invalidAdd = usdPrice + btcPrice // This will cause a compile-time error | |
let exchangedUsd = btcPrice * btcUsdRate // This is valid | |
let validBtcAllocate = Money.allocate btcTotal 2 // This is valid and works with BTC |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment