Skip to content

Instantly share code, notes, and snippets.

@schicks
Last active January 21, 2020 13:55
Show Gist options
  • Save schicks/a68a7f44796fbd6dbfc3d98485f25b90 to your computer and use it in GitHub Desktop.
Save schicks/a68a7f44796fbd6dbfc3d98485f25b90 to your computer and use it in GitHub Desktop.
Types are Boring
(*
* A linear algebra approach to the problem described here:
* https://alexnixon.github.io/2020/01/14/static-types-are-dangerous.html
*)
type currency = (* Currencies as a simple enum *)
| GBP
| USD
| JPY
type currency_value = (currency * int) list (* Values as a vector, with currencies as an eigenbasis *)
let ($*) s = List.map(function | currency, v -> currency, v * s) (* Scalar multiplication *)
let ($+) (a: currency_value) (b: currency_value) = (* Vector addition *)
let result = (List.map (fun a -> match a with
| currency, u -> match List.assoc_opt currency b with
| None -> (currency, u)
| Some(v) -> (currency, u + v))
a
) in
List.fold_left
(fun acc a -> match a with
| currency, u -> match List.assoc_opt currency result with
| None -> (currency, u)::acc | Some(_) -> acc)
result
b
let notionally trades =
List.map (function | cv, s, _ -> cv, s) trades
|> List.fold_left
(fun acc a -> match a with
| cv, s -> acc $+ (s $* cv)
)
[]
let trades = [
[GBP, 1], 100, "VOD.L";
[GBP, 2], 200, "VOD.L";
[USD, 3], 300, "AAPL.O";
[JPY, 5], 50, "4151.T";
]
let result = notionally trades
@schicks
Copy link
Author

schicks commented Jan 21, 2020

@fried_brice pointed out that this does not quite meet the requirements as described by Alex. Part of the problem was that the display function as Alex is taking it should take a scalar value (just an integer) rather than a currency vector, and this code addresses that. But the other part of the requirement, which is not addressed as well, is that a given trade should have one and only one currency. The code as written doesn't quite do that, but it's fairly easy to get to;

type price = int
type quantity = int
type ticker = string
type trade = currency * price * quantity * ticker

let notionally trades = 
    List.map (function | c, v, s, _ -> [c, v], s) trades
    |> List.fold_left
        (fun acc a -> match a with
        | cv, s -> acc $+ (s $* cv)
        )
        []

let trades: trade list = [
    GBP, 1, 100, "VOD.L";
    GBP, 2, 200, "VOD.L";
    USD, 3, 300, "AAPL.O";
    JPY, 5, 50, "4151.T";
]

let result = notionally trades

Now we have an explicit concept of a trade which is specific to a currency, and can trivially map it back up into a currency vector.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment