How to Pick a Language
 
How do you pick a programming language, to learn or to use for a project?
 
This article probably overlaps a lot with what others have already said on this topic.  But I try to add a categorization of programming languages that reflects the way they are discussed on blogs and discussion sites.  I think these categories are often implicit in discussions on the programming sub-reddit, Hacker News, or Lambda the Ultimate, but never made explicit for the benefit of younger developers.
 
Each section describes languages in a category, how they are similar, and what distinguishes them from each other, along with a little history.  Each section ends with reasons for choosing a language from that category (or not), and criteria for choosing between languages in the category.
 
Let me know if these categories make sense to you, or what you think a better categorization might be.  Posted to Hacker News for discussion here.
 
  1. 1. The Cs
 
C appears often on lists of languages every programmer should know.  Usually the reason given for learning it is “it will teach you about how your computer actually works.”
 
A common answer to the question “When should I use C?” is “when you want your program to run fast.”  This is usually true, but might not be true for programs that benefit from just in time compilation.  Or certain optimizations might be easier to write in a higher level language, so for a set amount of development time the program written in the higher level language could be faster than if you spent the same amount of time writing it in C.
 
My answer would be “When you need more control than your current programming language gives you.” C almost certainly gives you more control over what happens, when it happens, and how it happens.  If you want the computer to allocate exactly this many bytes, right now, and deallocate them only when you say so, use C.  If you want to write something higher level than assembly, but have a good idea how the machine code it compiles into might look, use C.
 
C++ and Objective C are object oriented extensions of C.  Of these two, most observers would say C++ “won.” It has more developers and more code written in it.  C++ stresses efficiency and repurposes much of C’s syntax for object oriented constructs.  C++ has lots of features, but that can be a problem when they interact with each other in unintuitive or opaque ways.  My understanding is that real world C++ development consists of choosing some subset of the language to use and sticking to that.
 
Where C++ favors static types that can be checked by the compiler and compile time method resolution, Objective C more closely resembles the dynamic Smalltalk model of object oriented programming.  Objects pass messages to each other, and the receiving object decides how to respond to the message, or passes it on to some other object altogether.  This allows design patterns that are more difficult or impossible in C++.  Objective C has a distinct syntax for message sending to make clear that sending a message is conceptually very different than a struct access.
 
Objective C message sending is highly optimized, but has more overhead than a C++ method invocation.  If that is an issue performance sensitive code can be written in C (or even C++) while maintaining the advantages of a dynamic runtime for higher level application logic.
 
While C++ has more coders and code, Objective C cannot be considered dead for one important reason.  The Mac and iPhone are growing rapidly, and the language for writing native applications for these platforms is Objective C.
 
Pick one of the Cs if:
You want as much control over your computer as you can get, without writing assembly.
 
Pick C if:
You want a simple, portable language with lots of libraries and example code.
 
Pick C++ if:
You want more features than C and you’re willing and able to deal with the added complexity.
 
Pick Objective C if:
You want to develop for Mac or iPhone.
 
  1. 2. Virtual Machine Languages
 
Some languages compile into an intermediate form between human readable code and processor specific instructions, called bytecodes. Bytecodes can run on any hardware and operating system for which there is an interpreter.  In the 1990s Sun introduced the Java Virtual Machine (JVM) as the bytecode specification for the Java language.
 
Java is similar to C++, but simpler and eliminates some things that make C++ programming difficult, like pointer references and manual memory management.  The JVM provides features like garbage collection, Unicode strings, and threads.  The JVM also makes possible security checks that are impossible in programs compiled to native instructions.  Sun promoted Java as a good language for programs delivered over the Internet based on these characteristics.
 
Microsoft saw Java’s popularity, and tried to make their own implementation.  But disputes with Sun over differences in their implementation and Sun’s specification led to Microsoft dropping Java and making a similar language of their own, C#.
 
C# learned from some of Java’s mistakes.  For example, where Java has primitive types (int, float, etc.) for efficiency in addition to objects, C# uses a technique called “auto-boxing” that allows programmers to treat primitives like objects in their code while keeping most of the efficiency advantages of primitive types.  Java added auto-boxing later, but there are still cases where there is a distinction between “boxed” and “unboxed” types that programmers need to understand.  Coming later has made it easier for C# to add features like closures and anonymous functions.
 
Although C# is an open specification and compiles to portable bytecode like Java, support for C# outside of Windows is limited.  Microsoft only supports C# on Windows.  The Mono project is an independent, open source implementation of the C# runtime environment, but they have not yet implemented all of the accompanying libraries.  So if you do any serious C# development, you should not assume it will work outside of Windows.
 
Some wondered if if Java would ever be fast enough to be a reasonable alternative to C++.  The massive resources Sun, IBM, and others have devoted to making the JVM fast place Java in the top tier of performers on benchmarking tasks.  While not faster than C++ or C, Java is often close and for certain benchmarks can be faster.  Programming language performance comparisons are always controversial, but the point is that Java performance is closer to “The Cs” than the “formerly scripting” languages in the next section.
 
There is an enormous number of Java libraries and frameworks, maybe more than for any other language.  Many of those are for “enterprise” programming; in other words, building large systems for large companies and institutions.  The danger of this is that sometimes enterprise programmers are tempted to over-design things, and you end up with a RequestProcessorFactoryFactory
  
Other language developers have realized that if they compile their language into JVM bytecodes, their language would run anywhere Java can.  This means they get garbage collection, performance optimizations, the ability to access all of the platform’s existing libraries, and other benefits for “free.”  Microsoft intentionally promoted this idea, which is why they called C#’s execution environment the Common Language Runtime (CLR).
 
Some languages taking this route are
  1.  JRuby/Jython and IronRuby/IronPython are ports of Ruby and Python to the JVM and CLR, respectively;
  2.   Scala combines functional and object oriented programming with more syntactic flexibility than Java;
  3.  Clojure is a new Lisp on the JVM;
  4.  F# is a functional programming language for the CLR.
 
Pick a VM language if:
You want a pretty fast language with lots of libraries and programmers, that’s easier to program than C.
 
Pick Java if:
You use more than one kind of processor or operating system, or want access to the largest body of libraries and programmers of any language.  As long as you can manage or avoid the verbosity of some “enterprise” frameworks.
 
Pick C# if:
You want to develop for Windows.
 
Pick another VM language if:
You want to access libraries written for the JVM or CLR from a language with more features (but fewer developers) than Java or C#.
 
  1. 3. Languages Formerly Known as “Scripting”
 
I say “formerly known as” because “scripting language” seems an anachronism at this point, at least for the likes of Perl, Python, and Ruby.  These languages are used to solve all kinds of problems, not just “scripting” the behavior of some other program.
 
The idea of a scripting language originated from a sequence of shell commands saved as a text file, then fed into a shell to be executed.  Eventually developers added conditionals, variables and iteration to Unix shells, turning them into jury-rigged programming languages.  Larry Wall saw shells turning into almost programming languages and decided to create a language specifically designed for these kinds of tasks, Perl.  Today Perl has the canonical repository of software libraries, CPAN.  Attempts at comprehensive software repositories for other languages are often described “It’s like a CPAN for [language X].”
 
Python and Ruby are the two languages most often considered as Perl replacements.  Python and Ruby forgo a lot of the quirks of Perl.  For example, a variable name in Perl can have several different meanings, depending on the punctuation character prefixing it.  Whether these meanings are intuitive or not depends entirely on how closely your mind matches the mind of Perl.
 
Characteristics shared by Perl, Python, and Ruby include
 
  1.  usually parsed and interpreted at runtime,
  2.  implemented in, and can be extended with, one of the Cs,
  3.  garbage collected,
  4.  implicit, dynamic types,
  5.  built in facilities for text processing and regular expressions,
  6.  literal syntax for data structures like lists and dictionaries,
  7.  concise idioms for handling file I/O,
  8.  slow compared to many other languages,
  9.  allows “higher order” or “functional” programming (each in its own way),
  10.  strongly identified with the creator of the language, still actively involved as a sort of “Benevolent Dictator for Life,”
  11.  and yes, often used to “script” programs written in other languages.
 
