"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.
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".