问题
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.
回答1:
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.
来源:https://stackoverflow.com/questions/14431526/sml-error-operator-and-operand-dont-agree-when-comparing-integer-in-list-to-an