Static / strong typing and refactoring

It seems to me that the most invaluable thing about a static / strongly typed programming language is that it helps refactoring: if / when you change any API, then the compiler will tell you that this change has changed.

I can imagine how to write code at runtime / a weakly typed language ... but I cannot imagine refactoring without the help of a compiler, and I cannot imagine tens of thousands of lines of code without refactoring.

It's true?

+6
static-typing refactoring strong-typing
source share
5 answers

I think that you combine when types are checked using how they are checked. Implementing runtime is not necessarily weak.

The main advantage of static types is exactly what you say: they are comprehensive. You can be sure that all call sites are of type by simply letting the compiler do this.

The main limitation of static types is that they are limited by the constraints that they can express. It depends on the language, with most languages ​​having relatively simple type systems (c, java) and others with extremely powerful type systems (haskell, cayenne).

Due to these limitations, types are not sufficient in themselves. For example, in java types there are more or less limited checks on type name matching. This means that the value of any constraint that you want to check must be encoded into some kind of naming scheme, therefore, a lot of pointers and boiler plates common to java code. C ++ is slightly better in that templates allow a bit more expressive, but do not come close to what you can do with dependent types. I'm not sure what the disadvantages of more powerful type systems are, although it is obvious that some people will use them in industry.

Even if you use static typing, most likely it is not expressive enough to check everything you care about, so you will also need to write tests. Regardless of what the static is gaining you more effort than the template requires, this is a discussion that has been raging for centuries and that I don't think it has a simple answer for all situations.

Regarding your second question:

How can we safely override the language at runtime?

The answer is tests. Your tests should cover all cases that matter. Tools can help you evaluate how comprehensive your tests are. Coverage verification tools let you know if lines of code are covered by tests or not. Test mutation tools (jester, heckle) can tell you if your tests are logically incomplete. Acceptance tests let you know what you wrote meets the requirements, and finally, regression and performance tests ensure that every new version of the product will maintain the quality of the latter.

One of the great things about proper in-place testing depends on complex types, which makes debugging a lot easier. When you run the tests, you get specific unsuccessful statements in the tests that clearly express what they are doing, and do not deceive statements about compiler errors (I think the errors of the C ++ template).

No matter what tools you use: writing code that you are sure of will take effort. Most likely, many tests will be required. If the penalty for errors is very high, for example, aerospace or medical control software, you may need to use formal mathematical methods to prove the behavior of your software, which makes such development extremely costly.

+13
source share

I completely agree with your feelings. The very flexibility that languages ​​dynamically typify should be good, actually making the code very difficult to maintain. Indeed, is there such a thing as a program that continues to work if the data types change in a non-trivial way without actually changing the code?

At the same time, you can check the type of the passed variable and somehow crash if it is not the expected type. You still have to run your code to root out these cases, but at least it will tell you something.

I think that Google’s internal tools actually do the compilation and probably introduce validation on their Javascript. I wish I had these tools.

+5
source share

To get started, I am a native Perl programmer, so on the one hand, I have never programmed a network of static types. OTOH I never programmed them, so I can not talk about their benefits. What I can say is what he likes in the refactor.

I do not consider the lack of static types as a problem for refactoring. What I find a problem is the lack of a refactoring browser. Dynamic languages ​​have a problem that you really don’t know what the code will really do until you run it. Perl has more than most. Perl has an additional problem with a very complex, almost unique syntax. Result: there are no refactoring tools (although they work very quickly on this ). As a result, I have to reorganize manually. And this is what introduces errors.

I have tests to catch them ... usually. I often face a couple of heaps of unverified and almost unverifiable code with a chicken / egg problem related to the need to refactor the code to verify it, but I need to check it to reorganize it. Hk. At this point, I have to write a very dumb, high level of "does the program output the same thing as before" to verify that I did not break something.

Static types, as provided in Java or C ++ or C #, really solve only a small class of programming problems. They ensure that your interfaces are transmitted with right-labeled data bits. But just because you get a collection does not mean that the collection contains data that you think they make. Since you get an integer, this does not mean that you got the correct integer. Your method accepts a User object, but is this user registered?

Classic example: public static double sqrt(double a) is the signature for the Java square root function . The square root does not work on negative numbers. Where does he say that in the signature? This is not true. Worse, where does he say what this function does? The signature only says what types it takes and what it returns. He says nothing about what is happening between them and about where the interesting code lives. Some people have tried to capture the full API using a contract design , which can be generally described as embedding run-time tests of your input, outputs and side effects (or lack thereof) ... but this is another show.

An API is much more than just function signatures (if it is not, you will not need all this descriptive prose in Javadocs), and refactoring is much more than just changing the API.

The biggest refactoring advantage of a statically typed, statically compiled, non-dynamic language allows you to write refactoring tools to perform fairly complex refactoring for you, because it knows where all the calls to your methods are. I am pretty envious of IntelliJ IDEA .

+2
source share

I would say that refactoring goes beyond what the compiler can check, even in statically typed languages. Refactoring simply changes the internal structure of programs without affecting external behavior. Even in dynamic languages, there are still things you can expect and check, you just lose a little help from the compiler.

+1
source share

One of the benefits of using var in C # 3.0 is that you can often change type without breaking code. The type should look the same - properties with the same name must exist, methods with the same or similar signature must still exist. But you can really switch to a completely different type without even using something like ReSharper.

+1
source share

All Articles