People make this sort of claim all the time, but my personal experience has not borne it out, and I have seen no data to backup this claim which would override my personal experience.
First, I find I have to write tests nearly as religiously in statically types languages, so I don't find that a convincing argument.
Second, most bugs I've experienced in large code bases using a dynamic language are not the sort that would be caught by most typed languages. On this point there is even some data that backs up my personal experience (http://vimeo.com/74354480).
Third, writing code with a static language takes more time in my experience. Maybe it prevents a tiny fraction of bugs, but for most things I'll take saving significant time and cost over preventing 3% of bugs (I made up that number).
In short, I'm not sold on these arguments for static typing.
I never felt the need for TDD or such… until I used Lua. I was drowning in bugs for a tiny 200 lines program! I just couldn't finish. Then I wrote some visualization code, then more of it, until I had the equivalent of a domain specific debugger. It worked. I found my mistakes, and corrected them. Then I though "that is where TDD comes from".
A statically typed languages such as Ocaml or Haskell would have caught the vast majority of my mistakes right away. Heck, even C++ would have helped. In my experience, writing code that "mostly works" is faster with a statically typed language. So many trivial tests you don't have to write, so much debugging you don't have to do…
> Second, most bugs I've experienced in large code bases using a dynamic language are not the sort that would be caught by most typed languages.
So, no Null pointer bug?
Also don't underestimate the number of domain logic errors that could have prevented by encoding the domain logic into the type system. Type systems can encode many invariants, they're not limited to "this is a bool, I wanted an int".
"The only arguments that hold water, in terms of programming language suitability, are bold, finished projects." http://t.co/ahOBAnXZ18
Here are some papers that underbuild this argument: http://page.mi.fu-berlin.de/prechelt/Biblio/jccpprt_computer... https://files.ifi.uzh.ch/rerg/amadeus/teaching/seminars/semi...
You can doubt, but I'm speaking from some experience with both. That doesn't necessarily mean I'm right, of course, but take it for what it's worth.
Great as it is, the language itself (and functional programming in general) tends to be pretty opaque to most programmers. Purity is powerful; we've all heard it before so I'll spare us yet another apologia. But, as my time spent TAing an introductory course in Scheme taught me in agonizing detail, purity is painful, too. Most people just don't naturally think of processes as a composition of mappings from values in a domain to values in a codomain. For better or for worse, the "process as widget-fiddling" way of thinking comes much more easily.
This is most difficult to accept, because I do.
It's like programmers are divided into two camps: those who think procedurally, and those who think mathematically. I have noticed lately how this can influence API design, down to things as simple as this:
void latin9_to_utf8( std::string& latin9str);
std::string latin9_to_utf8(const std::string& latin9str);
The first interface is a design mistake: it encourages side effects, and requires more code to use in practice. But it is also a very natural one: you want to convert strings, so you write a function that converts strings (KISS). The second interface is better, but more convoluted: instead of just converting strings, it returns converted strings.Guess which ended up in a real program I worked on…
Where things really fall apart is using recursion instead of loops. Especially when the time comes to mention tail recursion.
Though for this particular mistake, it may have come from some misguided notion about performance (like perceived overhead of constructing a string then passing it up the stack).
As for things like recursion and closures… I just avoid them because I know my colleagues will recoil in horror with such uselessly indecipherable excess of cleverness.
The size of Sonatype Snapshots and Central is roughly around Rubygems.org size I think. But so so so many Ruby projects are half baked, while so many public JARs are so much more mature (if often not very pleasant to work with).
This says something about what tooling does to people. Haskell people say they think in types but this make it look wrong. It would appear they are less good with types when you take their tools away. People working primarily in dynamic language probably end up writing programs that are mostly correct in that respect because they have no other choice to become productive.
This looks similar to how people who are raised in clean environments are more vulnerable to bacteries. This is not a bad thing: we still improved life expectancy a lot with hygiene and antibiotics. But we got weaker immune systems and allergies.
In my opinion, the maintainability of a codebase is not that much a function of the language, but of the architecture and structure of the code.
That said, I have been in industry for over a decade in a half, and the most miserable, hellish experience I ever had was dealing with a large (100k+) non-trivial consumer facing Python application. It was a special type of pain, with novel errors happening in production on a regular basis 4+ years into the project. Refactoring was horrifically risky, and the analysis tooling stack is AWFUL... to the point that when used, it was often outright wrong.
I will gladly give up those cost savings on the leading edge, to have a product I can actually maintain in the long term. I suspect this inability to maintain apps writing in certain dynamic languages is why they have culture bias towards starting over (and being proud of it, "version 2.0, rewritten from scratch!")... which I found a bit shocking at first versus C, which would trend far more toward refactoring.
* Sometimes, the time it takes to build something in a statically typed language is much longer. Long enough that by the time it is built it might not matter because somebody would have built it in node.js, ruby, or python. I really depends on the market.
* Type mismatches in a Python program are not the biggest and deadliest bugs. Unit tests and integration tests (that should be there anyway) should catch those well.
* Often static type languages that have pointers (C, C++) hide much deadlier and dangerous types of bugs -- pointer bugs like "wild pointers".
* Static type language that don't offer protocol checks also can't easily detect bugs where you open a file handle, close it, then read from it. It knows it is a FILE or some iostream but it doesn't help much there. I've heard other more advanced languages have something in place to handle that.
* The above point coupled with being able to use the REPL on a production system and code has saved significant debugging time. In a complex system there could be an interaction that is hard to reproduce and this one system is in that funky state. Can log in via SSH into it, edit the code to add some debug statements and reproduce it again.
In summary, I would like Python to have a standard type annotation syntax that good linters will be able to check for and IDEs will know how to read and use. But it would not be at the top of the list of features I would like Python to have next.
EDIT: (sorry, last sentence should have said "it would not be" instead of "it would be")
You can fix catch these errors with a suitably advance type system; but often a simple construct like Python's with-statement works well for most cases, too. (If you have higher order functions or macros or RAII, that kind of construct doesn't need to be build into the language, even.)
Not that it matters in most cases though. A programmer picking a language for a project does not realistically have access to the full design space of type systems. If the only implementation of a language with some type system feature is an interpreter for a core calculus written in Agda and not touched for the past several years, that feature is effectively unavailable. Most programmers are not in the business of designing and implementing custom type systems (and associated languages) and probably wouldn't gain enough from it for it to be worth the work of learning how anyway.
I think that there are two reasons why good data on this is not forthcoming.
The first is that for a long time the dominant statically typed languages have been C++, which forfeits most of the potential advantages of static typing by also being weakly typed, and Java, which handles types somewhat better (while by no means being state-of-the-art) but brings its own productivity challenges.
The second is, the kinds of enormous long-lived maintained-by-50-full-timers hoary old enterprise applications that I think most fans of static typing have in mind when they say, "I wouldn't want to maintain that in a dynamic language!" aren't often written in dynamic languages. This in and of itself could be seen as evidence that dynamic languages really are unsuited to the task.
Perhaps the more interesting evidence, though, is the tendency of what few larger- and longer-lived dynamic projects there are to become famous for messy code. This hits at what I think fans of static typing are really talking about - that doing any sort of large-scale refactor in a dynamic language is a terrifyingly error-prone experience. You need to rely on ever-fallible humans to have written tests that exercise all relevant code paths in order to verify that the refactor was successful in even the most basic terms. For someone who's used to having the compiler quickly, reliably and exhaustively provide the same feedback, having to fall back on the source of all known bugs (programmers) to do the same job leaves one feeling a bit exposed.
There have been attempts at measuring the effect of type systems, see for example this excellent presentation for some references: http://www.slideshare.net/Felienne/putting-the-science-in-co.... One of the studies mentioned here does claim that, other things being equal, static typing helps find bugs quicker.
Why be so condescending? Why assume things you have no way of knowing?
What do you want me to say? I have no illusions about the integrity of my rhetoric—it has none. It was a statement made without much thought based on my experience. It does not require or request your approval.
As bullish as I normally am on static typing, I agree with that wholeheartedly—today.
I'm also completely convinced that a language with good enough record row-subtyping will take over as being far better for EDA than any dynamic language within the next 5 years.
That's the thing about research in types—it's almost always about discovering new techniques to appropriately structure and represent more complex programming techniques. It can lag for reasons of sophistication, but once it's in place it'll be hard to beat.
Already I feel it's absolutely the case that good static types (Haskell I'm most familiar with, but I imagine it's the case with Scala, OCaml, F#) are a win for short, one-off scripts. The difference is that the types you use are looser than the ones you'd use for a super robust application. The typing ends up looking a lot more like the typing you'd implicitly apply to a Python program [0].
So, "use the right tool for the job" applies to typing technology as well. Static types, I believe, will eat the use cases of dynamic types.
[0] This is essentially an extension of the "unityped argument". If you program in Haskell in a "always IO, frequently ambiguous" fashion then you can get all the flexibility you're used to in scripting in Python with little overhead.
My personal experience working on large systems without unit tests (among others a 200.000 line javascript codebase) is that as long as you adopt good coding practices around how you define clearly typed interfaces the lack of unit tests doesn't hurt you that badly and there is no wild-growth of bugs. Using the jetbrains IDE's also made a big difference for me due to the static code checking. I want to see the little code quality box at the top of the scrollbar turn green, and the process of doing that tends to weed out a lot of those bugs that a unit test or a strict type system would catch.
I do think that if you're building large-scale or long-lived systems, it is not any faster to use a dynamically typed language. I've written lots of code in both strictly typed and loosely typed languages, and I find that while I enjoy a loosely typed language more, I am equally productive in the strictly typed environment because strict typing makes it easier to write reliable code from the outset.
It is a very nice Javascript-without-all-the-weirdness, however.
Like most things, I think this depends on context. Doing exploratory data analysis in a static, strongly-typed language, for example, is extremely painful. And writing quick, one-time scripts in such languages is usually more trouble than it's worth. For this reason, Python is a wonderful language for doing data analysis and writing quick one-time data munging scripts (and yes, I realize that Python can be considered strongly-typed).
On the other hand, I've now been exposed to languages like OCaml and Rust, and have had time to think about Java, and I now have a different attitude when I'm building a large application that will have to be maintained. I'm beginning to feel extremely vulnerable when I use a language like Python or (worse yet) Javascript to build such applications.
It's not that I'm not a Javascript hater. I like Javascript in general -- it has a kind of functional feel to it, it's easy to prototype quickly, you can use it client and server, nice ecosystem, and unlike so many I actually enjoy async programming. And I've long enjoyed Python for similar reasons.
But for building a robust application that will have to be maintained a long-time, you have to religiously write tests in these languages or you'll be buried in bugs. And even then, you still may be buried in bugs that a static, strongly-typed language would have detected. For this reason, I'm looking hard for an static, strongly-typed alternative to Javascript for the browser (i.e., a language compiling to JS) for building large applications. And Ocaml's js_of_ocaml is definitely on my list of things to evaluate.
So the programming language philosophy that I adhere to now is encapsulated in the hackneyed old saying "use the right tool for the job".