Reputation: 1853
I am new to Standard ML, and can't figure out why I am getting this type mismatch error:
fun number_in_month (month : int, dates : int list) =
if null dates
then 0
else if (month = (hd (tl (hd dates))))
then number_in_month(month, (tl dates)) + 1
else number_in_month(month, (tl dates))
Evaluating this function results in the following error:
Error: operator and operand don't agree [tycon mismatch]
5 operator domain: 'Z list
6 operand: int
7 in expression:
8 tl (hd dates)
However, at the REPL, if i do the following:
val x = [[84, 12, 23], [83, 01, 18]]
12 = (hd (tl (hd x))) (* -> val it = true : bool *)
I am not sure what the type-checking rules are in this case, and I don't see why the same expression would work on the REPL but not when I try to evaluate the subexpression in the function.
Upvotes: 1
Views: 7012
Reputation: 16255
You're getting the head of the tail of the head of a list. Your x
(in the REPL) is
a int list list
(a list of a list of ints). But your function definition declares it
as an int list
. Re-declaring number_in_month
with dates: int list list
should solve
your problem:
fun number_in_month (month : int, dates : int list list) =
...
It works as you expect in the REPL because you define x
without explicitly declaring it's type. SML infers that the type of x is int list list
which is why (hd (tl (hd x)))
passes the type-checker.
UPDATE
(was trying to add this right when stackoverflow went down)
If you're interested, here's some ideas on how you could re-write your code to make it more ML-ish:
First, you could use pattern matching:
fun number_in_month (month: int, []) = 0
| number_in_month (month: int, ([y,m,d]::rest)) =
if month = m then number_in_month(month, rest) + 1
else number_in_month(month, rest)
So number_in_month
takes a tuple of a month and a list of dates, which is logically either []
or ([y,m,d]::rest)
. This is compatible with how you've chosen to represent dates
(as a list of ints), but this will compile with a match nonexhaustive
warning. That makes sense, because what happens if you pass in dates
as [[84], [83]]
? The pattern match approach at least warns you about this, but with code like (hd (tl (hd dates)))
you'll get
a runtime error though your program has type-checked successfully. You could add another
pattern match for lists of dates where the date has less/more than 3 elements, but if
possible, it might be cleaner to represent dates as tuples of 3 ints.
type date = (int * int * int)
Then you could have:
fun number_in_month (month: int, []: date list) = 0
| number_in_month (month: int, ((y,m,d)::rest)) =
if month = m then number_in_month(month, rest) + 1
else number_in_month(month, rest)
Also, if you'd rather reuse code, you could try higher-order functions (such as foldr
):
fun number_in_month (month: int, dates: date list) =
foldl (fn ((_,m,_), c) => if m = month then c+1 else c) 0 dates
Or
fun number_in_month (month: int, dates: date list) =
length (List.filter (fn (_,m,_) => m = month) dates)
More than you asked for, but I hope it helps.
Upvotes: 7