Hello! I am making a ledger app for myself, and am puzzled with finding the proper transformation after lexer and parser steps into the app logic. I provide the part of text / types which are relevant for my question. Below the introduction of the file format, the actual question is in the “Ledger” section.
Ledger file
setting start_date "2025-08-24"
setting default_currency RSD
setting rate EUR 116.7
Lexer result
type t =
| Directive(string) // setting, default_currency
| Number(float) // -20.00, 42
| Currency(string) // RSD
| Date(Date.t) // "2025-01-01"
| Newline
// ...others
// Lexer result. In the code, Date variant has proper Date.t as the argument.
let result = [
Directive("setting"), Directive("start_date"), Date("2025-08-24"), Newline,
Directive("setting"), Directive("default_currency"), Currency("RSD"), Newline,
Directive("setting"), Directive("rate") ,Currency("EUR"), Number(116.7), Newline,
]
Parser result
@unboxed type currency = Currency(string)
type setting =
| StartDate(Date.t)
| DefaultCurrency(currency)
| Rate(currency, float)
type t =
| Setting(setting)
// ...others
// Parser result.
let result = [
Setting(StartDate("2025-08-24")),
Setting(DefaultCurrency(Currency("RSD"))),
Setting(Rate(Currency("EUR"), 117.6)),
]
Ledger
// `open` the module with currency type
type settings = {
startDate: Date.t,
untilDate: Date.t,
defaultCurrency: currency,
rates: Map.t<currency, float>,
}
type t = {
settings: settings,
// ...others
}
// How to implement?
let make: parserResult => t = lines => {()}
What is the best way to transform an array of settings into the record? I came up with some solutions, but each has it’s flaws:
- Use the same record type, but with optional fields (type duplication)
- Use temporary
Map.t
(introduction of the type which mirrors setting type) - Use
Array.find
(scanslines
array for each setting)
And then after iterating the array check that all fields are presented with Option.getExn
.
But both ways seem too cumbersome and explicit for me. I want to check that all options are provided in the ledger file. And I do not want to modify parser to be more than line-by-line mapper.
Is there a better way?