Gradual Type System
Allows parts of a program to be dynamically typed
and other parts to be statically typed
.
The programmer controls which parts are which by either leaving out type annotations or by adding them in.
Large software systems are often developed in multiple languages partly because dynamically typed languages are better for some tasks and statically typed languages are better for others.
Gradual type system allows programmers to choose between a dynamic or static type system without dealing with pains of language interoperability.
In a dynamically typed language, type checking is performed during program execution, immediately before the application of each operation, to make sure that the operand type is suitable for the operation.
def add1(x): return x + 1 class A(object): pass a = A() add1(a) # TypeError: unsupported operand type(s) for +: 'A' and ‘int'
Some or even all type errors are caught by a type checker prior to running the program.
The type checker is usually part of the compiler and is automatically run during compilation.
class A { int add1(int x) { return x + 1; } public static void main(String args[]) { A a = new A(); add1(a); } } /* A.java:9: add1(int) in A cannot be applied to (A) add1(a); ^ 1 error */
How can a type checker
predict that a type error will occur when a particular program is run?
It is impossible to build a type checker
that can predict in general which programs will result in type errors and which will not.
Instead, all type checkers
make a conservative approximation of what will happen during execution and give error messages for anything that might cause a type error.
For example, the Java compiler rejects the following program even though it would not actually result in a type error
.
class A { int add1(int x) { return x + 1; } public static void main(String args[]) { A a = new A(); // never if (false) add1(a); else System.out.println("Hello World!"); } }
The Java type checker
does not try to figure out which branch of an if
statement will be taken at runtime.
Instead, it conservatively assumes that either branch could be taken and therefore checks both branches.
Gradual type checker is a type checker that checks, at compile-time, for type errors in some parts of a program, but not others.
def add1(x: int): return x + 1 # x should be an int class A: pass a = A() # a is a class add1(a) # a is not int
Type checker
signals an error
when a type annotation is inconsistent.
The gradual type checker
deals with unannotated variables by giving them the unknown
type (also called the dynamic type in the literature), which we abbreviate as ?
and by allowing implicit conversions from any type to ?
and also from ?
to any other type.
Allowing the implicit converson from ?
to int
is unsafe, and is what gives gradual typing the flavor of dynamic typing.
Just as with dynamic typing, the argument bound to x
will be checked at run-time to make sure it is an integer
before the addition is performed.