OCaml for the Skeptical
U of C Library DLDC Informal OCaml Class

Type Inference and Type Errors

One of the best things about OCaml (it's practically our mantra) is: strong static typing with type inference. If you think now that you don't need this facility, because you don't really make many type errors in your code (and when you do they're trivial), prepare for a shock.

The first thing you'll notice when programming in OCaml is all the type errors you get. Sure, some of that is because you don't know the language yet, but many of them are the sort of errors that you are constantly making in your dynamically typed language and not noticing until you test a function. With luck this discovery happens the first time you run the function under the interpreter. I will claim that most users of dynamically typed languages are so used to this first level of debugging which weeds out type errors that they no longer recognize how many type errors they make.

Remember, it's quite rare (until you get to advanced territory) that the type errors you get from OCaml wouldn't have happened to you in another language. OCaml just spots them early and won't let you proceed without fixing them. How this can be perceived as a disadvantage is beyond me.

Okay. This page primarily exists to show you the single most common sort of type error and explain what it means. Suppose you try to add together an int and a string:

    # 2 + "foo";;
    This expression has type string but is here used with type int
    #

The word "this" refers to the underlined expression, in this case the string literal "foo". It means that in this context, you are using a string where the surrounding context requires an int. Simple enough.

Of course, nothing in programming is that simple. OCaml can't read your mind, so in fact the error could be with the 2 (perhaps it should have been a string, though OCaml knows that + doesn't (normally) apply to strings); the error might have been with + (perhaps you meant some infix binary operator that takes an int and a string as arguments). But no matter: you have certainly been pointed to the right place.

Now, a type error involving all literals is most likely just a typo. Typically, one or more of the troublesome subexpressions will be named values:

    # let a = 2;;
    val a : int = 2
    # let b = "foo";;
    val b : string = "foo"
    # a + b;;
    This expression has type string but is here used with type int
    #

In this case, you have to figure out the values of the variables to see what you did wrong. Here you can just evaluate a and evaluate b to figure this out.

Most commonly, this will happen across function boundaries:

    # let f a b = a + b;;
    val f : int -> int -> int = <fun>
    # let g x = x ^ f 2 3;;
    This expression has type int but is here used with type string
    #

Here you're trying to use the infix string concatenation operator ^, which takes two string arguments, with the parameter x — we can't know what value it might ultimately take on — and the result of evaluating f 2 3. Here, you need to ask OCaml what the range of f is (and why not ask about ^ while we're at it, to make sure it's type is what you assume):

    # f;;
    - : int -> int -> int = <fun>
    # (^);;
    - : string -> string -> string = <fun>
    #

to see that problem is that the range of f is int, not string. (Or perhaps you thought that ^ was exponentiation, which is ** (but it takes args of type float).)

Finally, this type error can even happen across module boundaries. (One case of) a module in OCaml is a file of source code. Imagine you are defining the function f (above) on one file and the function g in another. OCaml will catch this type error when you compile the file containing g!