Post navigation

Choosing a Programming Language: So easy, a caveman can do it

1. Introduction
About a week or two ago, I found myself prompting Google and other search engines with questions like, “what is the best programming language?”, “how to choose a programming language?”, “how to interpret performance benchmarks?”, et ad nauseam. I even took a few cheap-o “what programming language are you?” type quizzes (I, in fact, created this “cheap-o” quiz).

Gimmicks aside, being a non-programmer, I neither have the luxury of being dictated languages to learn nor the opportunity to learn perhaps dozens of languages throughout my career. This lead me down a path in which I felt compelled to choose once, and choose right. In my visionquest to find the “right” one, and after weeks of research, I am no closer to nor am I any more certain about any of these answers. In failure, however, I discovered that I had framed the problem incorrectly. Instead of thinking about learning a programming language as a linear endeavor or as an exercise in academia, I should have remembered the wisdom passed down by my illustrious ancestor, the caveman: “every problem looks like a nail if all you’ve got is a hammer”. Even our great fore-bearers knew that it is better to adopt a synergistic array of tools, so why wasn’t this immediately obvious?

Short answer: because I lacked an understanding of programming fundamentals. Because I only had a hammer (Excel/VBA), every problem looked like a nail. Which brings me to my first point: an understanding of how languages and programming paradigms were developed to address various types of problems provides a framework for deciding upon a complementary assortment of tools that are suited for various tasks.

With these thoughts and questions in mind, I humbly submit my analyses for your evaluation.

2. Paradigms
While there isn’t really a “best” overall language, it is important to recognize that some tool are better than others, depending on the task at hand.

To grossly over-simplify, the most basic programming language paradigm is the imperative, which tells the machine what to do, and how to do it. At the lowest end of the spectra are machine and assembly. One notch higher on the imperative level are procedural languages, such as C, FORTRAN, and Basic (the lowest of the so-called “high-level” languages). The efficacy and efficiency of any solution at this level is highly dependent on a programmer’s experience, inventiveness and ability.

Object-oriented languages (like C++ or Java) were later developed to enhance programmer productivity through the inheritance of object methods. In this way, a programmer can spend less time on telling the machine how to do things, instead relying on reusable code.

Independently of object-oriented languages, declarative paradigms were developed. These paradigms focused on letting programmers tell the machine what to accomplish, and letting the program decide how best to do it. Functional languages, like SQL, rely less on assignment and more so recursion to achieve this end. Logical languages take this even further, foregoing telling the machine what to do, but rather what an answer looks like.

While I’ve omitted many such paradigmatic distinctions, my point is to convey that no single paradigm allows programmers to solve all types of problems in the easiest and most efficient way. It is sufficient to be able to understand the concept of paradigms as it applies to problem sets; reciting them is secondary.

Figure 1: The Basic Paradigms

Table 1: Matrix for 29 Languages with 15 paradigms

3. Intended Uses
Along the same lines, it makes sense to be aware that various languages were created with different use cases in mind.

Table 2: Matrix for 29 Languages with 15 Intended Uses

4. Benchmarking

Benchmarking is one of the most common ways by which to measure and compare language performance. Most commonly, they measure speed, but can also be used to measure expressiveness, efficiency, and more.

The below graph summarizes languages benchmarks on a number of computationally intensive tasks using three criteria. The raw data for the metrics is available at http://benchmarksgame.alioth.debian.org/. For all the following criteria, lower numbers are better. The criteria are as follow:

Size(B): Corresponds to the average compiled size of code utilized to run task. It is a proxy for measuring language expressiveness; the smaller the number, the more a programmer can accomplish in fewer lines of code.

Mem(KB): Measures average memory utilization in kilobytes; lower numbers correspond to more efficient handling of memory.

CPU(s): Measures the average number of second to run a task; obviously, faster is better.

Admittedly, a simplified analysis such as this is prone to the “flaw of averages”, meaning that averages fail to demonstrate dispersion and outliers. A few sources have managed to overcome this by displaying performance metrics using star charts:

