## 4 Type Checking

MLton's type checker follows the Definition of SML closely, so you may find differences between MLton and other SML compilers that do not follow the Definition so closely. In particular, SML/NJ has many deviations from the Definition -- please see Section A for a list of those that we are aware of.
In some respects MLton's type checker is more powerful than other SML compilers, so there are programs that MLton accepts that are rejected by some other SML compilers. These kinds of programs fall into a few simple categories.

- MLton resolves flexible record patterns using a larger context than many other SML compilers. For example, MLton accepts the following.

      fun f {x, ...} = x
      val _ = f {x = 13, y = "foo"}

- MLton uses as large a context as possible to resolve the type of variables constrained by the value resriction to be monotypes. For example, MLton accepts the following.

      structure S:
         sig
            val f: int -> int
         end =
         struct
            val f = (fn x => x) (fn y => y)
         end

### 4.1 Type error messages

To aid in the understanding of type errors, MLton's type checker displays type errors differently than other SML compilers. In particular, when two types are different, it is important for the programmer to easily understand why they are different. So, MLton displays only the differences between two types that don't match, using underscores for the parts that do. For example, if a function expects `real * int` but gets `real * real`, the type error message would look like

    expects: _ * [int]
    but got: _ * [real]

As another aid to spotting differences, MLton places brackets `[]` around the parts of the types that don't match. A common situation is when a function receives a different number of arguments than it expects, in which case you might see an error like

    expects: [int * real]
    but got: [int * real * string]

The brackets make it easy to see that the problem is that the tuples have different numbers of components -- not that the components don't match. Contrast that with a case where a function receives the right number of arguments, but in the wrong order.

    expects: [int] * [real]
    but got: [real] * [int]

Here the brackets make it easy to see that the components do not match.
MLton's type checker is new with this release. Because of this, in addition to differences with other SML compilers, MLton may even differ from earlier versions of itself, when the type checker was not so careful. If you encounter a difference between MLton and another SML compiler, or even with an older version of MLton, and are not able to determine if it is a bug, please send mail to `MLton@mlton.org`. We would also appreciate feedback on any type error messages that you find confusing, or suggestions you may have for improvements to error messages.

### 4.2 Type information about programs

MLton has a flag, `-show-basis` file, that causes MLton to pretty print to file the basis defined by the input program. For example, if `foo.sml` contains

    fun f x = x + 1

then `mlton -show-basis foo.basis foo.sml` will create `foo.basis` with the following contents.

    val f: int -> int

If you only want to see the basis and do not wish to compile the program, you can call MLton with `-stop tc`.
When MLton is called with `-show-basis` and no input file and, it pretty prints the entire basis library. This can be helpful in understanding what the basis library makes available to user programs.
When displaying signatures, MLton prefixes types defined in the signature them with `?.` to distinguish them from types defined in the environment. For example,

    signature SIG =
       sig
          type t
          val x: t * int -> unit
       end

is displayed as

    signature SIG =
       sig
          type t = ?.t
          val x: (?.t * int) -> unit
       end

Notice that `int` occurs without the `?.` prefix.
MLton also uses a canonical name for each type in the signature, and that name is used everywhere for that type, no matter what the input signature looked like. For example

    signature SIG =
       sig
          type t
          type u = t
          val x: t
          val y: u
       end

is displayed as

    signature SIG =
       sig
          type t = ?.t
          type u = ?.t
          val x: ?.t
          val y: ?.t
       end

Canonical names are always relative to the ``top'' of the signature, even when used in nested substructures. For example,

    signature S =
       sig
          type t
          val w: t
          structure U:
             sig
                type u
                val x: t
                val y: u
             end
          val z: U.u
       end

is displayed as

    signature S =
       sig
          type t = ?.t
          val w: ?.t
          val z: ?.U.u
          structure U:
             sig
                type u = ?.U.u
                val x: ?.t
                val y: ?.U.u
             end
       end

When displaying structures, MLton uses signature contstraints wherever possible, combined with `where type` clauses to specify the meanings of the types defined within the signature.

    signature SIG =
       sig
          type t
          val x: t
       end
    structure S: SIG =
       struct
          type t = int
          val x = 13
       end
    structure S2:> SIG = S

is displayed as

    structure S: SIG
          where type t = int
    structure S2: SIG
           where type t = S2.t
    signature SIG =
       sig
          type t = ?.t
          val x: ?.t
       end