Beyond Code Style
They say “with great power comes great responsibility.” Because of the IntelliJ Scala plugin’s huge user base, most default settings tend to become de-facto standards in the Scala community, so we strive to choose the defaults wisely. Moreover, we believe that sometimes such kind of decisions are worthy of detailed explanations, and subject to approval by the community.
As experience has shown, many introduced novelties are often misunderstood and criticized in the beginning. For example, method signature enforcement was initially touted as some quirk that only keeps getting in the way. But as time went by, those rules and the side-effect thing became “self-evident” in the Scala community, mostly because that’s how all this was intended in the language from the inception. In other cases, like with the deprecation of postfix calls (before SIP-18), we referred to Martin’s tweet as a justification. Because there’s no appropriate Martin’s tweet (yet?) for what we’re introducing this time, we dare validate the step by ourselves.
We’re expanding the notion of “code style” for Scala beyond the typical constituents like spaces, parentheses and braces. For a start, we have introduced a special set of configurable rules of when to add / enforce type annotations (check Settings / Editor / Code style / Scala / Type Annotations). Truth be told, we added those settings about 4 years ago, but only now did we pick up our courage to enable some of those by default (on the other hand, you can’t say we’re rushing it :).
From now on, the type annotation settings will be used in the following functionality:
- Introduce Variable / Field, Extract Method, Override / Implement Method refactorings,
- Create Variable / Method from usage,
- Java-to-Scala code conversion,
- Reformat Code action with “Add type annotations” enabled,
- Type Annotation inspection.
In particular, we’re considering enabling the enforcement of type annotations on public methods, and we’re definitely expecting a mixed reaction to that. Why declare some result types explicitly? Well, we believe that it’s one of those places where tools can mend possible abuse of certain liberties in the language design. Here are the reasons:
- Public methods and properties represent a so-called “public API,” which is a form of abstraction. Forcing API users to investigate the inner workings of public methods (or to guess the result type) is a sure way to break encapsulation and to degrade code quality. Explicit type annotation make public API well-marked and easy to use.
- Explicit result types can greatly improve code editing performance. Unlike the compiler, an IDE has to re-infer types on each code update. To optimize the workload, IntelliJ IDEA tries its best to cache and preserve as much type information as possible. However, as the result type of a method with omitted type annotation can change even on “external” update (i.e. update outside of the method body), IntelliJ has to re-process the whole content of such a method on almost any change (and the content might often consists of implicits, type-level arithmetic and whatnot). Considering the first point, adding type annotations to public methods makes your code more IDE-friendly (for the same reason, you may also choose to add explicit types to really complex yet non-public methods).
- Type annotations on public methods increase the speed of incremental compilation by minimizing the amount of recompiled code. Algorithms of incremental compilers (like SBT or IntelliJ) track “public API changes” and dependencies to determine what files need to be recompiled. Thus, if type of some method in a frequently used class is adjusted after code editing (but still lays within some “implied” base type), almost the whole project might often be unnecessary recompiled as a result. In a sense, this argument is a combination of the two previous ones but from the compiler’s standpoint. Official SBT documentation supports this argument by saying that “explicitly annotating return types of a public API is a good practice in general.”
Note: One possible exception to these rules are simple declarations like
val answer = 42 where the result type is clearly evident, both for humans and for the compiler. Currently we’re determined to treat this kind of expressions separately.
So, adding explicit types to public methods can make our code cleaner and also speed up editing / compilation. Sounds good, doesn’t it? But what about tons of those “righteous” warnings, aren’t they supposed to be annoying? The good news is that there is a straightforward way to process them all at once:
- Go to Settings / Editor / Code Style / Scala / Type Annotations and make sure that Add & Check option is selected for public members.
- Invoke Analyze / Inspect Code, select Whole project as a scope and create an inspection profile with only the Type annotation required inspection enabled.
- Run the inspection and then choose Apply fix “Add type annotation” to batch-add all the explicit type results.
When SCL-10501 will be implemented, another way to add required type annotations in batch is to invoke Reformat Code with Add type annotations enabled (but this also reformats code for good measure).
Because the functionality is already present in all the Scala plugin builds, you don’t have to wait for it to be enabled by default: you can tweak the code style and try this out right now. Likewise, if you consider the undertaking to be too much of a hassle, just tweak the code style settings as you see fit.
At the next step, we will add many more options to the code style (currently listed as inspections), so that it becomes possible to choose, for example, what form of anonymous function definitions you prefer (
person => person.name or
_.name) or to require eta-expansions to be explicit (like
f _), and so on.
We encourage you to try the feature and, as always, we’d love to hear your feedback!
Scala Code Style now tells:
All public methods should have explicit type annotations. Type inference may break encapsulation in these cases, because it depends on internal method and class details. Without an explicit type, a change to the internals of a method or val could alter the public API of the class without warning, potentially breaking client code. Explicit type annotations can also help to improve compile times.
We’ve added fine-grained settings:
Tooltip for the “Type is obvous” checkbox: