Jevgeni has posted 19 posts at DZone. You can read more from them at their website. View Full User Profile

Are static typing and refactoring really connected?

08.11.2008
| 4864 views |
  • submit to reddit

One of the main problems brought out when comparing dynamic languages to static ones is lack of proper refactoring support. It is usually implied that dynamic languages are not conceptually refactorable, which speeds up code rotting.

Although there is plenty of evidence that dynamic languages do support refactoring, I'd like to concentrate on the other claim -- that statically typed languages are refactorable. Challenging this claim may seem laughable, as there is no lack of refactoring tools for Java or C#. But let's examine a more advanced language that is touted as Java successor -- Scala.

Scala supports structural types, which allow treating classes as records of methods that can subtyped by the presence of appropriate methods. This example was given in the Scala 2.6 release notes:

 

   
class File(name: String) {

def getName(): String = name
def open() { /*..*/ }
def close() { println(“close file”) }

}

def test(f: { def getName(): String }) {
println(f.getName)
}

test(new File(“test.txt”))
test(new java.io.File(“test.txt”))

 

In this code the type { def getName(): String } refers to any class with the method getName(): String in it. Now what happens if we try to rename the method in the structural type?

  1. We can rename all the instances of the getName() methods found in all classes anywhere.
  2. We can just rename the method in the structural type and update everything else manually

Both of these approaches are useless. The first one is basically a search and replace done on all code and may rename methods that we never intended to rename (e.g. getName() in the Person class). The second one doesn't really do anything for us.

The truth of the matter is that structural types miss an inherent scope associated with nominative (i.e. usual) types. Since every method signature in a nominative type originates from a single type, it gives refactoring a natural scope of all the subtypes of the originating type. Without that scope many refactoring techniques are essentially useless.

What is worse is that the presence of structural types also breaks refactoring in usual classes. E.g. if we try renaming getName() in the File type, we are also presented with a decision whether or not we can rename the method in structural type. And if we do rename it, we will break the code that accesses java.io.File the same way. Therefore if we want to refactor working code to working code we can again only rename everything or nothing at all.

Luckily it looks like the main refactorings broken by the structural types is renaming the methods and changing their signature. Unluckily these are the most common refactorings and having a same named method in any of the structural types breaks refactoring also in the usual classes. At the moment this mainly affects Scala and some other functional languages, but if the structural types become more spread it may come to a language near you :)

Interestingly, Cedric Beust brought out that you can refactor structural types as opposed to the duck types. Since I obviously think differently it would be interesting to hear his (and your) comments on the matter. Perhaps I'm missing something obvious?

Jevgeni Kabanov is the Tech Lead of ZeroTurnaround, makers of JavaRebel. You can find more of his writing at the http://dow.ngra.de blog.

Published at DZone with permission of its author, Jevgeni Kabanov.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)