Every now and then I hear some ridiculous things about the equals symbol. Some large subset of programmers—perhaps related to functional programmers, perhaps not—seem to think that = should only and ever mean “equality in the mathematical sense.” The argument usually goes,

*Functional programming gives us back that inalienable right to analyze things by using mathematics. Never again need we bear the burden of that foul mutant x = x+1! No novice programmer—nay, not even a mathematician!—could comprehend such flabbergastery. Tis a pinnacle of confusion!*

It’s ironic that so much of the merits or detriment of the use of = is based on a veiled appeal to the purity of mathematics. Just as often software engineers turn the tables, and any similarity to mathematics is decried as elitist jibber jabber (*Such an archaic and abstruse use of symbols! Oh no, big-O!*).

In fact, equality is more rigorously defined in a programming language than it will ever be in mathematics. Even in the hottest pits of software hell, where there’s = and == and ===, throwing in ==== just to rub salt in the wound, each operator gets its own coherent definition and documentation. Learn it once and you’ll never go astray.

Not so in mathematics—oh yes, hide your children from the terrors that lurk. In mathematics equality is little more than a stand-in for the word “is,” oftentimes entirely dependent on context. Now gather round and listen to the tale of the true identities of the masquerader known as =.

Let’s start with some low-hanging fruit, the superficial concerns.

$ \displaystyle \sum_{i=1}^n i^2 + 3$

If = were interpreted literally, $ i$ would be “equal” to 1, and “equal” to 2, and I’d facetiously demand $ 1 = 2$. Aha! Where is your Gauss now?! But seriously, this bit of notation shows that mathematics has both expressions with scope and variables that change their value over time. And the $ \sum$ use for notation was established by *Euler*, long before algorithms jumped from logic to computers to billionaire Senate testimonies.

Likewise, set-builder notation often uses the same kind of equals-as-iterate.

$ \displaystyle A = \{ n^2 : n = 1, 2, \dots, 100 \}$

In Python, or interpreting the expression literally, the value of $ n$ would be a tuple, producing a type error. (In Javascript, it produces 2. How could it be Javascript if it didn’t?)

Next up we have the sloppiness of functions. Let $ f(x) = 2x + 3$. This is a function, and $ x$ is a variable. Rather than precisely say, $ f(2) = 7$, we say that for $ x=2, f(x) = 7$. So $ x$ is simultaneously an indeterminate input and a concrete value. The same scoping for programming functions bypass the naive expectation that equality means “now and forever.” Couple that with the question-as-equation $ f(x) = 7$, in which one asks what values of $ x$ produce this result, if any, and you begin to see how deep the rabbit hole goes. To understand what someone means when they say $ f(x) = 7$, you need to know the context.

But this is just the tip of the iceberg, and we’re drilling deep. The point is that = carries with it all *kinds* of baggage, not just the scope of a particular binding of a variable.

Continuing with functions, we have rational expressions like $ f(x) = \frac{(x+1)x}{x}$. One often starts by saying “let’s let $ f$ be this function.” Then we want to analyze it, and in-so-doing we simplify to $ f(x) = x+1$. To keep ourselves safe, we modify the domain of $ f$ to exclude $ x=0$ post-hoc. But the flow of the argument is the same: we defer the exclusion of $ x=0$ until we need it, meaning the equality at the beginning is a different equality than at the end. In effect, we have an infinitude of different kinds of equality for functions, one for each choice of what to exclude from the domain. And a mathematical proof might switch between them as needed.

“Why not just define a new function $ g$ with a different domain,” you ask? You can, but mathematicians don’t. And if you’re arguing in favor or against a particular notation, and using “mathematics” as your impenetrable shield, you’ve got to remember the famous definition of Reuben Hersh, that “mathematics is what mathematicians do.” For us, that means you can’t claim superiority based on an idea of mathematics that disagrees with mathematical practice. And mathematics, dear reader, is messier than programmers and philosophers would have one believe.

And now we turn to the Great Equality Contextualizer, the **isomorphism. **

You see, all over mathematics there are objects which are not equal, but we want them to be. When you study symmetry, say, you learn that there is an algebraic structure to symmetry called a group. And the same structure—that is, the same true underlying relationships between the symmetries of a thing—can show up in many different guises. As a set, as a picture, as a class of functions, in polynomials and compass constructions and wallpapers, oh my! In each of these things we want to say that two symmetry structures are the same even if they look different. We want to overload equality when four-fold rotational symmetry applies to my table as well as a four-pointed star.

The tool we use for that is called an isomorphism. In brief terms, it’s a function between two objects, with an inverse, that preserves the structure you care about both ways. In fact, there *is* a special symbol for when two things are isomorphic, and it’s often $ \cong$. But $ \cong$ is annoying to write, and it really just means “is the same as” the same way equality does. So mathematicians often drop the squiggle and use =.

Plus, there are a million kinds of isomorphism. Groups, graphs, vector spaces, rings, fields, modules, algebras, rational functions, varieties, Lie groups, *breathe* topological spaces, manifolds of all stripes, sheaves, schemes, lattices, knots, the list just keeps going on and on and on! No way are we making up a symbol for each one of these and the hundreds of variations we might come up with. And moreover, when you say two things are isomorphic, that gives you absolutely no indication of *how* they are isomorphic. It fact, it can be extremely tedious to compute isomorphisms between things, and it’s even known to be uncomputable in extreme cases! What good is equality if you can’t even check it?

*But wait!* You might ask, having read this blog for a while and knowing better than to not question a claim. *All of these uses of equality are still equivalence relations, and x = x + 1 is not an equivalence relation!*

Well, you got me there. Mathematicians love to keep equality as an equivalence relation. When mathematicians need to define an algorithm where the value of $ x$ changes in a nontrivial way, it’s usually done by setting $ x_0$ equal to some starting value and letting $ x_{n}$ be defined as some function of $ x_{n-1}$ and smaller terms, like the good ol’ Fibonacci sequence $ x_0 = x_1 = 1$ and $ x_n = x_{n-1} + x_{n-2}$.

*If mutation is so great, why do mathematicians use recursion so much? Huh? Huh?*

Well, I’ve got two counterpoints. The first is that the goal here is to *reason* about the sequence, not to describe it in a way that can be efficiently carried out by a computer. When you say x = x + 1, you’re telling the computer that the old value of x need not linger, and you can do away with the space occupied by the previous value of x. To achieve the same result with recursion requires a whole other can of worms: memoization and tail recursive style and compiler optimizations to shed stack frames. It’s a lot more work to understand all that (to get to an equivalent solution) than it is to understand mutation! Simply stated, the goals of mathematics and programming are quite differently aligned. The former is about understanding a thing, and the latter is more often about describing a concrete process under threat of limited resources.

My second point is that mathematical notation is so flexible and adaptable that it doesn’t *need* mutation the same way programming languages need it. In mathematics we have no stack overflows, no register limits or page swaps, no limitations on variable names or memory allocation, our brains do the continuation passing for us, and we can rewrite history ad hoc and pile on abstractions as needed to achieve a particular goal. Even when you’re describing an algorithm in mathematics, you get the benefits of mathematical abstractions. A mathematician could easily introduce = as mutation in their work. Nothing stops them from doing so! It’s just that they rarely have a genuine need for it.

But of course, none of this changes that languages could use := or “let” instead of = for assignment. If a strict adherence to asymmetry for asymmetric operations helps you sleep at night, so be it. My point is that the case when = means assignment is an extremely simple bit of context. Much simpler than the albatrossian mental burden required to understand what mathematicians really mean when they write $ A = B$.

*Postscript: I hope everyone reading this realizes I’m embellishing a bit for the sake of entertainment. If you want to fight me, tell me the best tree isn’t aspen. I dare you.*

*Postpostscript: embarrassingly, I completely forgot about Big-O notation and friends (despite mentioning it in the article!) as a case where = does not mean equality! f(n) = O(log n) is a statement about upper bounds, not equality! Thanks to @lreyzin for keeping me honest.*

best_tree != aspen

No! Wrong!

For certain values of !

I’m all for jabs at JavaScript, but ^ is XOR operator. ** is a power operator, which correctly returns NaN… well, unless it is a size 1 array. 🙂

Also [0]==0, 0==[], but [0] != [] … non-transitive equality!

I’m aware of the operator, but still find the behavior confusing. Does a list of any size get typecast to 0? How bizarre!

And good point about the non-transitivity of == in Javascript!

Basically, a == b (or b == a) where a is an Object (including Array) and b is a primitive value (like a number), a is converted to a string first before comparing it to b. Arrays are converted to strings by converting its elements to strings and joining those with commas. If a string is compared to a number, the string is converted to a number. “0” and “” converted to a number are both 0.

When both a and b are Objects, a == b is only true if a and b are the same object (so [] != []).

It’s chaotic and awful, but there is some logic behind it.

== is not even reflexive for most programming languages! Because NaN == NaN is false.

But saying the best tree is Aspen is overloading equality in a different way! The best tree is a tree, and Aspen is a type of tree!

*Runs and hides*

I’ve been entertained! (And educated) Thanks!

Thanks for this post. This is also an annoyance of mine, people insisting that “=” should mean some poorly considered notion of equality rather than simply whatever sense of “is” that mathematicians use it for in appropriate contexts. But actually I encounter it most often not from programmers (functional or otherwise) but from people complaining about O() (“Big Oh”) notation (mentioned in your postpostscript), and insisting on needlessly pedantic set-theoretic notation instead of just the much more versatile and (used by professionals) equals sign. (A little bit on that here: https://shreevatsa.wordpress.com/2014/03/13/big-o-notation-a-couple-of-sources/ — relevant quote from Knuth is: “mathematicians customarily use the = sign as they use the word “is” in English: Aristotle is a man, but a man isn’t necessarily Aristotle”.)

Mathematics is not about things being formal. Contrary to apparent popular belief, most of mathematical texts are not. I’d give two illustrations: Xi-Ping Zhu and Huai-Dong Cao formalized Grigory Perelman’s proof of the Poincaré conjecture (expanding original paper multifold), but most mathematicians consider Perelman’s work sufficient. Secondly, there are automated systems for proof verification, but usually they require a transformation of normal mathematical text into special form. In fact, informality of mathematical texts is why it takes professional mathematician to understand it.

OTOH computer languages are always completely formal. Even when formal behavior is not completely defined by language definition, it is defined by compiler / CPU. Regardless of ‘=’. 🙂

My professor hates using the equal sign for big Oh notation. In his opinion it hides the purpose of O and uses f(x)∈O(n²) instead. Because O(n²) is the set of all functions that have c*n² (c∈R) as upper bound.

But of course, this further shows that math is just language and thus, differs from place to place.

For the record, that is not the correct definition of big-O without the word “asymptotically” before upper bound.

Choosing “c” appropriately, the word “asymptotically” can be dropped. I mean if you want to use mathematical language and idealism. The word “asymptotic” also does not seem meaningful if we talk about real world computing machines. “For very, very big ‘n'” is more accurate because infinity has no place in real world computation.

I think this from near the end is interesting:

“the goals of mathematics and programming are quite differently aligned. The former is about understanding a thing, and the latter is more often about describing a concrete process”

The developers I know who use functional languages (none of whom have ever made this argument about equality that you tear down) argue that one of FP’s main benefits is that tends to be conducive to writing code that is easier to understand. The closest they get to the position you’re arguing against is that shared mutable state is bad, but the normal argument is not that = has a particular meaning in maths. It’s that analysing the behaviour of any code that uses that state is much harder than analysing code that does not.

It may be true that most developers have limited interest in understanding their code, but this is arguably a problem. It might partially explain why as an industry we are apparently incapable of producing secure systems.

Summarizing,

You don’t correct a mathematician. Doing so may bring new meaning to the notation.

As for programmers,

they should be preaching. Don’t assign values, bind values.

Jeremy – I’m a mega fan of your work.

But think going deeper into this is quite fun

Your post goes to the point at the heart of philsophical number theory.

What does equality mean ?

Yup – you got functinal equivalence, isomorphism, and temporary assignment of values.

But I think you could prove – that all these types of equality – are “instances” of “different implementations” of “equivalence.

They are no more equivalent than 1 = 1 is equivalent.

I.e. 1 = 1 means I think we can define a bijective “counting function” that proves there’s the “same number” of “elemetns” in the “sets”

I think (not sure) – if you define – counting fucntion / same number / elements / sets differently – you get the differing definitions of equivalence you enumerate.

The interesting thing for me is that 1 = 1 is defined clear in 4 of peano’s axioms

https://en.wikipedia.org/wiki/Peano_axioms#Formulation

And you could mentally – try to develop different (and potentially) – more powerful notions of “equivalence” – with differing axioms

A final point… the prevalence of several “similar” concepts of equivalence in computer science – may point to an underlying “platonic idea” of equivalence – that either exists dormant in the world awaiting for us to discover it; or is a useful “technologocial” construct – that has accelerated “progress”

I assume you were bothered by the recent Hacker News post/thread on the subject of equality as well? 🙂

Your point is well made and completely right.

Hooray! Many people in “theory of programming” think that Mathematics is Formal Logic of a particularly limited form.

This post confuses syntactically overloaded “=” in math with semantically ambiguous “==” in programming. I would be willing to bet that most people who proclaim to be mathematicians will be able to answer a simple question of “what does = mean in this particular context” without any problem, but almost no JavaScript developer would be able to tell me what “a == b” means (other than self-referential “== is equality”). Things are no better when it comes to “=” in C++ or “equals” in Java.

You can’t reason about things you _can’t even define_ on the spot.

This article is rather about mathematicians using syntactic sugar than equal sign not meaning equality. For example, $n = 1, 2, \ldots, 100$ is short for $n \in \{ 1, 2, \ldots, 100 \}$ and $\cdots + O(x)$ is syntactic sugar for “$\cdots + r(x)$ where $r \in O(x \maptsto x)$ [in some limit]”. Here $O(f)$ is an equivalence class (a set) consisting of those functions that in a specific sense behave as $f$ in a certain limit.

Also, a real mathematician would not write “let $f$ be the function $f(x) = \frac{(x+1)x}{x}$” and let the domain be defined ad hoc, but rather write “let $f : \mathbb{R} \setminus \{ 0 \} \to \mathbb{R}$ be defined by $f(x) = \frac{(x+1)x}{x}$.” And the equality sign here does not mean “set the left hand side to the right hand side”. Instead it really means equality and $f$ is a function satisfying this equality.

This said, both being a mathematician and a programmer, I have no problems using equal sign stand for assignment and write things like $x = x + 1$. This really means $x(k+1) = x(k) + 1$ where $x(k)$ is the value of $x$ at step $k$ during the run of the program.

I disagree with your characterization of “a real mathematician,” as most mathematicians I work with would not bother to define the domain when the domain is so obvious. That’s my point: mathematicians go far beyond the line of “syntactic sugar” to “dependent on context.” Pointing out that one didn’t exclude the trivial case almost always yields eye rolls or a joke along the lines of “we’re not undergrads.”

But more to your point, “syntactic sugar” refers to convenient but unambiguous notation. Having tons of different kinds of syntactic sugar for the same symbol means you can’t call it syntactic sugar any more; it’s only well-defined contextually, which is my point. Birational equivalence A = B isn’t equality of the A and B, it’s the existence of a function f with some properties for which f(some part of A) = some part of B. The structure of f is just as crucial as the equality part. That’s not equality of A and B, and that’s not even (just) equality of f(A) and B! Conflating “equality” (as the hypothetical programmer expects) with “equality of some derived thing, plus a whole bunch of conditions and baggage unrelated to the equality itself” is exactly why = doesn’t mean equality.

“The first is that the goal here is to reason about the sequence, not to describe it in a way that can be efficiently carried out by a computer.” – this is exactly why some of programmers use FP, which turns out to be also more performant than most of imperative implementations. When we look at what FP programmers preach it is more about composability and reasoning about the structure of any given program/algorithm. “Programs must be written for people to read, and only incidentally for machines to execute.” – Harold Abelson.

As to “=” meaning equality, in most FP there is a separate category for pure equality, which is Eq typeclass (or however it is implemented).

Tomasz, consider this quote from the post:

“To achieve the same result with recursion requires a whole other can of worms: memoization and tail recursive style and compiler optimizations to shed stack frames. It’s a lot more work to understand all that (to get to an equivalent solution) than it is to understand mutation!”

So you generally get performance when you do rather complicated things, or otherwise FP typical recursion is less performant.

Of interest to any readers might be the essay by Barry Mazur, “When is one thing equal to some other thing”.

Did you see this from reddit the other day? I basically made the same argument in less detail as the top comment https://www.reddit.com/r/programming/comments/8bft82/why_does_mean_assignment/dx6gp58/

I did not, sorry. I tend to steer clear of technical discussions on Reddit.

haha fair! Glad to see you had a view similar to mine from a maths perspective though as some of the comments discussion made me doubt if I was coming at it from the right direction. I’ve been following your blog for about 4 years now (about to graduate master in maths) and always look forward to new posts, so thanks for all the cool content!

I am a General Theory of Systems analyst and this is the most bedeviling problem in the field, because the issue is not limited to mathematics but is intrinsic in general languaging, concepts, dynamics comparisons and (god-forbid) tautologies.

Information theory definitions vary. Interpretations of ‘entropy’ vary. And delving into a unified model of ‘systems behaviors’ the universe — beyond physics — just leads to territorial arguments and attitudes.

I found your post as I jumped into an exasperated google search “equalities”. It was at the top. 🙂 Would you be kind enough to email me for further conversation? I’d be grateful. integrity at prodigy dot net James (I am not a twitter addict and view WordPress only infrequently. thank you!) Jan 3, 2020