Static Typing Is Not for Type Checking

In his post “Strong typing vs strong testing” Bruce Eckel described the idea, that statically (or strongly) typed languages don’t give you much, because you should verify your programs with tests anyway, and those tests will check the types as well – no need for the compiler to do that (especially if it makes you less productive with the language).

While this looks like a very good point initially, I have some objections.

First, his terminology is not the popularly agreed one. This stackoverflow answer outlines the difference between statically-typed (types are checked at compile time) vs strongly-typed (no or few implicit type conversions). And to clarify this about the language used in the article – Python – this page tells us Python is dynamically and strongly typed language.

But let’s not nitpick about terminology. I have an objection to the claim that static typing simply gives you some additional tests, that you should write anyway.

In a project written in a dynamic language, can you see the callers of a method? Who calls the speak method in his example? You’ll do a search? Well, what if you have many methods with the same name (iterator(), calculate(), handle(), execute())? You would name them differently, maybe? And be sure that you never reuse a method name in the whole project? The ability to quickly navigate through the code of big project is one of the most important ones in terms of productivity. And it’s not that vim with nice plugins doesn’t allow you to navigate through classes and to search for methods – it’s just not possible to make it as precise in a dynamic language, as it can be done in a static one.

Then I want to know what I can call on a given object. To do API “discovery” while I write the code. How often, in a big project, you are absolutely sure which method you want to invoke on an object of a class you see for the first time? Go to that class? Oh, which one is it, since you only know it has the calculate() method (which is called in the current method)? Writing a test that validates whether you can invoke a given method or not is fine, but doesn’t give you the options to discover what are your options at that point – what method would do the job best for you. Here comes autocomplete with inline documentation. It’s not about saving keystrokes, it’s about knowing what is allowed at this point in the code. I guess constantly opening API documentation pages or other class definitions would work, but it’s tedious.

Then comes refactoring. Yes, you knew I’d bring that up. But that’s the single most important feature that the tests we write enable us to use. We have all our tests so that we can guarantee that when we change something, the code will continue to work correctly. And yet, only in a statically typed language it is possible to do full-featured refactoring. Adding arguments to a method, moving a method to another class, even renaming a method without collateral damage. And yes, there are multiple heuristics that can be employed to make refactoring somewhat possible in Ruby or Python (and JetBrains are trying), but by definition it cannot be that good. Does it matter? And even if it doesn’t happen automatically, tests will catch that, right? If you have 100% coverage, they will. But that doesn’t mean it will take less time to do the change. As opposed to a couple of keystrokes for a static language.

And what are those “mythical big projects” where all the features above are game-changers? Well, most projects with a lifespan of more than 6 months, in my experience.

So, no, static typing is not about the type checks. It’s about you being able to comprehend a big, unfamiliar (or forgotten) codebase faster and with higher level of certainty, to make your way through it and to change it safer and faster. Type checking comes as a handy bonus, though. I won’t employ the “statically typed languages have faster runtimes” argument. (and by all this I don’t mean to dismiss dynamically-typed languages, even though I very much prefer static and strong typing)

And then people may say “your fancy tools and IDEs try to compensate for language deficiencies”. Not at all – my fancy tools are build ontop of the language efficiencies. The tools would not exist if the language didn’t make it possible for them to exist. A language that allows powerful tools to be built for it is a powerful one, and that’s the strength of statically-typed languages, in my view.

Share Button

