26 May 2016

Most Type Errors Should Be Warnings

Compilers for statically typed languages often generate type errors, but they should instead generate warnings, in many cases.

Warnings are appropriate when something may or may not succeed at runtime. That's the point of a warning — tell the user that they may have made a mistake. For example:

import java.io.File;
...
void doSomething(File file) {
  file.uploadToServer();
}

should produce a compiler warning, since the argument may be an instance of a subclass that actually has an uploadToServer() method. Blocking code that may execute perfectly is a bureaucratic attitude.

Errors are appropriate only when the compiler can prove that a line of code will fail at runtime if executed. Like:

String s = ...;
s.launchMissile();

launchMissile() isn’t declared in String, and neither in a subclass, since String is a final class. This code is guaranteed to fail at runtime. So a compiler error is fine.

There are three classes of code: code that's guaranteed to fail, code that may or may not fail, and code that's guaranteed to work. The first category of code should fail to compile, with an error. The second should compile with a warning, and the third should, of course, compile without a warning or error.

Another example of something that should be a warning is calling a method with an argument that may not be of the correct type. You can, of course, cast it, but the question is what the compiler should do if you don't. If a method wants a List, giving it a Collection should trigger a warning, not an error.

Java already has a way of suppressing warnings, via the @SuppressWarnings annotation. You should be able to use that:

Object object = ...;
@SuppressWarnings("cast") File f = object;

There's no need for a separate cast syntax in the Java language, since it's merely a special case of suppressing a warning, for which we already have a syntax.

Errors are appropriate when the compiler can prove that it's not an instance of the appropriate type. For example, if it's an instance of an unrelated class and therefore can't also be an instance of the given class. For example, if a method requires an argument of type InputStream, and you're passing a File, since neither class extends the other, the argument is guaranteed to not be an InputStream, so it should be an error.

A third example is the error message saying that a checked exception must either be caught or declared in the throws clause. Rather than flagging an error, the compiler should generate code to catch the exception and wrap it in a RuntimeException, or a new class, CheckedException. With a warning, so that if you don't like what the compiler is doing for you, can write code to do something different.

Reserve errors for code that's guaranteed to fail at runtime if executed. Use warnings when it may or may not work, making the programmer aware, but not coming in his way with rigid, bureaucratic rules.

No comments:

Post a Comment