The static and dynamic typing debate concerns several independent decisions:
- When to provide feedback about type errors (at compile time or runtime)
- When to provide the programmer with an executable (in the presence of “errors” or “not”)
- When to do type checking (at compile time, runtime, ship time, check-in time…)
The extreme case static typing claims that both of these should be at compile-time. The extreme case of dynamic typing says do all of these at runtime and always provide the executable.
There are many examples between these two extremes. For example, Java in Eclipse always gives an executable, provides feedback at compile time and runtime, by compiling methods that do not compile as methods that simply throw a compile error.
I propose that we do type checking and feedback at compile time, but always provide an executable for testing, giving the developer the option to ignore the feedback selectively. For example, suppose you make a change to data structure D, which breaks A, B, and C, according to the compiler’s type checker. By providing the executable even in the presence of type errors, the programmer don’t have to waste time fixing functions B and C if A needs to be tested in order to accept or reject the change to D. This way, the programmer can determine the shortest path to recognizing a problem with the change to D, without having to fix all errors.
If the compiler does not provide an executable when possible, the programmer has to fix all associated with A, B, and C, before being confident that the change to D is adequate. In other words, the compiler is in charge of how programmers and teams decompose their tasks, rather than the programmers and teams, and yet the compiler has no knowledge of their constraints.
I’m not proposing that we defer type checking or error feedback. Checking and feedback are great, as long as the developers are in charge of whether they can ignore them. Thus, to some extent, I’m proposing that whether a type checking problem is a warning or an error (in the conventional sense of whether an executable is generated), should not be up to the compiler and its flags, but up to the developer.
I’m not proposing that we ship code with type errors. But a team should be able to defer fixing these problems until the appropriate time. They should eventually be fixed in case they’re a problem, and preferably as soon as it is known whether the changes that caused the warnings are acceptable.
There is the seemingly orthogonal issue of false positives in type checking. In certain cases, type checkers will generate feedback that speculates about a problem (in some cases it cannot be certain about correctness). This is another issue in the static and dynamic typing debate. The dynamic proponents argue that false positives waste developers’ time. But I would argue that they waste no one’s time as long as such false positives can be ignored, and the executable can still be tested.
One danger with my proposal is that if you give developers the freedom to ignore type checking problems, they may never attend to them. But who really deserves to make this judgement? Compiler designers or the developer themselves? Shouldn’t a developer be allowed to trade correctness and guarantees for ship time if it allows them to show a prototype to a client faster or test the feasibility of a design faster? Again, compilers and compiler designers have no knowledge about a developer’s concerns or priorities and yet implicit in the compiler’s design is a judgement about the developers’ priorities. Compilers enforce this naive value system by placing correctness requirements in the inner loop of software development.