"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."
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.
Funnily enough, my experience has been quite the opposite.
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".
I always wonder, if languages like Haskell are so good, why don't we see a lot of amazing open source projects built with these languages? If you can get things done faster with fewer bugs it should be reflected in real life code bases. Yet I haven't seen it.
"The only arguments that hold water, in terms of programming language suitability, are bold, finished projects." http://t.co/ahOBAnXZ18
Programmers write the same number of debugged lines (or expressions) of code per day, regardless of the language they write in. A language only enables faster development if it requires fewer expressions to say the same thing. That is why high-level languages trounce low-level languages when it comes to productivity. I admit to not being very familiar with Haskell, but if it is not more concise it's not likely to be more productive.
I don't believe that. The speed of producing code is not relative to typing speed. LOC per day depends on the complexity of the lines. I can write 500 simple lines or 50 complex lines of code in a day. I'd agree that higher level languages deal with some of the complexity for you, like memory management. But I bet the business logic takes a similar amount of time in almost any language, assuming the developer has the same amount of experience in each language.
I don't think typing speed factors into it. It's more a factor of cognitive load. While programming you are working at the level of "expressions", and your speed is limited to how quickly you can turn business logic into appropriate sets of expressions. Programming languages that are lower level require more expressions to say the same thing, hence are slower to program in. I don't think it's a controversial notion to say that assembly is less productive than C, which is less productive than Python. The reason is the number of lines (or rather expressions) needed to achieve a piece of functionality. The business logic most definitely does not take the same amount of time when written in assembly compared to python. The higher level languages tend to be more productive, but it is only a given if the higher level language is actually more concise.
The way to reduce lines of code is to create good abstractions. Some languages may be better at this than others, and I think this is more important than the type system of the language.
There are upsides and downsides to working in something with a sufficiently useful type system versus working in a lisp - even as pertains specifically to abstractions.
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.
I think maybe it's just another example of the worse-is-better principle.
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.
> Most people just don't naturally think of processes as a composition of mappings from values in a domain to values in a codomain.
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:
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…
Case like that are straightforward, and people still manage to botch them. Which is why I suspect there is something deeply wrong, or at least fundamentally different, about the minds that produce such mistakes.
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).
It is interesting that I often see people coming primarily from a strongly typed language say this kind of thing, whereas people used to working in dynamic languages rarely make obvious type errors.
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.
If you value time spent writing code higher than preventing bugs, I have doubts about the size of codebases you were maintaining. The reality I have experienced is that working with a large codebase in Python (talking 100kloc+ here, and teams larger than 3) is an absolute nightmare. Lack of explicit typing on argument values and returns means that everytime you add an argument to a function, or move things around, you are frantically grepping trying to find all the callers and hoping that you caught all of them. Add in the more functional aspects such as passing functions around and lambdas and *args and kwargs and different return types depending on function behavior and it gets very ugly. Given the fact that adding functionality and refactoring are the most common activities when working with a large existing codebase, this becomes very old, very quickly. I suspect that Guido's newfound interest in explicit typing coincides with his employment at Dropbox and Google and realizing the very real pain that comes with maintaining large Python codebases.
I work with several codebases, (+250Kloc, +150Kloc and 50Kloc), all in Python (plus some bash scripts). The teams working on them are obviously bigger than 3 people.
Though they present challenges, I wouldn't consider them "a nightmare", and the number of bugs introduced by the lack of typing are absolutely minimal.
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.
I agree with you on one part, writing static language INITIAL code, does generally take more time. Depending on the language, it can vary from slightly more in something like Go (where composition is valued over type hierarchies) to a great deal longer in languages that encourage lots of castle building and type relationships.
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.
I have also been in the industry for about as much and noticed:
* 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")
> * 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.
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.)
You can fix catch these errors with a suitably advance type system
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.
Yes. Though you can get pretty far with the extensions built into a mainstream compiler like ghc these days. If you are already doing Haskell, then the investment necessary for getting familiar with these is lower.
> I have seen no data to backup this claim which would override my personal experience.
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.
"I have seen no data to backup this claim which would override my personal experience"
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.
The problem was in the phrase "other things being equal". Other things are never equal. Studies try to make them as equal as they can, and wind up differing as to which things are more important to get equal, and wind up with different results.
I know that this is a cop out argument, but trust me, we all went through this phase. Eventually you'll see static typing as another tool in the box, that is only helpful and doesn't get in your way.
Told you it was a cop-out argument. I'm just telling you what my experience was when encountering this topic, and then my conclusion after revisiting it many times over the course of a dozen years or so.
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.
I do care, it's why I made the comment I did in earnest. The lack of a fully formed argument was simply because of my lack of time and typing on a phone. Point taken: I will try to be more kind even to strangers on the internet. You need to let this go.
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.