Domanda

let rec calcTotalMonths ~moneyOwed:moneyOwed ~interestRate:interestRate ~monthlyPayment:monthlyPayment ~months:months=0 = (   
  let newBalance = (moneyOwed -. monthlyPayment) *. interestRate in   
  match newBalance <= 0. with 
    true -> months 
  | false -> calcTotalMonths newBalance interestRate monthlyPayment months+1
);;

I was wondering why I keep getting

File "budget.ml", line 19, characters 12-73:
Error: This expression has type
         moneyOwed:float ->
         interestRate:float -> monthlyPayment:float -> months:'a -> 'b

Why is months a type 'a when it clearly uses the '+' operator on months. It also returns months at the base case of recursion so why is it a 'b as the return type.

EDIT: after doing Jeff's suggestion

File "budget.ml", line 19, characters 12-77:
Error: This expression has type
         moneyOwed:float ->
         interestRate:float -> monthlyPayment:float -> months:int -> 'a
       but an expression was expected of type int
È stato utile?

Soluzione 2

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.)

Altri suggerimenti

You're not applying + to months, you're applying it to the result of calcTotalMonths. Function application has higher precedence than infix operators.

The expression is interpreted as if you wrote this:

(calcTotalMonths newBalance interestRate monthlyPayment months) + 1

You should write this:

calcTotalMonths newBalance interestRate monthlyPayment (months + 1)

Update

It looks like you want months to be an optional parameter with a default value of 0. The way to define that is like this:

? (months = 0)

Note: it's not good form to have trailing optional arguments, as it leads to problems with partially applying your function.

Update 2

Finally, if you want to call a function that has labelled arguments, but without supplying the labels, you can only supply the non-optional parameters. This doesn't work for you, because you need to supply the months in your recursive calls. I think things will work if you supply all the labels in your recursive call.

Update 3

For what it's worth (a moderate amount), here's how I would write this function. I don't use labelled or optional arguments except when they're solving a serious problem. Also, I think a helper function is a much better way to handle the iteration count (months):

let monthsUntilPaid owed interest payment =
    let rec go balance months =
         let balance' = (balance -. payment) *. interest in
         if balance' <= 0.0 then months else go balance' (months + 1)
    in
    go owed 0

(I just noticed that gsg also showed how to use a helper function.)

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top