Because Perl excels at text processing, and dynamic HTML generation is largely a text processing task, Perl was a good fit for many of the first dynamic web sites, leading to one of Perl’s nicknames, “duct tape of the Internet.”  Python and Ruby are also popular for web programming.  Much of the time spent processing a web request is consumed by I/O bound calls to databases or pushing data across a network, so slow computation speeds are usually not an issue.
 
While Perl, Python and Ruby have similar capabilities, they differ in the style of programs that tend to be written in them.  A Perl slogan is "there is more than one way to do it," meaning that Perl does not restrict programmers but allows for lots of different ways to express an idea.  Python has an opposite slogan: "there should be one—and preferably only one—obvious way to do it."  This means that Python has a single, idiomatic way to best perform common tasks, making code clear and easy to read for other programmers.
 
Like Perl, there is often “more than one way to do it” in Ruby.  Ruby has a consistent object oriented design, but also enough syntactic flexibility to bend it into something that looks like a language created just for your specific problem.  The common name for this is domain specific language, or DSL.  DSLs can be expressed in Perl and Python, too, but Ruby’s syntax and culture make it a more common technique in Ruby.  The most famous example is Rails, a DSL for web development.
 
Pick a formerly scripting language if:
You want to script other programs, interface with code written in one of the Cs, process lots of text, or if programmer productivity is more important than run time performance.
 
Pick Perl if:
You want to partake of the wonders to be found in CPAN, and your mind fits the mind of Perl.
 
Pick Python if:
You believe "there should be one—and preferably only one—obvious way to do it."
 
Pick Ruby if:
You want the flexibility to bend the language to fit the shape of your problem.
 
  1. 4. Functional Straitjackets
 
In Functional Programming (FP), functions are defined in the mathematical sense.  Given identical inputs, a function will always return the same output.  This is not always true in an imperative language like C or an object oriented language like Java.  In those languages, functions and methods can refer to values other than their inputs, and thus compute  different values for the same inputs at different times.
 
Another way to say this is that functions do not allow “side effects.”  This is the reason for “straightjackets” in the title of this section.  Some other languages incorporate FP ideas, like passing functions as arguments to other functions and defining new functions at run time.  You can write functions without side effects in these languages, but you don’t have to. A functional straightjacket leaves you no other choice.
 
It may seem odd to choose a programming paradigm that intentionally restricts what you can do.  One benefit of the no side effects restriction is it makes it easier to reason about your code.  If a function only depends on the values of its inputs, its behavior is easier to predict.
 
An indirect benefit is that it makes certain strategies for parallel code execution easier.  Threads share mutable state, which makes reasoning about multi-threaded programs notoriously difficult.  Because FP has no mutable state it is easier to distribute parts of a computation to different processors and combine the results at the end.
 
The language most synonymous with Functional Programming today is Haskell.  Haskell is strongly, statically typed, meaning that the types of inputs and outputs of functions are strictly defined.  Any attempt to pass an argument of the wrong type to a function means the program will not compile.  Functions themselves have types, based on the types of their inputs and outputs.  Functions can be passed as arguments to other functions, which then become part of that function’s type, so types in Haskell can be very complex.  However, Haskell compilers use sophisticated type inference algorithms, so many of these types do not need to be explicitly annotated, but are inferred by the compiler.  A benefit of strict typing is that once a program compiles successfully, there is a good chance it will run correctly on the first try.  A shortcoming is that you might need to learn a lot of concepts to understand things that are straightforward in other languages, like learning about monads just to understand IO.
 
The strong static typing of Haskell also gives compilers lots of information to make performance optimizations, so Haskell makes a good showing in benchmarks, in the general range of the VM languages.  CS academics are fond of Haskell and the Haskell community is very enthusiastic.  Whether Haskell will be adopted by industry in significant numbers is an open question.
 
Erlang, on the other hand, was born in industry.  Erlang was developed at Ericcson to create massively distributed, fault tolerant systems.  A typical Erlang program creates many processes that share no state but pass messages to each other.  Processes can also be distributed across a network.  The lack of shared stated makes functional programming a natural fit.  Erlang is dynamically typed and slower than Haskell on a single processor, but Erlang’s ability to scale is unmatched.
 
