Move the optional argument to the front and use labels at the call site:
let rec calcTotalMonths ?(months=0) ~moneyOwed ~interestRate ~monthlyPayment =
let newBalance = (moneyOwed -. monthlyPayment) *. interestRate in
if newBalance <= 0.0 then
months
else
calcTotalMonths
~months:(months + 1)
~moneyOwed:newBalance
~interestRate
~monthlyPayment
Note that this uses some shorthand for labelled arguments: ~argname:argname
can be replaced with just ~argname
. Moving optional arguments away from the last position is done to avoid an unfortunate facet of the labelled arguments system: see the manual's description of optional arguments for the details.
Looking at this, I sense the possibility that you only intend to use the months
argument from within calcTotalMonths
, and that the optionality of the argument exists to hide it from external callers. If so, this is not good style. A better approach would be a nested function to handle the loop:
let calcTotalMonths ~moneyOwed ~interestRate ~monthlyPayment =
let rec loop months balance =
let newBalance = (balance -. monthlyPayment) *. interestRate in
if newBalance <= 0.0 then months
else loop (months + 1) newBalance in
loop 0 moneyOwed
(Ignore that if you really need the argument.)