For many years we've been using statically typed languages for the safety they offer. But now, as we all gradually adopt Test Driven Development, are we going to find that safety redundant? Will we therefore decide that the flexibility of dynamically typed languages is desirable?
I've been a statically typed bigot for quite a few years. I learned my lesson the hard way while using C. Too many systems crashed in the field due to silly typing errors. When C++ came out, I was an avid adopter, and rabid enforcer of strong typing. I scoffed at the smalltalkers who whined about the loss of flexibility. Safety, after all, was far more important than flexibility -- and besides, we can keep our software flexible AND statically typed, if we just follow good dependency management principles.
Four years ago I got involved with Extreme Programming. I liked the pragmatic emphasis it placed upon developing software. I also liked the emphasis it put on testing. Since then I have become test infected. I can no longer concieve of writing software without using test driven development. I can't imagine not having a comprehensive suite of unit tests to back up my development.
About two years ago I noticed something. I was depending less and less on the type system for safety. My unit tests were preventing me from making type errors. The more I depended upon the unit tests, the less I depended upon the type safety of Java or C++ (my languages of choice).
I thought an experiment was in order. So I tried writing some applications in Python, and then Ruby (well known dynamically typed languages). I was not entirely surprised when I found that type issues simply never arose. My unit tests kept my code on the straight and narrow. I simply didn't need the static type checking that I had depended upon for so many years.
I also realized that the flexibility of dynamically typed langauges makes writing code significantly easier. Modules are easier to write, and easier to change. There are no build time issues at all. Life in a dynamically typed world is fundamentally simpler.
Now I am back programming in Java because the projects I'm working on call for it. But I can't deny that I feel the tug of the dynamically typed languages. I wish I was programming in Ruby or Python, or even Smalltalk.
Does anybody else feel like this? As more and more people adopt test driven development (something I consider to be inevitable) will they feel the same way I do. Will we all be programming in a dynamically typed language in 2010?
For my own systems development, I still feel dynamically typed languages are a bit too loose and slippery. I don't feel comfortable with what happens to the notion of contracts in a dynamically typed language, for example. I asked Guido about that here:
It is interesting to me, however, that you attribute your "conversion" as being a result of your adopting test driven development. I have kind of been kicking around the idea of developing a "system" in Python, but haven't tried it yet. I won't really know how I'll feel about building a system with a dynamically typed language until I actually try that.
Anyway, I suspect that given my contracts-oriented personality, I would be happy building systems with a type *inferring* language -- one in which I get the reduced finger typing and reduced code clutter of a language that doesn't need to declare variables, but in a language in which variables do actually have a type known at compile time.
Gosling's comment about the difference between a prototype and an industrial strength application is valid. On the other hand, I think Test Driven Development is the single most important discipline in the creation of industrial strength applications. And if TDD is also the thing that makes static typing redundant then...
I feel your pain. I've been coding almost exclusively in Ruby for about a year now, but it appears circumstances will drive me to go back to coding in Java. Dynamic languages engender a different mindset, and it's taken some time to fully get my head around TDD as well, but the combination is powerful.
A few years ago now, I was also a static typing bigot. I just so happened to discover TDD slightly before I made the switch to Ruby. Since then, I've attempted to keep as many of my programming activities as possible centered around Ruby.
I say "just so happened", because my switch from the static to the dynamic frame of mind wasn't driven by TDD. I do, in fact, use TDD extensively with Ruby, but I guess I found another set of training wheels that allowed me to see strong dynamic typing for what it was. I can see how, psychologically, you could use TDD to get you there, but I'm not convinced that TDD is necessarily all that big a driver toward accepting a dynamically typed language. It's a much-debated topic, but I don't think static typing is any "safer" than dynamic typing. To a static "bigot", it may seem that static typing is safer, and there are a million arguments you could use to say it is. But, in practice, it just doesn't make a big difference.
A lot of us "bigots" just need something that makes dynamic typing feel permissable for the short time it takes to realize that there was never anything to worry about. Of course, it's a good idea to keep doing TDD, even if you were using it as a training wheel equivalent. But, it's also a good idea to do it in statically typed languages.
Your experience and motivations are probably very different from mine. Just adding another perspective.
The one thing that static typing gives me that is hard to replace in, say, python is dependency tracing.
It is not generally feasible to reliably find every use of C.m in a medium to large python system. It is feasible in C++ and in Java (with refactoring tools) it is automatic.
This goes to maintenance and evolution through refactoring.
Type (or 'protocol') inference (even if imperfect) would be great with a dynamically typed language for this type of analysis. I suspose that was achieved for smalltalk with the refactoring browser, but the python equivalent has a way to go.
> Excellent post. It is funny you came out with this topic > out of the blue.
I remember this very topic coming up in the 1990s in the Visual Basic world. The Microsoft book "Advanced Visual Basic 5.0" (and similarly for 6.0) written by the British software house "The Mandelbrot Set" included a chapter "Programming with Variants". Basically, in Visual Basic variants are a sort of typeless type that could, in effect, behave as any other VB data type. As I recall, the case was well argued (i.e. it convinced me) and included the idea that it was the programmer's responsibility to ensure that the incoming data was correct and that relying on static typing was too unreliable.
Although the book was well received, the particular chapter came in for a lot of savage critisism. Unfortunately, this was in the days before the concepts of design-by-contract and test-driven-development had been fully concieved or were well known outside acedemia so it was very difficult to carry the argument and the concept never really caught on in the VB community.
>I also realized that the flexibility of dynamically typed >langauges makes writing code significantly easier. Modules >are easier to write, and easier to change. There are no >build time issues at all. Life in a dynamically typed world >is fundamentally simpler.
Nice article! However I don't have chance to use dynamic language in my work right now... Hope I can execrise this later.
However, I don't really think I spend a lot of time in java to make there is no build error... Could you share more about how dynamically typed language help you in development?
> However, I don't really think I spend a lot of time in > java to make there is no build error... Could you share > more about how dynamically typed language help you in > development?
Have you ever added an exception to a method and then had to change many other methods with either 'throws' clauses or try/catch blocks? Have you ever changed the type of a function argument and then had to find all usages and change them? Have you ever changed a class and then had to recompile, and redeploy, all the classes that used that class? These are just three of the issues that don't occurr nearly as often in dynamically typed languages.
> Have you ever added an exception to a method and then had > to change many other methods with either 'throws' clauses > or try/catch blocks? Have you ever changed the type of a > function argument and then had to find all usages and > change them? > Have you ever changed a class and then had > to recompile, and redeploy, all the classes that used that > class? These are just three of the issues that don't > occurr nearly as often in dynamically typed languages.
OK but if your change affects the interface or calling protocol you still have to find all the callers in either case. This is quite feasible in the statically typed case, whether you use grep or a fully fledged analysis tool.
But how would you find the call sites in a dynamicaly typed language? For what string would you grep?
Putting it another way: a static type system, for all the effort it requires of the programmer, creates a static dependency graph for the program. And this is useful in analysis and refactoring.
Don't get me wrong, I find the dynamically typed languages more productive overall. But the "find all callers" problem represents a very real practical difference that I have not seen addressed in the static vs. dynamic discussion.
> Anyway, I suspect that given my contracts-oriented > personality, I would be happy building systems with a type > *inferring* language -- one in which I get the reduced > finger typing and reduced code clutter of a language that > doesn't need to declare variables, but in a language in > which variables do actually have a type known at compile > time. Something like OCaml ?
> But how would you find the call sites in a dynamicaly > typed language? For what string would you grep?
Even in a dynamically typed language you can usually use something like grep to find what you're looking for. Just because a language is dynamic doesn't mean you use that feature to the fullest extent all the time and never write static code. That would be scary and unmaintainable.
In other words, most of the time you just grep -r for the function/method/class name you're looking for. The rest of the time normal code modularity helps to constrain where you need to look.
> Putting it another way: a static type system, for all the > effort it requires of the programmer, creates a static > dependency graph for the program. And this is useful in > analysis and refactoring.
This is also possible at least in Python, tho it can be compute intensive and to some extent relies on observing points of call for usage. This relies on most code being more or less correct, but in incrementally writing code, that is usually true. Another approach is to use a call tracer to observe calling patterns and mesh that info with data obtained other ways. There may be other methods that could be used like bayesian analysis or what-not.
None of this is as deterministic like a static type graph but based on actual experience doing a bit of this kind of analysis, and writing tools that use the results, it can work remarkably well. Some of it requires faster computers, but those are coming.
To address worries that all this fluffiness results in hidden bugs, use unit testing and code coverage tools to make sure that code is really working as it should. There's plenty of ways to write bad code that still compiles, so even in a static language nothing really beats actually running the code through its paces and verifying correct behavior.
I think the bottom line is that dynamic languages will replace static languages in many things, but it will take time for people to get used to the idea of working with them. Most of the common criticisms evaporate with actual experience, and I think over time we'll see increasingly powerful tools, especially given another couple of orders of magnitude increase in computing power.
> It is interesting to me, however, that you attribute your > "conversion" as being a result of your adopting test > driven development.
Color me skeptical about claims like "we'll catch it in testing." History tells me that when schedule pressure hits, things that don't HAVE to be done fall on the floor. With static typing you don't get the option to be sloppy and find typing errors at runtime (often in production.)
And I don't ascribe this to the thoughtful developers in this forum, but there are many who just won't follow through on the testing requirement, even without external pressures. They want to get things out quick and move on to the next bit of fun.
However, does that mean that we should get rid of static type checking altogether? I think that's too radical, any kind of static checking is extremely useful. However, we should always make an effort examine the extent of the static dependencies inside our system. Large systems tend to use a combination of both static and dynamic checking. For example, Eclipse is a large scale progam however its able to overcome the problems of static type checking. I don't believe that there's a Smalltalk program of comparable size to the Eclipse project. The evidence therefore exists that you can build large programs with static typing, however its interesting to know how to do this.
Large programs developed in a statically typed language is successfully acheived by exploiting a Component Model. That is, you structure the composability of your application in terms of a component model that uses dynamic typing. The advantage of using a statically typed language like Java over a dynamic typed one like Smalltalk is that with Java you can develop in both modes instead of exclusively in one.