Pick a functional straitjacket if:
You want to be confident that your program is correct, or you want to utilize all the processors at your disposal without the worries that come from sharing state between threads.
 
Pick Haskell if:
You want there to be a good chance that your program will run correctly the first time it successfully compiles.
 
Pick Erlang if:
You’re not so concerned about single threaded performance, but want ludicrous scalability.
 
  1. 5. Lisps
 
The defining characteristic of Lisp is that programs are constructed from the data structure for which Lisp is named: lists (linked lists, to be precise).  Both lists and Lisp programs are expressed with a syntax of nested parentheses, called S-expressions.  This means functions for taking apart and creating lists can also take apart and create Lisp programs.  This powerful technique is difficult to duplicate in languages lacking this feature.
 
The most common way that programmers leverage this ability is through macros.  A macro transforms source code before it is compiled.  Because Lisp macros operate on lists, they are much more powerful than macros in a language like C that operate on raw text.
 
On the TV show Heroes, the villain Sylar has the meta-ability of taking the abilities of other super-powered characters for himself.  Similarly, Lisp has the ability to take features from other programming languages and make them its own.  An impressive example of this is the Common Lisp Object System (CLOS).  C++ and Objective C were new languages created to add Object Oriented (OO) features to C.  Using macros, OO was added to Common Lisp as a library.  Other programming paradigms have also been implemented as Lisp libraries.  For example, multiple textbooks present a Prolog-like logic programming language implemented in Lisp.  Lisp is often used to experiment with new programming paradigms.
 
In general, Lisp’s features make it  a good fit for exploratory programming, where you are breaking new ground.  Or a large problem whose complexity is getting difficult to manage.
 
There are many Lisps to choose from.  If someone says just “Lisp,” with no qualifier, Common Lisp is most likely the Lisp they have in mind.  The Common Lisp specification combined many of the best features from various other Lisps popular at the time.  SBCL is an open source implementation of the Common Lisp spec, and Allegro and LispWorks are commercial implementations.  There are several others.
 
In some ways, Common Lisp is close to a superset of the features of all other programming languages.  Where most languages compare well against another language’s weak points, Common Lisp can make a fair comparison with the other language’s strong points.  By default, CL has dynamic types, making it a good language for rapid prototyping like “formerly scripting” languages.  With optional type hints, compilers can make performance optimizations that, in some cases, emit code as fast as C.  CLOS has features found in no other object system.  The “loop” macro compares well against other powerful iteration constructs like Python’s list comprehensions.  CL has all the features needed for functional programming like Erlang and Haskell.
 
Functional programming is the default CL paradigm.  While not a “functional straightjacket,” CL is designed so that programming in a functional style is most natural.  Even when other paradigms are used, they can have a “functional flavor” to them.  Where most OO programming systems are based on methods belonging to a class or object, CLOS is based on “generic functions” that can be specialized on the types of their arguments.
 
On the downside, the Common Lisp spec is missing things like threads, networking, and Unicode string handling.  This means that there is no guarantee that programs that need this functionality will work across implementations.  (More on this below.)  Some other CL functions now seem dated or idiosyncratic, like CL’s file handling functions.  Because CL is the result of a standards process and there are no prospects for re-opening that process, there is little hope for repairing these flaws.
 
The Common Lisp community can be found in the comp.lang.lisp newsgroup.  The inhabitants of c.l.l. are brilliant, eccentric, and cantankerous.  They can answer pretty much any question you throw at them, but will first grill you to make sure you are not a troll or trying to get them to do your homework for you.  They are also likely to tell you that you are asking the wrong question, and that what you think you want to do isn’t what you really want to do.  Get past these defenses, however, and there is much knowledge to be had.  CL’s frozen spec and guarded community explain in part why CL’s popularity does not match its impressive feature set.  (To explain the rest, Google “A.I. Winter.”)
 
