Abstract
Flaws in computer software are a fact of life, both in small and
large-scale applications.
Compilers for modern programming languages accommodate many program
analyses for finding mistakes at compile-time (statically).
These analyses detect a significant portion of errors automatically:
this makes program analyses a valuable and indispensable tool for
developing reliable, high quality software.
Type checking is
... read more
perhaps the most popular and best studied form of
static analysis. This analysis guarantees that functions (or methods)
are never applied to incompatible arguments, and is traditionally an
important part of functional programming languages (e.g., ML and
Haskell).
One common feature of ML and Haskell is that they are implicitly typed:
no type annotations in the program are required.
Instead, these languages rely on an effective type inference algorithm,
which automatically deduces all types.
This inference algorithm has the pleasing property that it finds
principal (or most general) types.
In fact, the inference algorithm not only recovers type information, it
also detects type inconsistencies in programs, which are generally
caused by mistakes made by the programmer.
In most compilers, however, the algorithm for type inference is not
designed to provide good feedback, but rather focuses on performance.
As a result, the type error messages that are reported by these
algorithms can be difficult to understand, especially for beginning
programmers.
This thesis is entirely devoted to improve the type error messages for
a functional programming language.
Ideally, we should follow a human-like inference strategy, and depart
from the mechanical order in which standard inference algorithms
proceed. This entails, for example, a global analysis of a program,
which depends on heuristics that capture expert knowledge. We may even
anticipate common errors and provide additional information for special
classes of mistakes.
We have reached the above results by pursuing a constraint-based
approach to type inference. The typing problem is first mapped to a set
of type constraints, which is then passed to a specialized constraint
solver.
Because there is no best type inference algorithm that suits everyone,
we present a parameterized framework, which can be instantiated and
customized according to one's personal preferences.
We propose a set of type inference directives to personalize the type
inference process even further.
Type inference directives change the error message facility, but
without losing soundness of the underlying type system.
For instance, these directives make it possible to phrase error
messages in terms of a specific domain,
which makes them very suitable for creating a close integration of the
DSL and the host language.
In addition, directives allow to tune the error messages to the level
of expertise of a specific group of programmers.
All type inference techniques that we propose have been implemented in
the Helium compiler, which covers almost the entire Haskell 98
standard.
This compiler has been used for the introductory functional programming
courses at Utrecht University, by students without any prior
knowledge of functional programming. The implementation affirms that
our approach scales to a full-blown programming language.
show less