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.