Scheme is simpler and more consistent than Common Lisp.  Where Common Lisp has separate namespaces for functions and variables, for example, Scheme has just one.  Scheme’s simplicity makes it a good choice for a teaching language, but Common Lisp has more features, more programmers, and more libraries.  Scheme is growing in terms of features, programmers, and libraries, but it remains to be seen whether it will maintain its simplicity relative to Common Lisp as it grows.  Like Common Lisp, Scheme is a specification with multiple implementations, such as PLT Scheme.
 
Arc is Paul Graham’s attempt to create a “100 year language.”  Graham has said that he is not just making a language to be popular in the short term, but a language that will still be useful in 100 years.  A joke about Arc, though, is that being a 100 year language means that it will take that long to actually be useful.  Like most jokes, there is a grain of truth to this one.  There was a lot of excitement about Arc when it was first released, but it has been a while since Arc has been updated, and activity on the Arc discussion forum has almost died out.
 
The goal of Arc is to make programs as short as possible in the number of meaningful tokens (making all variable names one character doesn’t count).  Arc introduced interesting innovations in pursuit of this goal, like a concise syntax for anonymous functions and treating hashes and lists as functions of their indexes.  It lags, however, in pragmatic features like performance optimizations and Unicode strings.
 
These multiple Lisp dialects, with multiple implementations,  highlight the fragmentation of the Lisp community.  When a new Lisp library is written, a different dialect, or sometimes even a different implementation of the same dialect, does not automatically benefit.  This is a problem because much of the value proposition of a programming language lies in the libraries written for it.  It also means that when a question arises, or you want to hire a programmer, it is harder to find someone experienced with the dialect and implementation you are using.
 
Clojure addresses the lack of a coherent Lisp community on multiple fronts.   It runs on the JVM, so it automatically inherits the vast amounts of existing Java code and the attention of Java developers seeking a more productive language than Java.  It is a functional straightjacket like Haskell and Erlang (no side-effects), appealing to functional programmers.  It appeals to Lisp programmers with innovations like literal syntax for hashes, vectors, and sets and a common interface for iterating over all those types, in addition to lists.  Clojure appeals to those tired of waiting for an Arc update by stealing some of Arc’s best ideas, like data types in function position and short syntax for anonymous functions.
 
Maybe the most important factor in creating a community around Clojure is its creator, Rich Hickey.  Where Common Lisp has a frozen spec, Scheme a slowly evolving spec, and Arc a creator with lots of other things on his mind, like the “formerly scripting” languages Clojure has an engaged “Benevolent Dictator for Life.”
 
Pick a Lisp if:
You have a problem that requires serious exploration or breaking new ground, or you want to use a bottom up process to build a language to fit your problem.
 
Pick Common Lisp if:
You want a language whose feature set is close to a superset of the features of all other languages, and you’re willing to put up with sketchy library support and a brilliant but eccentric and cantankerous community.
 
Pick Scheme if:
You want to see how a simple language with a few well chosen features can produce elegant, powerful programs, or you want a good vehicle for exploring the deep ideas of computer science.
 
Pick Arc if:
You want a 100 year language and you’re willing to wait that long.
 
Pick Clojure if:
You want a Lisp that can access Java libraries, a functional straitjacket with macros, or a Lisp with an engaged, active creator.
 
  1. 6. JavaScript
 
JavaScript gets its own section, because no other language can challenge it in its domain.  JavaScript has the single most important piece of programming real estate exclusively to itself:  the browser.
 
To make a music analogy, JavaScript is a band that becomes famous for cheesy pop songs, and only later earns indie cred as discerning fans discover the cutting edge artist underneath the cheesy pop exterior.  JavaScript got its name as a pure marketing ploy, branded with “Java” even though there is no technology connection.  It got its start with tasks like input field validation and popup ads.
 
But eventually people realized that JavaScript had a lot of the features that programmers were buzzing about, like first class and anonymous functions, closures, data literals, and prototype inheritance.  Some long winded guy with a blog even thinks it might be the Next Big Language.
 
Pick JavaScript if:
You want your code to run in a browser.
Jim Rankin’s Blog
Thursday, January 22, 2009