Paul… uh… NoLastName has an excellent post on silver bullets and functional programming. He cites studies, makes logical connections… why, it’s hardly a blog post at all!
Anyway, I starting writing a comment, but it grew and grew, so I am posting it here instead. Read his post first….
You do a good job of laying out the issues, but I disagree that it’s likely that functional languages are vastly more productive across the field of “larger” systems. (By “vastly,” I’ll take “just” 5x faster across a broad variety of programming tasks.)
I am skeptical that the success of functional languages in smaller tasks (they dominate academic programming contests or the USN prototype you mention) provides strong support to the idea that they strike at “essential” not “accidental” issues. That is, it’s not that they’re silver bullets, it’s that they’re good languages.
You argue that the defect rate staying constant (per KLOC) argues for the “essential” case, but this is a long-known phenomenon that holds true across a broad variety of languages. (I think the original studies were by Boehm, but it may have been Capers Jones.) I believe the way to read this is that “expressive density” is beneficial in programming languages. In other words, what you describe as “packing more into fewer lines” is itself a benefit (but not a silver bullet).
As you recognize, concurrency is the anvil on which will break today’s popular programming languages. The threading models in the popular C-derived languages are demonstrably intractable, just as C’s memory model was intractable and has led to decades of misery. But this is clearly a problem of “accidental” quality: it’s not that programming dual-cores falls short of 2X times easier than single-threading, it’s that doubling your cores makes your programming task harder.
Functional languages, with their guarantees on the semantics of assignment, have a huge leg up on concurrency. It is much, much easier to build a system that automatically and efficiently manages the distribution of work when you have those guarantees. However, I don’t believe there is any language that has reached the “finish line” in terms of expressive density, interoperability with mainstream languages (i.e., a C interface and “zero-config” interoperability with one of the two major managed platforms), and runtime efficiency.
When there is such a language, though, it’s worth pointing out that it’s embrace by the mainstream is still far from guaranteed. LISP is probably as close to that “finish line” as anything. LISP has been around for 48 years! And yet it is the most-abandoned language in programming (perhaps C has gotten to the point of contending that title). Smalltalk is universally admired for its embodiment of OOP, the construction approach that absolutely dominates mainstream programming. And yet Smalltalk doesn’t show any sign of a dramatic increase of acceptance. What is increasing rapidly? Ruby. To me, Ruby seems a Smalltalk-like language with a primitive development environment. But maybe text editors and command-line-like shells are actually benefits. … The subject of another post … My final point regarding the mainstream embrace of languages: if I were to design a language that I hoped to be popular, I’d use curly brackets to denote code blocks.
Productivity jumps (and silver bullets, if they’re at all possible) arise from embodying in a language (or perhaps a framework) new concepts that are broadly applicable. Languages have long been the vehicle for delivering these advances because facilitating the use of truly broad concepts (use of functions as first-class objects of reason, modularization of state and behavior in “classes”, search — a concept embodied in Prolog and Oz) generally requires semantic guarantees.
This certainly advocates for the power of domain-specific languages embodying domain-specific concepts.
For general-purpose languages, I think the challenge for the manycore era is to discover a new concept (and I don’t think it’s software transactional memory, or message-passing as we conceive it today) that allows us to exploit parallelism. I think the challenge for the perhaps-not-vanishingly-far future is to develop new concepts that relieve, by orders-of-magnitude, the tasks that we commonly consider programming.
For instance, right now there’s a huge debate about explicit typing. Is it helpful or not to declare that a variable is an integer? Meanwhile, there’s not a huge debate about the value of unit-testing. That is, everyone agrees that it’s helpful to declare that a variable always be greater than 0 and less than, say, 2^32. Is the irony not clear? Unit-testing assertions are type information: constraints on the interpretation of the inherently-plastic memory representation. It seems to me clear that unit-testing specification should migrate into languages, not remain the province of libraries. It ought to be the case that a failure of the unit-testing suite have the same, if not greater, ramification that comes from assigning a signed int to an unsigned one.
More generally, one can see that there’s a conceptual failure with the idea of a purely static compile phase, followed by a monolithic runtime representation (we conceive of differences between “Debug” and “Release” runtimes, but those are simply compile-time conventions). Microsoft’s C++ compiler has an advance on this model, with the idea of instrumenting a build, profiling it, and then altering the code-generation based on the results of that profiling. That’s how testing should work! There is useful information about program structure that could be automatically extracted from unit-testing; if nothing else, that a particular class or method is a source of trouble. Far, far more speculatively, might it be possible for the computer to generate alternatives and present to the “programmer” those that satisfy the tests/constraints? Back in 1990, I speculated that genetic programming might be the only thing that could use up all the power of the then-distant gigahertz-speed processors. (Little did I expect Office 97.)