1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200
|
\sec{Type checking}{type-checkin}{Type_checking.html}
%
{\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, {\smlnj} has
many deviations from the Definition -- please see
\appref{nj-deviations} 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.
\begin{itemize}
\item
{\mlton} resolves flexible record patterns using a larger context than
many other SML compilers. For example, {\mlton} accepts the
following.
\begin{verbatim}
fun f {x, ...} = x
val _ = f {x = 13, y = "foo"}
\end{verbatim}
%
\item
{\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.
\begin{verbatim}
structure S:
sig
val f: int -> int
end =
struct
val f = (fn x => x) (fn y => y)
end
\end{verbatim}
\end{itemize}
\subsection{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 {\tt real * int} but gets {\tt real * real}, the type
error message would look like
\begin{verbatim}
expects: _ * [int]
but got: _ * [real]
\end{verbatim}
As another aid to spotting differences, {\mlton} places brackets {\tt
[]} 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
\begin{verbatim}
expects: [int * real]
but got: [int * real * string]
\end{verbatim}
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.
\begin{verbatim}
expects: [int] * [real]
but got: [real] * [int]
\end{verbatim}
Here the brackets make it easy to see that the components do not
match.
{\mlton}'s type checker is fairly new. 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 {\mltonmail}. We would also appreciate feedback
on any type error messages that you find confusing, or suggestions you
may have for improvements to error messages.
\subsection{Type information about programs}
{\mlton} has a flag, {\tt -show-basis } {\it file}, that causes
{\mlton} to pretty print to {\it file} the basis defined by the input
program. For example, if {\tt foo.sml} contains
\begin{verbatim}
fun f x = x + 1
\end{verbatim}
then {\tt mlton -show-basis foo.basis foo.sml} will create {\tt
foo.basis} with the following contents.
\begin{verbatim}
val f: int -> int
\end{verbatim}
If you only want to see the basis and do not wish to compile the
program, you can call {\mlton} with {\tt -stop tc}.
When displaying signatures, {\mlton} prefixes types defined in the
signature them with {\tt ?.} to distinguish them from types defined in
the environment. For example,
\begin{verbatim}
signature SIG =
sig
type t
val x: t * int -> unit
end
\end{verbatim}
is displayed as
\begin{verbatim}
signature SIG =
sig
type t = ?.t
val x: (?.t * int) -> unit
end
\end{verbatim}
Notice that {\tt int} occurs without the {\tt ?.} 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
\begin{verbatim}
signature SIG =
sig
type t
type u = t
val x: t
val y: u
end
\end{verbatim}
is displayed as
\begin{verbatim}
signature SIG =
sig
type t = ?.t
type u = ?.t
val x: ?.t
val y: ?.t
end
\end{verbatim}
Canonical names are always relative to the ``top'' of the signature,
even when used in nested substructures. For example,
\begin{verbatim}
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
\end{verbatim}
is displayed as
\begin{verbatim}
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
\end{verbatim}
When displaying structures, {\mlton} uses signature contstraints
wherever possible, combined with {\tt where type} clauses to specify
the meanings of the types defined within the signature.
\begin{verbatim}
signature SIG =
sig
type t
val x: t
end
structure S: SIG =
struct
type t = int
val x = 13
end
structure S2:> SIG = S
\end{verbatim}
is displayed as
\begin{verbatim}
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
\end{verbatim}
|