30 thoughts on “Static Typing Is Not for Type Checking”

  1. FWIW, Amanda Laucher and I explored this question at Strange Loop a couple of years ago. tl;dr what Bruce overlooks is that types and tests have a logically duel relationship: types are universally quantified while tests are existentially quantified. This is what Dijkstra meant when he said “Testing can only demonstrate the presence of bugs, never their absence.” That is, what is true of code that satisfies the type checker is true for all possible executions, whereas something that’s true for a test is at least true for that execution (and maybe more, but the test can’t demonstrate that). This is why QuickCheck-style testing frameworks, which generate a lot of random test data and run hundreds or thousands of test executions, are a thing, and in fact they describe the property being tested with this random data as “forAll,” just as in universal quantification—”forAll” here is probabilistic rather than literal. Depending on the programming language and the importance of the property being tested, it could even be worth writing a “forAll” in QuickCheck-style testing, getting it to pass, then encoding the property using the type system and removing the test, because the compiler is now enforcing what the test was testing.

    People also often overlook that many domains have sufficient combinatorial complexity that even a generative testing approach like QuickCheck rapidly becomes infeasible: even 500 or 1000 randomly generated examples won’t tell you anything meaningful about the phase space of the code. This is where static typing really wins, thanks to universal quantification handling an infinite number of cases. It’s this sense in which static typing is strictly superior to dynamic typing with tests.

  2. Everything you say in paragraphs 5 and 6 are clearly doable in a dynamically typed language; I do them every single day in Common Lisp. In fact many of those features first appeared in either Lisp or Smalltalk IDEs, both of which are dynamically typed.

  3. 1. When I wrote that piece (a rather long time ago) I was indeed sloppy with my terminology, and “strong typing” and “static typing” got conflated in people’s minds, no matter how I tried to correct it.

    2. I think I gave the impression that static typing “wasn’t important,” when what I wanted to do was look at the cost & benefits of statically typed languages vs. dynamic languages.

    3. I’m a fan of strong typing, even moreso after I’ve seen the kinds of things Scala can do by virtue of its type system. I just want to know how much overhead there is, how many constraints it imposes — basically, how much do things enable, and how much do they hold us back?

    4. I also find static typing very valuable — again, if it doesn’t impose too many constraints. Look at Facebook, creating tools like http://flowtype.org/ to impose static typing on Javascript. Helping you find errors, however you do it, makes you program faster and I like that. Python is even adding type annotations to support type-checking tools.

    5. People have been creating and maintaining big, complex projects in Python almost since it has come out. Ruby on Rails continues to take over the Web space. I believe that as time passes, we’ll have to revisit our prejudices about what’s reasonable to do in dynamic languages. And from that, rethink what we understand about what makes people productive and what creates robust, maintainable systems.

    FWIW, Scott Meyers once told me that the two reasons for static typing is (1) so the compiler can generate more efficient code and (2) Tooling (which is mostly what you describe above). He notably did not mention error checking, although that could arguably be put into the “tooling” category.

    One thing for sure, these ideas will continue to evolve as we gain more experience. Ultimately my interest is in asking questions about how we can do better.

  4. First, I must say I’m honoured for getting a comment by the person whose book was the first thing I read about my favourite language – Java.

    1. I indeed didn’t meant to nitpick about those, just clarified for the sake of what follows.

    As for Scala – if it was only its type system, I would have loved it. Unfortunately it has a lot of other things in one place that make it less of a preferred language for me (though I’m currently using it).

    And yes, in the end, “the right tool for the job” always wins, though my personal preference is to avoid dynamically typed languages. But asking those questions and trying to answer them may ultimately bring a type system that has the best of both worlds.

  5. You know, developers who do a lot of Java, C# or similar languages end up spoiled by good tools. 🙂

    Good tooling is often the biggest factor influencing people deciding what language to learn or use. I’m not sure if this is a good trend in the long term, evolutionary speaking, because we may miss some great stuff while we were deciding what language to use based mostly on developer tooling.

    It’s pretty normal for any language starting out without much good tooling — it takes lots of effort (specially for dynamic languages, we know that).

    Since we humans won’t be getting much smarter as the decades go by but languages can, I think it’s pretty important that there are people who build stuff and are willing to put up with not-so-great tools for some language that has good things to offer.

  6. I liked your post even if I prefer dynamically typed languages nowadays. Another argument in favor of static typing is that it is self documenting through the interfaces. You don’t have to read inside the method you call or trust its documentation to figure out what kind of object you can pass as argument and what is returned.
    This need for extensive documentation is what irritate me in python.

    However your gain with dynamic language is that you have your feedback quicker…

    Working with static and dynamic language imply a different work flow maybe. With dynamic language, you’ll make assumptions on types you send and get and verify them using test very often. It’s OK because you don’t need to build and you stay focused. In static language you lie more on the ide and type checking to exercise your code less often.

  7. @Bozho You should resist “productivity” that can’t be refactored. You write software that gets easier to change over time. Languages should be designed for tooling, not the other way around. That’s good evolutionarily and we should detest languages that are designed to be “untoolable”. Dynamic languages resist tooling is a lie. SQL loves tooling. It’s just that “some” dynamic languages put tooling as the absolutely anus in the spirit of sheer arrogance that you don’t need tooling.

    BTW, interpreted languages are vulnerable to injection attacks.

  8. I mainly agree with you on the advantages of statically-typed languages about refactoring (you need test anyway) and navigation/self documentation (even if may be NOT enough). However a good IDE can help dealing with those problems with dynamic languages too.

    But I don’t see how statically typed languages are inherently better than dymanic ones. In my opinion dynamic languages can be way better than static-typed in many cases: runtime code changing, JSON/XML to domain model parsing, redesign of legacy systems without re-compilation. Better = easier to use + faster feedback + cleaner code.

    > ‘A language that allows powerful tools … that’s the strength of statically-typed languages, in my view.’

    Tools like Gradle and frameworks like Grails, Rails are here thanks to the dinamic nature of languages they are based on, so: I do not agree 😉

  9. To be honest, I prefer raw Spring MVC to grails (having used them both extensively). But that may be a personal preference 😉

  10. interesting.

    when speaking of java, I think I never check the javadoc via IDE directly. Either I go for online docs, or even more often, I just use F3 (in eclipse) to jump directly to implemetation (but that might be specific to my use cases, where all/almost all the stuff I use is open source, or I have sources available).

  11. There is one area that most people seem to overlook when they state that you should write tests to cover your code, and therefore static type verification is irrelevant. That area is the maintenance of tests.

    Most projects with high test coverage end up with more test code than production code. This test code needs to be refactored, and quite often outlives the implementation it is testing. Static type verification gives you some level of confidence that the tests still work as designed. I have seen big javascript code bases where tests have been refactored and are now subtly broken but still pass (or don’t even run at all without the test runner complaining!). Most of those issues with the tests could have been picked up with static typing, and refactoring tools which guarantee they are operating on the right types would have helped . Without static typing, the main way to verify that tests work after refactoring them is to break the code under test and check the test fails. This is a painfully manual process. You could write tests for the tests, but that leaves Turtles All The Way Down.

    I must admit that the only times I have been tempted to change to a dynamic language is when the system is composed of small components that communicate frequently over the network (think micro services). This is because these systems have weak typing over the network, and therefore the discoverability and verification that type safely gives you becomes a weak guarantee of the system’s behaviour. In those cases, I think I would almost prefer to write tests in a static language and the implementation in a dynamic language. That seems a bit counterintuitive though!

    Regarding the original ‘Strong Typing vs. Strong Testing’ blog post, I think that the author seems to forget that C++ and Java are particularly verbose static languages. e.g Scala list as follows (first class functions, implicit typing, built in foreach: all the things the original author liked.

    trait Pet {
    def speak
    }

    case class Cat() extends Pet {
    def speak() = println(“meow!”)
    }

    case class Dog() extends Pet {
    def speak() = println(“woof!”)
    }

    def command(p: Pet) = p.speak

    val pets = List(Cat(), Dog())

    pets.foreach(command)

    The whole conversation around not being able to do duck typing in static languages is a misnomer. Scala supports it (http://en.wikibooks.org/wiki/Scala/Structural_Typing), but it is rarely used. Just ‘cos a object has a method named fire, it doesn’t tell you which domain the term fire applies to (e.g. employees, guns or an event timers??)

    I think Scala has a fairly good balance of the two, as it is very lightweight to create domain objects compared to Java, .Net, etc and to use them meaningfully. There are other things that Scala doesn’t score so highly in (e.g. compile time!!!), but that is a different discussion for different day. The hunt for a perfect language goes on, but in the meantime, we need to take the best parts of the languages that offer the most useful features to our own individual projects.

  12. Bohzo, could you please elaborate on the reasons why you prefer Spring MVC over Grails? I am very interesting in hearing about your thoughts, having used both extensively.

  13. The first real programming languages I got my feet wet with were PHP and Javascript. Oddly enough, I eventually landed a full-time job writing C# MVC in Visual Studio. It has been a big adjustment. But looking back I can think of how messy my scripts were. All I had knowledge of was languages (scripts) that allowed me to do what now seems like almost anything with no consequences until I executed the dang thing.

    I eventually looked into OOP best practices. I know OOP is not for everyone, and is not completely relevant to the article since it’s more about structure. But I learned about structure before I ever heard the terms “strong” or “static”. (I think alot of my PHP was in the style of functional or declarative programming.) In fewer words, I learned good programming the hard way, no compiler, and implicit/non-existent type conversion.

    The c# compiler, and the incessant ‘Cannot convert type A to type B’ messages have been annoying, but good teachers. It has opened my eyes to the way in which JS and PHP convert almost everything behind the scenes. They didn’t get in my way.

    It’s good to finally read an article that helps not only distinguish “strong/weak” and “static/dynamic”, but also answers the why of these terms.

  14. To start with, grails was rather buggy 3 years ago (last time when I used it). Every step outside the predefined path was a struggle. A grails project part of a maven multi-module project? 4 days and lots of hacks. It didn’t give me that much over spring-mvc (it’s built ontop of it anyway, so most of the features are available there). And then comes tooling, which isn’t perfect for groovy (due to the reasons outlined in the article above)

    What I particularly liked, though, is the GSP taglib(s). The best template engine I’ve used, and I think now they made it a separate module so it can be used with non-grails projects as well.

  15. “If the only tool you have is a hammer, every problem looks like a nail.”

    It’s more of a religious argument. Those using statically typed languages drank the purple Cool-Aid.

    I’ve used both statically typed, C# most recently. And, Smalltalk on the dynamic front. Using C#, after Smalltalk is like going back to using a typewriter with white-out, after using a word processor. Smalltalk is the dynamic word processor. I am 10x more productive with Smalltalk.

    If you want some meat, here’s a well written article on the topic: http://smalltalk.org/articles/article_20070928_a1_ContinuousNuisanceDoubleTypeCognitiveDissonanceTax_v1.html

    In my Smalltalk IDE, I can select any method, right-click and instantly open a list of all implementors and/or all callers of that method. It’s just part of the meta-data. I want re-usable code, so polymorphic method names are a good thing. A simple example, send the “print” message to any object, letting the object definition encapsulate how to print any object type, or defer to the super-class’ generic implementation.

    For decades, in Smalltalk, code could be edited in the debugger, saved (incrementally compiled) and optionally continue running your program. Visual Studio has a partial implementation for .NET languages, not even close to Smalltalk’s.

    I don’t want to sound religious about Smalltalk, but I do prefer a word processor to a typewriter. That’s reality. Still, I do need to focus on the-best-tool-for-the-job. C# has a place in my toolbox.

    There are many ways to measure productivity and code quality. Ultimately, total effort per function point is a useful one. On page 46 of http://namcookanalytics.com/wp-content/uploads/2013/07/Function-Points-as-a-Universal-Software-Metric2013.pdf a table sets out number of months to implement a 1,000 function point program. Here’s a few metrics for dynamics and static languagues:

    Smalltalk 21 coding months
    Ruby 46
    C# 51
    Python 53
    C++ 53
    Java 53
    PHP 53
    JavaScript 71
    C 128
    Assembly 213

    In my work, I’ve decided to use both highly productive Smalltalk and .NET with C# for Windows and server apps.
    Here’s a Smalltalk for .NET, a work-in-progress (just the language, no classic Smalltalk IDE or debuggers yet)
    https://essencesharp.wordpress.com/

    For client-side web apps, Smalltalk and JavaScript. Here’s a tool that compiles Smalltalk to JavaScript:
    http://amber-lang.net/
    I can manipulate JavaScript objects with Smalltalk, vice-versa.

    Smalltalk has 5 keywords. C++ has more than 50 keywords. Learning Smalltalk is quick, easy and will teach you design patterns to make you better with other languages. Take a look: https://www.youtube.com/watch?v=eGaKZBr0ga4

  16. Thanks for that post! I need to fight JavaScript zealots at work… They want to write everything in JS. I hope that your text will open their eyes a bit. Languages like C# and Java are great for big projects because they have meaningful constraints and are self documenting. JS lacks these vital features.

  17. What about optional typing? A language that supports BOTH (static and dynamic) would seem ideal. And there is! Groovy, check it out, you’ll be glad you did. And all of this argument will sound silly.

  18. “statically (or strongly) typed languages”

    These aren’t at all the same, and Anyone who doesn’t understand the difference isn’t qualified to comment on the subject.

    “But let’s not nitpick about terminology”

    It’s hardly nitpicking to point out that someone is confusing two totally different things and then making claims about one while talking about the other.

    “don’t give you much, because you should verify your programs with tests anyway, and those tests will check the types as well – no need for the compiler to do that (especially if it makes you less productive with the language)”

    I didn’t know anything about Bruce Eckel, but I now know that he’s a fool.

  19. Even Google, FaceBook and Twitter went in search of strong typing and started to use those languages its just those who are uneducated and don’t understand strong or static typing that think weak typing it is better. The more sophisticated and developed languages allow weak typing anyways. JavaScript is the final fronttier once weak typing goes out of the browser apart from saving the industry and business billions the fire of weak typing will go out of the universe. Or those who have poor tools and like poor tools.

  20. I have never been in a situation where the compiler in a statically typed language has actually caught any errors which would otherwise manifest at runtime, most errors which the compiler catches are when I make typo whilst typing the type identifier or when I use int instead of long or float instead of double or some other trivial type error which doesn’t even occur in a dynamically typed language.

    The problem I have with most static type systems (I’m mostly referring to the type systems of Java, C, C++, C#) is that types represent the low-level implementation of a data structure or the low-level representation of a value in memory e.g. int represents a 32-bit integer value, long a 64-bit integer value etc. When I am programming in C where I mostly work with bits and raw memory and I’m trying to squeeze every drop of performance out of the program this makes sense however when I’m working in a high-level language such as Java where I’m mostly trying to map a complicated idea into code, I really do not care about nor do I want to be bothered with how many bits are in my integer value. I would prefer to just have a Number type which automatically uses the representation which is most efficient for the platform on which the program is running, and if a calculation causes overflow the Number type should automatically switch to a BigNumber instead of me having to anticipate whether my program may encounter situations which result in overflow and having to explicitly state it in my source code (often resulting in poorly performing code since a BigNumber may indeed be unnecessary in the majority of cases), turns out this is behavior which I’m looking for is actually how the core behavior of dynamically type systems. Yes I can implement a Number class in Java but then that essentially becomes a dynamic type system which brings me to another ideal of a type system. A type system which lets you OPTIONALLY specify types with the specifications being either specific 32-bit, 64-bit integer values or a more generic (semi-dynamic type) such as a Number.

Leave a Reply

Your email address will not be published. Required fields are marked *