§4.4.

Simplicity in programming languages

In the previous section, I discussed aspects of simple code. However, it is also possible to consider simplicity in programming languages.

  • Is JavaScript a simple programming language?

  • Is JavaScript a good programming language?

Again, one way to think about a programming language is whether it has three attributes of simplicity:

  1. Uses a small number of rules

  2. Each rule is easy to understand

  3. No (or very few) exceptions to the rules

Addition and concatenation

To explore simplicity, consider the + operator that performs both addition and string concatenation in many programming languages. When given two numbers, + typically performs addition. When given two strings of text, + typically performs string concatenation.

The following table demonstrates the effects of the + operator:

Code

Result

Operation

1 + 2

3

Numerical addition

1 + 10

11

Numerical addition

"a" + "b"

"ab"

String concatenation

"1" + "2"

"12"

String concatenation

This table is valid for many programming languages, including JavaScript, Python, Java, Ruby, Go, C# and SQL.

So far, the rules underpinning this table are simple: numbers get added, strings get concatenated.

Simultaneously handling strings and numbers

What should be the result of using + on different types? For example, what is the result of adding the number 1 to the string "2" (i.e., 1 + "2")?

A programming language designer could define the + operator to do anything. Here are just some possibilities:

  1. 1 + "2" results in the number 3

  2. 1 + "2" results in the string "12"

  3. 1 + "2" raises an exception

  4. 1 + "2" causes the computer speakers to generate an alarm noise, then ignores the current line and skips to the next line of code

  5. 1 + "2" results in the boolean value false

  6. 1 + "2" results in the string "Today is Monday"

Which of these outcomes are reasonable? Which would result in a "simple" programming language?

f is not reasonable: there is no clear rule that would explain why adding a number to a string should result in the day of the week. It makes no sense.

Similar reasoning excludes e: it is excessively complex to introduce a third type (i.e., boolean) to explain what should happen when combining a number with a string.

The behavior of d is also eccentric: the consequences of skipping lines could be unpredictable for a developer. Furthermore, there isn’t a simple relationship between the idea that combining numbers with strings should result in speakers playing sounds. [1]

The remaining options a, b and c are more reasonable.

Both a and b would suggest a general language principle that mismatched types must be automatically converted before performing the operation. In a, numbers are preferred (i.e., strings get converted to match the number). In b, strings are preferred (i.e., numbers are converted to match the string type).

Possibility c, would make sense in a language with a general principle that mismatched types are a problem: they suggest programmer error and should result in an error or exception.

Language designers do not choose between these possibilities in isolation. The rules of the language need to be consistent with other combinations of parameters. For example, what happens if you add a number 1 to a boolean true?

In a language based on possibility c, it is simple to anticipate that the result would also be an exception. However, for languages a and b, it isn’t straightforward: which types are preferred and how do you convert between them?

The Python programming language closely follows option c, whereas JavaScript follows option b.

+ in Python

The following table shows the different outcomes of + in Python. You can read the table by using the row and column headers: the value of each cell is the result of adding its row header to its column header.

+

True

False

1

"2"

None

{}

[]

True

2

1

2

TypeError

TypeError

TypeError

TypeError

False

1

0

1

TypeError

TypeError

TypeError

TypeError

1

2

1

2

TypeError

TypeError

TypeError

TypeError

"2"

TypeError

TypeError

TypeError

"22"

TypeError

TypeError

TypeError

None

TypeError

TypeError

TypeError

TypeError

TypeError

TypeError

TypeError

{}

TypeError

TypeError

TypeError

TypeError

TypeError

TypeError

TypeError

[]

TypeError

TypeError

TypeError

TypeError

TypeError

TypeError

[]

It is apparent, by visual inspection, that the table has a simple structure. There is a parsimonious explanation that can explain the entire table:

  • Numbers are added

  • Strings are concatentated

  • Arrays ([]) are concatenated

  • Treat True as the number 1 and False as the number 0

  • Any other combination results in an error

+ in JavaScript

Consider the table of outcomes for + in the JavaScript programming language:

+

true

false

1

"2"

null

{}

[]

true

2

1

2

"true2"

1

"true[object Object]"

"true"

false

1

0

1

"false2"

0

"false[object Object]"

"false"

1

2

1

2

"12"

1

"1[object Object]"

"1"

"2"

"2true"

"2false"

"21"

"22"

"2null"

"2[object Object]"

"2"

null

1

0

1

"null2"

0

"null[object Object]"

"null"

{}

"[object Object]true"

"[object Object]false"

"[object Object]1"

"[object Object]2"

"[object Object]null"

"[object Object][object Object]"

"[object Object]"

[]

"true"

"false"

"1"

"2"

"null"

"[object Object]"

""

I have known JavaScript for decades, yet I’m still astonished by the complexity of this table.

You can verify the table by testing combinations in Node.js:

$ node
Welcome to Node.js
Type ".help" for more information.
> true + true
2
> {} + {}
'[object Object][object Object]'
> typeof ({} + {})
'string'
>
Exercise: Explain ‘+’

Before reading on, I encourage you to figure out a simple explanation for how + works in JavaScript. Is there a simple set of rules that can explain the table of outcomes above?

To understand the table, it helps to understand JavaScript’s history. The original purpose of JavaScript was to be a “little” programming language. It was a simple language designed for beginner programmers to add short scripts to their web pages. JavaScript’s designers expected experienced developers to use the Java programming language for sophisticated programs.

For a developer to understand the rules for + in a language like Python, they would need an understanding of several advanced concepts: types, the differences between numbers and strings, exceptions, and how to handle exceptions. The designer of Python expects developers to understand these concepts, so he prefers that developers discover their mistakes early, rather than allowing potentially incorrect code to run in production.

In contrast, the design of JavaScript prioritizes code that runs rather than code that is correct. JavaScript makes it easier for a beginning programmer to edit and experiment with their code to seeing how their changes influence the outputs. JavaScript avoids complex compile errors or exceptions that would stop a beginner programmer from making any progress with their program.

So, JavaScript is designed with the philosophy that some result — any result — is better than a difficult-to-understand error message.

With that goal in mind, JavaScript is not entirely arbitrary. It has consistent rules underpinning the + operator. These rules ensure that any possible combination of inputs produces some result: [2]

  1. First, any objects or arrays are converted into a "primitive" value (i.e., undefined, null, boolean, number or string). In practice, this means that the toString() method is used to convert the objects and arrays into strings.

    • The default toString() method for an object ({}) returns "[object Object]"

    • The default toString() method for a list ([], [1,2,3], ["a","b","c"]) converts the elements into strings ("", "1,2,3", "a,b,c")

  2. Next, if either operand is a string, then perform string concatenation

  3. Otherwise, perform numeric addition

    • true is equivalent to 1

    • false and null are equivalent to 0

Perhaps one way to think of these rules is to understand that JavaScript always attempts to perform either numeric addition or string concatenation. If the operands aren’t already numbers or strings, it will first convert the values to primitives before adding.

Discussion

The question “Is JavaScript a simple language?” is not straightforward. In many respects, the language appears complex and inconsistent (recall the complexity of the outcomes of the + operator in JavaScript). For an experienced developer, languages such as Python appear to be simpler and more consistent than JavaScript.

Nevertheless, I am hesitant to say that JavaScript is not a “good” language. Although JavaScript’s rules are not as simple as Python, there are consistent rules underpinning the language. Remembering JavaScript’s history makes it easier to understand: JavaScript is designed so that almost every operation will succeed with an outcome, even if that ‘successful’ outcome does not make sense.

Today, millions of professional developers use JavaScript. It has evolved far beyond its original purpose. As a professional developer, you have the power to make JavaScript a better and simpler language by avoiding rules that introduce unexpected complexity. You could use true + true + true + null in your code to represent the number 3, but that would make your code nearly impossible for others to understand. Instead, if you only use + to add numbers (2 + 2) or to concatenate strings ("hello " + "world"), then you are ensuring that the programming language you use is a simple, clear and more beautiful subset of JavaScript. It is your responsibility to use discretion in the features of JavaScript (or any programming language) that you use.

The JavaScript interpreter (e.g., Node.js) is also a computer program. Thus, a discussion of simplicity in programming languages is also worth considering as a case study in software design. It is not always easy to create something simple: difficult problems often require difficult solutions.

In JavaScript, simplicity means allowing beginners to use a language with fewer concepts but more complex rules (i.e., no need to understand exceptions, but a more complex rule for +). In Python, simplicity means giving programmers more advanced concepts, but fewer rules to remember (i.e., the + operator raises an exception for any inputs that don’t make sense).

As you design software, carefully consider every function you write. For example, is it simpler to understand a complex firstHasGreaterAverage function or to use an average function twice? There is no best answer in any design decision: you need to think about your preferences, your objectives and, most importantly, the other developers on your team who need to read your code.

Exercise: JavaScript operators

In this section, I discussed the JavaScript + operator. In the next chapter, I will discuss equality (==) in JavaScript.

Experiment with Node.js to find the output of other JavaScript operators with different parameters:

  • - (subtraction)

  • < (less than)

  • * (multiplication)

Are there simple rules to explain the behavior of these operators?


1. Microsoft’s Visual Basic programming language could ignore some errors by skipping over lines (On Error Resume Next)! However, its use was rare and was strongly discouraged because the results could be so unpredictable.
2. The formal definition of the addition operator appears in the language standard.