There are numerous sources and ways to benchmark a programming language’s performance (see: http://dan.corlan.net/bench.html; and, http://julialang.org/). However, there is no way to expediently untangle all of the inherent biases in them (see explanation here). AttractiveChaos combats some of this bias by testing fewer languages with different compilers as a sensitivity/robustness test.

5. Other criteria
Aside from traditional empirical benchmarking, there other considerations. Some of which are quantifiable, while other lend themselves more to qualitative analysis. Below is a short list of criteria by which to compare various languages. This list is not exhaustive nor, with the exception of productivity, ordered in any particular manner.

Productivity – For problem-centric programmers, productivity is probably the most important factor. It is also the most inter-related with other criteria.

Expressiveness – On the most basic level, an expressive language is a concise language, meaning that it can do a lot more for less code. Programmers disagree on the correct way to measure expressiveness, whether through the number of code blocks, number of characters, or overall size of the compiled code (see one possible solution). Wikipedia also has an interesting take on measuring expressiveness.

Aesthetics – RoBlog has a nice post on this facet of a language: “A beautiful language has a clear and uncluttered syntax — neither more verbose than necessary nor more terse than readability allows. A beautiful language has clear semantics, …should be expressive…, [and] should be efficient.”

Maturity – A mature language has been vetted and boasts a large and well-documented library. FORTRAN is often cited as having a very mature library for numerical analyses.

Speed/Efficiency – Raw speed may not always be necessary, but should never sacrificed unnecessarily. As compilers become more sophisticated and robust, even some of highest level interpreted languages can come within a factor or two of C/FORTRAN.

Portability – Portability may or may not a necessity, but it never hurts to be able to deploy across multiple operating systems, and even within a web browser. Portability is one reason why Java has been so well received.

Scalability – This is a concern when moving from a research/prototype environment to deployment. Transcribing code from one language to another may be a good academic exercise, but surely one inconvenience as well.

Ease – One way of measuring a language’s speed and efficiency starts not at compile-time, but rather with coding. The ease at which a language can be learned is important if this is how you measure productivity.

Flexibility – Can your language address a broad array of problems, or is its utility narrowly confined?

Price – Even when price is no object, it is. With so many open-source projects to choose from, one should be able to justify an extra expense.

Extensibility – Does your language play well with others? Can you use alternative compilers or call on non-native libraries without much trouble?

Acceptance/Popularity – While many might eschew popularity as a valid criterion, it is in most cases a useful indicator for traction in any of the previously mentioned categories.

6. Note on Popularity
It has been my experience that the deciding factor for many people has been language acceptance and popularity. Admittedly, popularity is somewhat of positive reinforcement loop: popular and possibly inferior languages stay popular precisely because they are popular in the first place. Unfortunately, this cycle makes it difficult for new and innovative languages to capture an audience. For example, Julia (http://julialang.org/) which was released in 2012 offers many promising features and advancements in the realm of numerical and high-speed computing, but will have a difficult time replacing the deeply embedded Matlab, R, and FORTRAN. Also, OcaML is often cited as a superior functional language, but has yet to fulfill its potential due to immature and sparse libraries. The examples for this kind of phenomenon are endless (via-a-vis Google’s Go and Dart; DARPA’s responses to FORTRAN: Fortress, Chapel, and X10, etcetera…).

On the other hand, language popularity is good proxy for traction in a number of performance and aesthetically related characteristics. Given that popular languages may or may not be inherently “superior” to their less popular counterparts, their are a lot of good reasons for going with the crowd. One of the main reasons is due to that they are taught in schools. Equally as important is language maturity. If a lot of people are using a language, it is likely to feature mature libraries, a vibrant community presence, and well documented support. In my personal experience, the best resource for trouble-shooting code is simply typing the issue into Google. If the language is in wide use, their is high likelihood that someone has already solved a similar, if not almost identical, problem.

7. Programming as an Exercise in Project Management
In addition to programming languages being a set of tools designed to facilitate problem solving, it is helpful to think of problem solving as a project management process. When faced with a problem in which you have control over the tools and methods utilized, you might begin by breaking down the problem into sub-problems. Hypothetically, let’s assume that I am tasked with designing a program to trade stocks, including locating profitable trades, to actually executing them through a broker. Although the entire process can begin and end with an industrial strength language like C++ or JavaScript, I might make better use of time by breaking down the problem into phases such as the following:

Prototyping: pseudo-languages like Excel, Matlab, Mathematica, or R are well suited to rapid-prototyping for data analysis types of applications.

Testing: scripting languages like Ruby, Python, and Perl can often be used as “glue” to implement prototypes into test environments.

Integration/Deployment: once prototypes have been vetted, they can be interpreted and implemented in other languages by “specialist” programmers.

8. ConclusionThe tacit wisdom of adopting “problem-cetric” approach premises on the notion that the proper role of automation is to enhance productivity, not stifle it. In other words, we are the masters, they [computers] are our servants. Trivial and academic pursuits aside, the questions for choosing programming languages are the same ones your prodigious ancestors pondered when flint first struck stone, or when the tension of the sapling was first strung into snare then bow. Likewise, whether your prerogatives are for aesthetic appeal or raw computational speed, the goal, from inception to coding to run time, is always to carry out the most of our productive interests for the least amount of effort. Ask yourself: “Is optimization of speed premature while productivity languishes?”; “Are semantic elegance and lexical simplicity just means by which to further our productive capacity?” Eschewing the “one-size fits all” paradigm, we should be able to call upon a diverse array of tools; ones that compensate for the shortcomings of others, and that complement each other in ways that enhance our ability to solve problems.

13 thoughts on “Choosing a Programming Language: So easy, a caveman can do it”

I wonder how you built your matrix. I think the entries are false for some of the programming languages. For example OCaml which is clearly a multi-paradigm language, that has lazy evaluation, generic through polymorphism, arrays also. What’s “structured” that only Dart and C# are sharing ?

Maybe you should explain better what are the 15 paradigms, or link to an explanation for them and why you choose to set TRUE/FALSE for them.

Same for intended uses, OCaml is largely used for education, at least in France where it used to learn the functional paradigm. Can you explain on High performance computing ? I can see only Julia listed for it, not even C ? Concerning OCaml, the computer language shootout page has it almost as fast as C in a lot of benchmarks, but this can be subject to discussion.

Talking of something else than OCaml, see Lisp that has meta programming, multiple dispatch which are not listed in your articles.

honestly? High-Performance Computing is NOT done in Julia. All – and I mean all – HPC is done in either Fortran or C /C++. Any Cluster-Admin will kick you off the system if you ask for an installation of some other prog. language (remember, these things cost millions -any single CPU-cycle counts and is protocolled in € $ £ and ¥.

You need to fact check your list of lazy evaluation supporting languages; your spreadsheet is wrong. It’s probably true that Haskell is the only language in your list that has that as default behavior on your spreadsheet but C#, for example, absolutely supports it.

>>Trivial and academic pursuits aside, the questions for choosing programming languages…<>Size(B): Corresponds to the average compiled size of code utilized to run task.<>Speed vs Memory vs Expressiveness<<

The benchmarks game does not provide 3 independent measurements.

Those measurements provide a single independent measurement (program elapsed time). Memory use and source code size are secondary dependent dimensions — because slower programs are arbitrarily replaced by faster programs in the dataset; and because the programs are shown in elapsed time order, program contributors optimise for program speed rather than space (both memory usage and source code size).

Those measurements provide a single independent measurement (program elapsed time). Memory use and source code size are secondary dependent dimensions — because slower programs are arbitrarily replaced by faster programs in the dataset; and because the programs are shown in elapsed time order, program contributors optimise for program speed rather than space (both memory usage and source code size).

1. I have to agree. Anticipated monetary reward is often the deciding factor. I, however, am writing from the perspective of a non-programmer. I don’t get paid for writing code. I wrote this from the perspective of an analyst, and profit most from doing the most (accurate) work in the shortest amount of time.

2. I stand corrected. Size corresponds to the “size of compressed source code instead of lines of code.”

3. That’s a good point about one of the ways in which benchmarks can be biased. It’s kind of like Heisenberg’s uncertainty principle (although that’s maybe a stretch)… you distort observations by nature of trying to observe. Benchmarks are optimized, and therefore often fail to provide a fair and balanced understanding of a how a language performs in its day-to-day usage.

I’m actually exploring Python programming language. It’s my very first time dealing with programming. My first impression is: seems that most people assume that one must first learn the programming language in order to, then, think whatever program; or, better, that if one doesn’t know a computer language, one is unable to think a program. That seems absolutely false; one doesn’t need to learn Chinese in order to be able to think the stuff that’s going to be said then in Chinese. So natural language can be used to think and express any program one could think, of course not the thought nor its expression will be accurate enough to be run by a computer until it’s written in the proper computer language, but that’s another story. Since programming language seems to imply just certain restrictions over natural language, more than selecting the language that best fit my hypothetical requirements, could you point me somewhere I can be told about those specific restrictions first, in order to be able to use my natural languages skills to think “as if” in programming language from the very beginning? I know the moment will arrive, and maybe I’ll end up (or not) using a programming language not just to express but also to think all the thought in the exact terms that the thought must be expressed, but, in the meantime, I would like to feel (if what I’ve been saying is True) that I’ve already have the ability to think whatever is necessary to design any program, that learning a computer language is like learning a way to translate my own thought, and not a must learn to build any particular kind of brains that I don’t have yet.

Is it possible to answer my question in plain natural language?
Thanks in advance!