Monday, April 13, 2009

When in doubt pile on the type information...

OMG, I just spent twenty minutes trying to figure out a compiler error. I wrote my own version of List.filter

let filter pred =
 let rec filter_acc acc = function
  head::tail when predicate head -> filter_acc head::acc tail
  | head :: tail -> filter_acc acc tail
  | [] -> List.rev acc
  in
 filter_acc [];;

The bold variable above is identified as the source of the error and here is the message

Error: This expression has type ('a -> 'b) list but is here used with type 'a

What? acc is a list of functions that take 'a and return 'b, no it is simply 'a list.

After spending way too much time pondering this problem I finally accepted that I am a static type luddite and determined that if the type inference system can't make head nor tail of my code, I should give it a hint and see if the error changes. So in the function below I specify that the acc parameter to filter_acc is a list of some type

let filter pred =
 let rec filter_acc (acc : 'a list) = function
  head::tail when predicate head -> filter_acc head::acc tail
  | head :: tail -> filter_acc acc tail
  | [] -> List.rev acc
  in
 filter_acc [];;

Sure enough the error now concerns head::acc and the error changes to

Error: This expression is not a function, it cannot be applied

Aha, the compiler thinks I am attempting to use acc as a function (acc tail), the precedence of the cons operator is obviously very low.

The following corrects the problem:

let filter pred =
 let rec filter_acc acc = function
  head::tail when predicate head -> filter_acc (head::acc) tail
  | head :: tail -> filter_acc acc tail
  | [] -> List.rev acc
  in
 filter_acc [];;


Bear with it, if you find yourself in this situation pile on the type information and let the compiler help you identify, however, esoterically, where the real error lies.

No comments: