Project Nayuki

Being a polyglot programmer


Today I write program code in multiple languages – usually Java, JavaScript, Python, C, C++, and x86 assembly. This wasn’t always the case, and my first 5 years of programming experience were spent almost exclusively on Java. Although I did get exposed to other languages occasionally, it was only much later that I got into the habit of regularly programming outside of Java. Nowadays I choose the language for new projects based on convenience and requirements, and when I publish algorithms I go the extra mile to provide code in multiple languages for the reader’s convenience.

I believe there are benefits to being a multi-lingual (polyglot) programmer. Because not everyone has the time, circumstances, or motivation to be multi-lingual, I hope this article will raise awareness of the phenomenon and start discussions on the pros and cons of this way of working.


Overall observations and opinions

Getting easy access to features
  • Literal lists and dictionaries: Python, JavaScript, C++
  • Unicode text handling: Python, Java, JavaScript
  • Regular expressions: Java, Python, JavaScript
  • Efficient int and float arithmetic: C, C++, C#, Java
  • Big integer arithmetic: Java, Python, Haskell, C#, Mathematica
  • Complex numbers: Python, C, C++, C#, MATLAB
  • File input/output: Java, Python, C, C++, C#
  • Network sockets: Java, Python
  • JSON handling: Python, JavaScript
  • ZIP, DEFLATE: Java, C#, Python
  • Easy to compile/run on Unix systems: C, C++, Python
Adopting habits from stricter languages
  • In dynamically typed languages like Python and JavaScript, I find it helpful to mentally label the type of each variable – for example integer, string, or list of Booleans. By keeping track of variable types clearly, type errors are essentially eliminated. Sometimes I even explicitly annotate the variable types in code comments (e.g. at the top of Of course this way of thinking is mandatory when working in a statically typed language.

  • In the minority of languages like Java, C#, and Haskell, the condition of an if/while/for statement must evaluate to a Boolean-typed value – not an integer or an object. For example if (true) is legal, but if (0) is a compile-time type error in Java. Most other languages allow various types of values to be interpreted as a Boolean condition in if/while statements, e.g. void *ptr = (...); if (ptr) { ... } in C, or num = 0; if num: ... in Python.
    Different languages have different notions of what values are treated as true or false. For example 0 is treated as false in Python and JavaScript, but surprisingly true in Ruby. For example [] (empty array/list) is treated as false in Python but true in JavaScript. For example 0 is false in JavaScript but new Number(0) is considered as true. However, at least null in JavaScript, NULL in C, None in Python, and nil in Ruby are all consistently treated as false.
    The easiest way to guarantee consistent behavior across all languages is to ensure that the condition of an if/while statement is always a Boolean value. This means writing explicit comparison tests as needed, avoiding the shortcut way provided by each language’s semantics. For example in Python, instead of writing if obj:, we should explicitly write out if obj is not None:. For example in C, the code x = 3; if (x) { ... } should be expanded out to x = 3; if (x != 0) { ... }. This kind of explicit value testing is already mandatory in Java, and following the same practice in other languages makes code more robust and much less likely to be misunderstood or mis-translated.

  • JavaScript only has floating-point numbers, not integers (though it has bitwise integer operations). Most other languages distinguish between floats and ints, and observing this distinction in JavaScript is helpful for writing correct, efficient code that can be potentially translated to other languages.

  • Python uses code indentation to denote block structure, so correct indentation is mandatory by design. In other languages it is a very good idea to maintain perfect indentation all the time too, otherwise the code becomes confusing and messy to read.

  • Haskell does not have an implicit notion of a null value, and requires you to use the Maybe wrapper type to express a value that can be either present or absent. I find that in Java code, null values are rarely useful, and I frequently write comments or assertions to denote that a value must not be null.

  • Encapsulation isn’t taken very seriously in Python or JavaScript. There are idiomatic ways to differentiate public and private members, but they are not always used by coders. Compare this to C++, C#, and Java, where declaring members as public or private is taken seriously at the outset.

  • Python has duck typing, so inheriting from a blank abstract superclass is usually optional. But this design pattern is mandatory in statically typed languages like C++, C#, and Java, and declaring the inheritance is useful in expressing the data type relationships and design intent to a programmer reading the code. A Python example can be found in my optimizing brainfuck compiler in the Command class.

Avoiding language-specific quirks
  • JavaScript is one of the few popular languages where reading an uninitialized variable or non-existent array index yields a legal undefined value instead of throwing an exception. Relying on this behavior is ugly, and it poorly translates to other languages. For example, instead of writing a[3] == undefined, you should write a.length <= 3.

  • JavaScript lets you declare a variable multiple times within a function (e.g. var x; var x;), which has the same effect as declaring it just once. In Java this kind of code is a compile-time error. In C/C++ this is an error if the variable is re-declared in the same block, but if declared in a nested block then a new variable shadows the old variable (which can be dangerous).

  • In JavaScript and MATLAB, setting the value at a non-existent array index (for example a[a.length] = 8;) will automatically grow the array. This is even faster in some JavaScript engine implementations. However I think this code is poor style and translates poorly to other languages; it is much clearer to write a.push(8);.

  • The str type in Python 2 performs double duty of representing byte sequences and ASCII text strings. This leads to sloppy data typing and subtle latent bugs. In contrast, Python 3, Java, and C# clearly delineate byte sequences from Unicode text strings by design.

  • Java’s String.split() method will remove trailing blank elements by default, unless a negative number is specified as the limit. This violates the intuitive notion of string splitting. By contrast, the behavior of string splitting in Python and JavaScript corresponds to what you would intuitively expect.

Concepts better explained in another language
  • In Java, the distinction between values and references can be confusing for the beginner programmer. Even as he matures in Java, the two behaviors are likely to be seen as magic. On the other hand if he worked in C or C++, then he is forced at an early stage to understand the distinction between an immediate value/struct versus a pointer to a value/struct.

  • The Java documentation for equals() and hashCode() provide more helpful details on how to write conforming method implementations than Python’s documentation in the equivalent methods __eq__() and __hash__(). Also, there are more readily available tutorials on implementing equals() in Java.

  • Java is a mature, thought-out, and well-documented object-oriented language with a managed runtime. Python is object-oriented and has a runtime too, but its documentation lacks the depth and rigor that the Java ecosystem has. I find that concurrent programming (threads, locks, condition variables, etc.) and garbage collection (when are objects reclaimed, finalizer methods, weak references, etc.) are inadequately documented in Python, rarely used, and/or rarely discussed. I learned about these concepts largely from the Java ecosystem (from official documentation and third-party articles), and found it helpful to mentally translate the concepts in the Java-oriented articles into the vocabulary of the Python language and libraries.

  • In Python you might be able to argue that some simple-looking operations don’t need locking in a multi-threaded environment. For example, putting a value into a dictionary (like d[x] = y) looks deceptively like an atomic operation supported by the language. It is likely to be correct in the CPython interpreter because the dictionary-put method is implemented as a C function, and C functions cannot be preempted even if they need to execute numerous instructions. But the Java equivalent of this code (which would be the method call d.put(x, y);) is strictly thread-unsafe, and must be surrounded with an appropriate lock and unlock.

Seeing what a language is missing
  • Java’s BigInteger library is my favorite implementation out of all bigint implementations that are built into a programming language (third-party libraries don’t count). It was released very early and contains numerous useful features like bit length, popcount / Hamming weight, primality testing, modular inverse, and so forth. By contrast, C#’s BigInteger library was released very late and only contains basic arithmetic, without extra features. Python has had bigint support from early on, but still lacks the extra features.

  • Java and C++ have sorted sets and maps, which are useful when you need a mutable collection and be able to iterate over all the keys in sorted order. Python only has hash-based sets and dictionaries, with no plans to add sorted ones into the standard library.

  • When traversing a container (such as ArrayList), Java’s iterators support element removal and other operations. Python’s iterators are defined in a confusing way, and don’t support element removal or seeking. On the other hand, Python supports generators while Java doesn’t. Generators/coroutines make it much easier to express certain streaming algorithms, such as incremental data compression/decompression.

  • Python and Haskell have a built-in type for fractions / rational numbers, while Java doesn’t. Having to implement or copy a fraction implementation every time it’s needed in Java can get tedious.

Gaining insight into an algorithm’s core
  • By expressing an algorithm or data structure in multiple languages, you will discover which parts stay the same and which parts change. Parts that usually stay the same include the control flow, data flow, and data structures (array, dictionary, tree, etc.).

  • The explicit static typing in C, C++, C#, Java, etc. is visually noisy and can hurt readability when glancing at the big picture of an algorithm. But they are useful when drilling down to the details to produce correct runnable code.

  • Going a step further, the const qualifier on data types in C and C++ makes it very clear whether a function is allowed to mutate an object/structure or not. By contrast, object mutability is only a convention in Java, JavaScript, and Python, not enforced through the type system. However, the price to pay for const qualifiers is more syntax in the code and more careful thinking when designing logic.

  • The explicit memory deallocation in C can be a distraction that makes algorithms harder to understand. This is much less of a problem in C++ due to RAII and containers like std::vector, std::string, smart pointers, etc. This problem is non-existent in garbage-collected languages like Java, JavaScript, Python, etc., because memory deallocation is almost always implicit (except for developers who implement buffer structures like ArrayList).

  • All the popular programming languages use IEEE 754 binary floating-point arithmetic by default. Hence numerical algorithms will behave almost identically in any language, except for tiny differences in the approximation error of math library functions (e.g. exp() being different by a few ULPs). In particular, it means that if 0.1 + 0.2 ≠ 0.3 in Python when using 64-bit floating-point numbers, then the same problem will occur in JavaScript, C#, et cetera.

  • Java’s lack of operator overloading makes BigInteger arithmetic look ugly and unreadable. By comparison, bigint arithmetic is very readable in Python and Mathematica due to native support.

  • The act of porting code to another language gives me an excuse to re-read and think about the entire codebase. This often leads to simplifications, small bug fixes, better comments, and other improvements in the code.

Expanding the audience inexpensively
  • Compared to most programmers, I spend a lot of time designing algorithms in addition to banging out code. For example I worked on data structures like AVL tree list, broad techniques like Huffman coding, and non-trivial real-world standards like QR Codes. The time that I spend on the math and algorithms is a large one-time cost that is independent of whatever language I choose to implement the ideas in. After I draft my first implementation, I resolve the bugs in the architecture, data flow, computation of correct values, low-level logic, etc. This process of coding is costly because it takes deliberate effort to turn a pile of informal mathematical/algorithmic ideas into a concrete implementation in a real, executable programming language.

  • But after creating one working and debugged implementation, it’s smooth sailing from there on out. Adding another language implementation of the same concept is quite cheap compared to all the upfront preparation and initial debugging work that went into the first implementation. My usual approach to porting languages is to copy and paste the code, and work line by line to fix the syntax and change the library calls. For example when translating from Java to Python, I would replace if (x) with if x:, && with and, lst.add(e); with lst.append(e), delete all the types, and so on. My codebases usually have a runnable main program or test suite (instead of being a passive library), so I can quickly check that the translated version of the code runs properly.

  • By porting my programs to multiple languages, there is a higher chance that a reader understands one of the languages that I published in. This makes it easier to disseminate my math/algorithm ideas and code style choices to a wider audience.

  • Some kind users on the Internet have ported some of my published code to other programming languages. (For example, my next lexicographical permutation algorithm to Ruby, my simple DEFLATE decompressor from Java to C#, and my smallest enclosing circle algorithm to Scala.) Because other people are willing to translate my code to more languages, it confirms that my goal to provide multiple language versions is valuable in the first place.

Code examples

The first few interactive examples illustrate how similar Python, JavaScript, Java, and C++ are in their syntax and semantics. When I write code in one of these languages, I can port it to another language by reading and writing one line at a time, fixing up the syntax and changing the library API calls along the way. In particular, I don’t need to change the overall architecture, control flow, or data flow.

Fibonacci function

This example demonstrates function declarations, types, variables, integers, arithmetic, if-statements, for-loops, and throwing exceptions.


# Correct for all non-negative n, thanks to native bigint
def fibonacci(n):
	if n < 0:
		raise ValueError("Negative index")
	a, b = 0, 1
	for i in range(n):
		a, b = b, a + b
	return a


// For simplicity in this illustration,
// we don't worry about large values of n that
// cause rounding errors or numeric overflow
function fibonacci(n) {
	if (n < 0)
		throw "Negative index";
	let a = 0, b = 1;
	for (let i = 0; i < n; i++) {
		let c = a + b;
		a = b;
		b = c;
	return a;


// For simplicity in this illustration, we don't worry
// about large values of n that cause numeric overflow
int fibonacci(int n) {
	if (n < 0)
		throw new IllegalArgument("Negative index");
	int a = 0, b = 1;
	for (int i = 0; i < n; i++) {
		int c = a + b;
		a = b;
		b = c;
	return a;


// For simplicity in this illustration, we don't worry
// about large values of n that cause numeric overflow
int fibonacci(int n) {
	if (n < 0)
		throw "Negative index";
	int a = 0, b = 1;
	for (int i = 0; i < n; i++) {
		int c = a + b;
		a = b;
		b = c;
	return a;

List manipulation

This example illustrates data declarations, the list ADT, and working around missing features with loops and temporary variables.


a = [2, 7, 4]
a.insert(0, 9)
del a[1]

b = [1, 5, 6]
# [9, 7, 4, 3, 1, 5, 6]

print(sum(a))  # 35


let a = [2, 7, 4];
a.splice(0, 0, 9);
a.splice(1, 1);

let b = [1, 5, 6];
a.push.apply(a, b);
// [9, 7, 4, 3, 1, 5, 6]

let sum = 0;
for (const x of a)
	sum += x;
console.log(sum);  // 35


import java.util.*;

List<Integer> a = new ArrayList<>();
Collections.addAll(a, 2, 7, 4);
a.add(0, 9);

List<Integer> b = new ArrayList<>();
Collections.addAll(b, 1, 5, 6);
// [9, 7, 4, 3, 1, 5, 6]

int sum = 0;
for (int x : a)
	sum += x;
System.out.println(sum);  // 35


#include <iostream>
#include <vector>

std::vector<int> a{2, 7, 4};
a.insert(a.begin(), 9);
a.erase(a.begin() + 1);

std::vector<int> b{1, 5, 6};
a.insert(a.end(), b.cbegin(), b.cend());

std::cout << "[";
bool head = true;
for (int x : a) {
	if (head) head = false;
	else std::cout << ", ";
	std::cout << x;
std::cout << "]" << std::endl;
// [9, 7, 4, 3, 1, 5, 6]

int sum = 0;
for (int x : a)
	sum += x;
std::cout << sum << std::endl;  // 35

Classes and objects

This example shows the declaration of a class, constructors, methods, public and private fields, and working with objects.


class Person(object):
	# Constructor
	def __init__(self, nm):
		# Creation of fields = nm
		self._salary = 0
	# Method
	def get_lowercase_name(self):
	# Method
	def get_salary_multiple(self, x):
		return str(self._salary * x)
	# Method
	def raise_salary(self):
		self._salary += 1

def main():
	p = Person("Alex")



class Person {
	// Constructor
	constructor(nm) {
		// Creation of fields = nm;
		this.salary = 0;
	// Method
	getLowercaseName() {
	// Method
	getSalaryMultiple(x) {
		return (this.salary * x).toString();
	// Method
	raiseSalary() {

function main() {
	let p = new Person("Alex");



public class Person {
	// Fields
	public String name;
	private int salary;
	// Constructor
	public Person(String nm) {
		name = nm;
		salary = 0;
	// Method
	public String getLowercaseName() {
		return name.toLowerCase();
	// Method
	public String getSalaryMultiple(int x) {
		return Integer.toString(salary * x);
	// Method
	public void raiseSalary() {

public class Main {
	public static void main(String[] args) {
		Person p = new Person("Alex");


#include <algorithm>
#include <cctype>
#include <cstdlib>
#include <iostream>
#include <string>
using namespace std;

class Person {
	// Fields
	public: string name;
	private: int salary;
	// Constructor
	public: Person(string nm) :
		salary(0) {}
	// Method
	public: string getLowercaseName() {
		string result(name);
		transform(result.begin(), result.end(),
			result.begin(), [](char c){return tolower(c);});
		return result;
	// Method
	public: string getSalaryMultiple(int x) {
		return to_string(salary * x);
	// Method
	public: void raiseSalary() {

int main() {
	Person p("Alex");
	cout << << endl;
	cout << p.getSalaryMultiple(5) << endl;

Libraries published on my other pages

Among my published articles, there are many examples where I implement an algorithm, data structure, or application in multiple languages. These examples show how I map a real-world problem into code in different languages, and how the resulting pieces of code compare in terms of clarity, conciseness, and explicitness.

Next lexicographic permutation algorithm
  • ~25 lines of code per implementation

  • Featuring arrays, loops, integers, generic types

Free small FFT in multiple languages
  • ~200 lines of code per implementation

  • Featuring floating-point types and arithmetic, trigonometry, bitwise operations, memory allocation, arrays

  • Python has a huge advantage in conciseness due to native support for complex numbers and list comprehensions

Forcing a file’s CRC to any value
  • ~200 lines of code per implementation

  • Featuring file I/O, string parsing, exception handling, bitwise integer math

  • The C code looks obtuse in string handling and explicit error handling

AVL tree list
  • ~400 lines of code per implementation

  • Featuring tree graphs, recursion, reference/pointer manipulation, ADT design, encapsulation

  • The data structure is difficult and the algorithm deals with subtle details. To produce the first working implementation, I spent a lot of time on understanding how the theoretical algorithm works and on debugging my code. When I ported the code to other languages, these one-time costs did not apply.

QR Code generator library
  • ~1000 lines of code per implementation

  • Featuring object-oriented design, module/component arrangement, bitwise integer math, arrays

  • There was a large upfront cost in reading and understanding the QR Code specification, along with organizing the logic of the first implementation in an architecturally coherent way

Overview of Project Nayuki software licenses
  • A list of all code I published on my web site, which includes the topic/title and the list of programming languages

  • About 40% of my pages have multi-lingual program code; the remaining 60% are mono-lingual

  • In decreasing order of popularity, I use Java (most often), JavaScript, Python, C, and so on.

My opinions on specific languages

Disclaimer: These are personal opinions based on my needs, skills, and weaknesses. There is no single right answer or universal truth about a language or feature being good or bad. The languages are listed in descending order of how familiar I am with them and how often I use them.


A general-purpose object-oriented language that is safe, concise compared to C, fast enough, and having a decent standard library. I like the static typing (easy refactoring and saves me from so many trivial mistakes), well-defined language and library semantics (predictable behavior and good documentation), platform independence (no need to deal with endianness, bit width, and platform tooling issues), decent speed for low-level numeric processing, Unicode text support, file and network I/O APIs, and competent multi-threading facilities (built-in locks and monitors, memory model, concurrency libraries). Java is my default/preferred language for many projects for these reasons; also I enjoy using the powerful Eclipse IDE. When I write a program intended to be ported to multiple languages, I almost always start with the Java version first; after it is fully functional and debugged then I translate it to other languages (such as adding header files and prototypes for C++, or stripping out types for JavaScript and Python). Things I dislike about Java include the fact that it is too heavyweight for quick throwaway scripts, it has a bunch of little quirks like signed bytes and type parameter erasure, it doesn’t support ad hoc list and dictionary data structures like Python and JavaScript do, and people commonly write bloated “enterprisy” code in Java. On a final note, I’m not convinced that JVM languages like Scala, Groovy, and Clojure are worth the trouble compared to plain old Java.


A lightweight object-oriented language that is concise, powerful, relatively safe, slow, and possessing a rich standard library. I like that it lives up to its reputation of being executable (and readable) pseudocode, its enforcement of proper code indentation, its practical and feature-packed standard library, the fact that you can declare lists and dictionaries in the code, the power of list comprehensions and generators, and its general lightweight feeling when reading/writing code. Python is my preferred language for writing short scripts and making small pieces of code to prove a point in a concretely implemented way. I don’t find it appropriate for large projects with many modules and data types. I dislike the Python 2 vs. 3 transition mess (note that I publish 2/3 polyglot code to appeal to both crowds but write 3-only code for private use), how some libraries changed names and features in a subtle way (unlike the stability of Java libraries), how some changes were made gradually and functions added gradually (instead of all at once like in Java), the poor protection against simple typos and data type errors due to the dynamic typing, the lack of standardized documentation syntax (unlike Javadoc), and the slow numerical performance (about 10× to 100× slower than Java, which is in turn slower than C; this is painfully apparent in my Project Euler solutions benchmark timings).


The lingua franca of web programming, JavaScript is a weakly typed, object-oriented, moderate speed language with only a basic standard library. It is superficially similar to Java in syntax and a couple of libraries (such as Math, String, Date), but is quite different in how values, objects, and functions work. The two main things I like about JavaScript are that it enables me to use HTML web pages as a powerful input and output medium, and that its syntax and semantics are reasonably sane (unlike say C++ or PHP). I also like how functions are first-class values, how easy it is to declare a function in an expression, and how smart people created JIT compilers that made JavaScript much faster than similar dynamic languages like Python and Ruby (JavaScript is about 5× slower than Java in arithmetic; Python is about 30× slower). I avoid problems related to JavaScript’s weak typing by thinking about and writing code in the mindset of static typing – in other words, I consider each variable to have one type (such as integer) and avoid doing potentially nonsensical operations that involve mixed types (such as integer < string). I dislike how I/O and event-handling code involves writing numerous function callbacks, sometimes even deeply nested callbacks. Writing vanilla JavaScript is good enough for me; I don’t care to use the popular libraries like jQuery, Angular, React, etc., or safer / more expressive languages like TypeScript that can be compiled down to JavaScript. One time I took a piece of Java code (Panel de Pon puzzle solver) that was type-correct and fully debugged (e.g. no out-of-bounds array accesses) and translated it to JavaScript; due to the non-trivial algorithms involved, it would have been risky to write and debug it in JavaScript as a first attempt.


A straightforward procedural language that is unsafe, well-supported on Unix, good for computationally intensive arithmetic and algorithms, and good for system programming. I like the raw speed and heavy compiler optimizations (especially for numerical algorithms), the Unix friendliness (a C compiler is usually available out of the box), and the clean interfacing with assembly code. I don’t like the poor support on Windows (need to install some distribution of GCC or the massive Microsoft Visual C++ toolset), the undefined behavior (makes arithmetic and so many operations downright treacherous), the bare-bones standard library, the variably sized integer types, the inconvenience of maintaining header files, and the manual memory management (lacking automatic garbage collection).


A baroque object-oriented programming language that is unsafe, good for applications that need a better way to organize data structures and methods, and a good upgrade path for C programs that get too big. I like it for applications where I could use C but where I need more structure and modularity – such as when I need to define numerous custom data structures and methods on them. I also like RAII for managing object lifetimes, convenient memory-managed containers like std::vector and std::string, and passing values by reference. All the things that I like and dislike about C also apply to C++; additionally I dislike how slow C++ compilation times are, how C++ fixes none of the problems of C, and how C++ adds many new facilities that are near-duplicates of existing C features.


A pure functional programming language with a strong emphasis on expressing algorithms in a compact and mathematically pure way. But the high level of abstraction in Haskell makes me constantly feel like I need a PhD in math to read other people’s Haskell code, utilize libraries, or to write any working code at all. I like Haskell’s static typing, algebraic data types, and rich standard library. I dislike how terse and opaque the code is when multiple higher-order functions are used, how I can’t reason about memory usage in long computational chains due to the lazy evaluation, how writing I/O and imperative code needs the programmer to jump through extra hoops, how unclear the mapping between language constructs and low-level machine operations are (thus frustrating the analysis of execution time and memory usage), how nothing seems to be straightforward and there is always a new theoretical concept to learn (e.g. continuation-passing style, type classes, infinite data structures, currying, string-appending functions, etc.).


A rich, safe, object-oriented programming language that is good for application programming on Windows. I have spent little time programming in C# and have barely seen the possibilities offered by the language (such as LINQ and GUI design). Right off the bat, C#’s naming conventions bother me because namespaces and members are capitalized in the same way as classes are (e.g. Namespace.Sub.ClassName.SomeMethod() in C# vs. package.sub.ClassName.someMethod() in Java). Due to C#’s blatant similarity to Java (I would say Java SE 1.4 is similar to C# 1.0), I compare its features and behaviors heavily to Java. And because I choose Java as my primary programming language, I try to stay within my comfort zone and write in the subset of C# that overlaps with Java. Overall I have a slight negative opinion of C# because it seems to start with Java’s design as a safe and simple C++-like OOP language, and then add tons of features to it that are hard to keep track of (e.g. properties, operator overloading, unsigned types, checked arithmetic, extension methods, partial classes, LINQ). Over the years, it has become clear that Java has taken a much more conservative direction than C# toward language extension – it tries to add new features in such a way that they can be easily mapped (de-sugared) into older features. Also I dislike the MSDN documentation for the standard library (i.e. the .NET Framework) because I find that the wording, presentation formatting, and code samples are much less helpful than the Java SE documentation. If I grew up in different circumstances and C# was my main programming language, I probably would know all its features and use them to make my code powerful and concise. But because I care about other programming languages too, I find many of C#’s features to be distractions that increase my cognitive load without bringing sufficient benefits. But all of this only reflects my opinion of C# as a beginner and can’t be taken as a properly informed opinion.

As an actual example of where I weigh programming languages for a specific application, see my Wikipedia PageRank article. There I discuss how the project would be expected to fare in Java, Python, and C++. My choice reflected the best balance between speed, safety, and library convenience for the application at hand.

Personal history of languages

I first learned computer programming nearly two decades ago. Very slowly over the years, I learned and got comfortable with more and more programming languages. The years and grades given in the list below are accurate to about ±1 year, because only in retrospect did I realize how my habits were changing:

  • 1999, Grade 6 (elementary school): Dabbled in web JavaScript programming on the browser side and on the server side (Classic ASP on Microsoft IIS).

  • 2002, end of Grade 8 (middle school): Learned Java by myself from a textbook. This was the first time I understood crucial programming concepts like array indexing and function calls. I also got better at JavaScript too, but didn’t use it much.

  • 2003, Grade 10 (high school): Learned Object-Oriented Turing by simply reading the official help manual (bundled with the IDE) for a few hours. I was exposed to this language because my high school had a Grade 10 course that taught Turing, but I skipped the course because I knew the basics of programming beforehand and felt the course was too simple. Nevertheless I was still able to become competent at programming in Turing in my spare time. I translated some of my algorithms like heap sort from Java to Turing, and appreciated the syntactic and semantic differences between the languages.

  • 2005, Grade 11 (high school): Learned C and C++ while doing programming competitions. I treated the languages as basically like Java but with a few modifications. I knew that numbers, arrays, and control flow were basically the same in syntax and semantics, but I/O functions and pointers were different. My only regret about my experience with C/C++ is that I didn’t understand the deadly concept of undefined behavior until ~7 years later.

  • 2007, university 1st year: Learned Python in an introductory programming course. Up until now, almost all my programming was done in languages with C-like syntax. Also did a summer job in Python server-side web programming, thus forcing me to really know the language to a level of working competence.

  • 2009 to 2011, university middle years: Took several courses where MATLAB was the main programming language for homework assignments. The course material focused on numerical analysis and matrix arithmetic. MATLAB’s archaic syntax (designed in the 1980s), suppression of mistakes or poor error handling, and the proprietary software licensing all detracted from my enjoyment of the language.

  • 2012 to 2015, after graduation: Did lots of C++ coding and debugging in different jobs, hence refining my fluency and understanding of the language. For example I learned the C++ equivalents of features that existed in C, such as references vs. pointers, iostream vs. stdio, class vs. struct.

  • 2013: Around this time I discovered that I actually enjoyed programming in multiple languages. When writing articles on the Project Nayuki web site and coding Project Euler solutions, I started to put a bigger emphasis on publishing as many programming language versions of an algorithm as reasonably possible.

Final thoughts

  • A programmer who is stuck using one programming language will architect and implement code in a way that optimizes for the particular set of features and bugs in that language. For example a Java programmer can’t apply list comprehensions and will fail to appreciate their conciseness; a Python programmer might create/modify an object’s methods at run time even when there are safer alternatives that would be applied in a statically typed language; a C programmer might spend effort re-ordering functions so that no prototypes are required even though it may not be the most natural reading order for a human. The mono-lingual programmer will find it hard to distinguish between details of a particular programming language from widely applicable concepts in general computer science.

  • When expressing a piece of logic in multiple programming languages, sometimes the structure differs immensely from language to language (e.g. functional vs. imperative style of manipulating lists), but sometimes the differences are just trivial mappings of a few words (e.g. bool in C++ vs. boolean in Java; else if (...) in C/C++/Java vs. elif ...: in Python). Viewed in this light, I have a higher resistance to try programming languages – I want to see something substantially better (and hardly any things worse) before I’m willing to adopt a new language.

  • Some personal weaknesses I’ll admit include: There are too many minutiae (header files, integer type widths, object ownership and deallocation) and pitfalls (all sorts of ways to trigger undefined behavior) in C and C++ that I feel mentally not strong enough to handle it except for special limited-scope projects. I missed learning some popular lightweight languages like Ruby, Lua, and Go. I’m too averse to frameworks (especially JavaScript web frameworks like jQuery, etc.) and frequently use only the base/vanilla language itself. I tried doing hardcore functional programming in Haskell but feel utterly incompetent in it.

More info