Have a tweet:
I have no idea if Pony is making the right choice here, I don’t know Pony, and I don’t have any interest in learning Pony. But this tweet raised my hackles for two reasons:
- It’s pretty smug. I have very strong opinions about programming, but one rule I try to follow is do not mock other programmers.1 Programming is too big and I’m too small to understand everything. Disagreeing is fine, laying out why people are wrong is fine, making fun of them is not fine.
- It’s saying that Pony is mathematically wrong. This is objectively false.
I tweeted a thing about why
1/0 = 0 is mathematically sound. Some people agreed, some people agreed with caveats, and some people called it bunk. A few people said it’s clear I don’t know real mathematics, because a real mathematician would never make such a mistake.
So in this post I’d like to clearly, formally lay out why it’s consistent to say that
1/0 = 0, why some of the common objections don’t apply, and what the real mathematicians say. Fair warning, this post is going to be a little more mathematically dense than my usual stuff. I’ve tried to make it clear but, well, math.
First we need to explain what we mean by “consistency”. The formalism is sound if you can only use it to prove true statements. To say
1/0 = 0 is unsound is to either say that we can prove
1/0 ≠ 0 or that, given
1/0 = 0, we can prove something that’s false. These are actually equivalent statements but it’ll be useful to treat them as distinct for teaching purposes.
Next we need to explain what we mean by “division”. But to do that, I need to introduce fields.
A field is a set of elements (S) along with an addition operator (
+) and a multiplication operator (
*) that follow some properties:
- The set is closed under both operations. If
xis an element of S and
yis an element of S, then both
x + yand
x * yare elements of S.
- Both operations are commutative.
a + b = b + a, and
a * b = b * a.
- Both operations are associative.
a + (b + c) = (a + b) + c, and
a * (b * c) = (a * b) * c.
- Multiplication is distributive.
a * (b + c) = a * b + a * c.
- There is an element that is the additive identity, or
0, such that
a + 0 = a.
- There is an element that is the multiplicative identity, or
1, such that
a * 1 = a.
- The two identities are different elements.
1 ≠ 0.
- Every element has an additive inverse,
-a, such that
a + (-a) = 0.
- Every element EXCEPT 0 has a multiplicative inverse,
a⁻, such that
a*a⁻ = 1.
These are all of the rules of a field. We can define S, +, and * any way we want, as long as all of the rules are followed. The real numbers, along with our conventional notion of addition and multiplication, form a field. This gives us the building blocks to discover properties of our system. In order to prove a theorem, we need to be able to derive it solely through the definitions of
S/+/*, the definition of a field, and any axioms we have.2 For example, we can prove that for all
a * 0 = 0:
- By the additive identity,
0 + 0 = 0, so
a * 0 = a * (0 + 0)
- By the distributive property,
a * (0 + 0) = a * 0 + a * 0
- By the additive inverse property, there is some
- (a * 0)such that
- (a * 0) + (a * 0) = 0.
- Combining (1) and (2), we get
a * 0 = a * 0 + a * 0.
- Combining (3) and (4), we get
- (a * 0) + (a * 0) = - (a * 0) + a * 0 + a * 0.
- Evaluating (5) gives us
0 = a * 0. QED.
Now that we have this as a theorem, we can use it to prove other theorems. Given this property, we can immediately show that there is no multiplicative inverse of 0:
- If 0 had a multiplicative inverse
0 * 0⁻ = 1.
- For all real numbers,
a * 0 = 0.
1 ≠ 0, there is no multiplicative inverse of
Okay, now we can talk about division in the reals.
The field definition does not include division, nor do our definitions of addition or multiplication. This means we are free to define division however we want. We want to define it in a way that mostly follows our intuition and is sound. I say mostly because our intuition doesn’t generalize. As an example, we intuitively think of
a * b as “
a summed up
b times”. So what’s
-1 * π? How do you sum up something π times? While it would be nice if division didn’t have any “oddness” to it, we can’t guarantee that without kneecapping mathematics.
The intuitive definition of division is multiplying by the inverse.
a/2 = a * 2⁻. Under this definition, we can get all of the properties of division we’re used to by proving that they hold. For example,
a/a = 1. Proof:
a/a = a * a⁻ = 1.
a * (b/c) = b * (a/c). Proof:
a * (b/c) = a * (b * c⁻). Since multiplication is commutative and associative, we can rearrange this to get
b * (a * c⁻) = b * (a/c).
This is all great, except for one problem: 0 does not have a multiplicative inverse. Both of those proofs are invalid: if I write
0/0, I get
0 * 0⁻, which is an invalid equation. So we cannot prove that
a/a = 1. We can, however, prove something weaker:
- Theorem: IF
a ≠ 0, THEN
a/a = 1. Proof: same.
- Theorem: IF
c ≠ 0, THEN
a * (b/c) = b * (a/c). Proof: same.
Note this does not go both ways: it does not follow that
0/0 ≠ 1. All we know is we cannot use this theorem to prove that
0/0 = 1. So we do not have that
0/0 ≠ 1. For any given number, we cannot prove that
0/0 is not that number! Since we’ve defined division as multiplying by the inverse, and zero does not have an inverse, our definition of division does not cover dividing by zero. It does not say anything about it, leaving it “undefined” if you will.
Since this form of division is not defined for 0, it is a partial function over the reals: there is some value in its domain that we have not specified. Practically, this is fine: we’re used to thinking of
1/0 as an impossible operation. But this has some weird consequences: one of our axioms is everything is equal to itself. So does
1/0 = 1/0? If you say that’s silly, we no longer have
a = a! And even if you’re fine with that, what about the statement
TRUE \/ (1/0 = 1/0)? It shouldn’t matter what the second clause is, because that statement should be true. But by the axiom of the excluded middle, we’d then have to say the second clause is either true or false. When trying to formalize math and logic, we have to find some way to address what happens if you write
1/0. So there’s generally three things mathematicians can do:3
- We can say nope, sorry, you can’t even write
1/0, much less give it a value. This is what we do in our day-to-day lives, and the way that Agda and Idris handle division.
- We can choose some value that isn’t a real number, such as “undefined” or infinity, and say
x/0 = <whatever>. This is what some mathematicians do with the Riemann sphere.
- We could choose some real number, like 19, and say that
x/0 = 19. This is what Isabelle, Lean and Coq do.
All of these have tradeoffs. We already talked about some of the headaches with banning equations like
1/0. With an “undefined” value, division is no longer closed on the reals. In the last case, you explode everybody’s intuitive notion of division. But all of these are sound. Since our original notion of division does not say anything about dividing by zero, it does not rule out anything. None of these extensions lead to a contradiction.
The controversy is over the last case, so let’s focus on that. We’ll define division as follows:
IF b = 0 THEN a/b = 1 ELSE a/b = a * b⁻.
Something I need to emphasize here: this does not give us an inverse of 0.
1/0 is not
0⁻. This means that while
0/0 = 1,
0 * 1/0 = 0. Division is only “multiply by the inverse” when the denominator isn’t 0. All we’ve done is special case dividing by zero and nothing else. And doing so is mathematically consistent, because under this definition of division you can’t take
1/0 = 1 and prove something false.
Here’s where a lot of people objected. They would take the fact
1/0 = 1 and prove something false, usually
1 = 0. None of these proofs, however, are sound. To see why, let’s dig into a couple example proofs and show where they break down.
Here’s a common argument:
1/0 = 1
1/0 * 0 = 1 * 0
1 * 0/0 = 0
1 = 0
The problem is in step (3): our division theorem is only valid for
c ≠ 0, so you can’t go from
1/0 * 0 to
1 * 0/0. The “denominator is nonzero” clause prevents us from taking our definition and reaching this contradiction.
Here’s where people got tripped up. They assume we needed the nonzero clause on our division theorems because
x/0 is undefined. “If
x/0 is a value, then the theorem should extend to
c=0, too.” This is wrong. The problem is not that
1/0 was undefined. The problem was that our proof uses the multiplicative inverse, and there is no multiplicative inverse of 0. Under our modified definition of division, we still don’t have
0⁻, which means our proof still does not work for dividing by zero. We still need the condition. So it is not a theorem that
a * (b / 0) = b * (a / 0).
To be clear, this does not mean they must be different! All we know is that we cannot use this theorem to argue they are equal. Since the “proof” that
1 = 0 used that theorem, the proof is unsound.
Pretty much every counterargument makes this exact same mistake: it assumes that because
1/0 is now defined, there is now some
0⁻ that generalizes our theorems. But there’s not.
Another common objection is that if
1/0 = 1, then multiplicative inverses are no longer unique:
2/2 = 1, but also
2/0 = 1, so now 2 has two inverses. This, again, confuses cause and effect.
1/2 is the inverse of 2 not because of how we define inverses, but how we defined division.
2/2 = 1 because
2/2 = 2 * 2⁻. But, again, zero does not have an inverse, and
2/0 is not
2 * 0⁻. Since
0⁻ does not exist, it is not an inverse of 2, and every nonzero number still has a unique inverse.
If you want to prove that
1/0 = 1 leads to a contradiction, you must explicitly list every step you take and show that none of them assume that
1/0 = 0
We’ve now established Let’s assume for now that if we choose some constant
C, then defining division such that
x/0 = C does not lead to any inconsistencies.4 It turns out that for certain choices of C, specifically 0, we can make some theorems stronger. We can do this by removing the conditions on (some of) our division theorems, and then add a special case to the proof itself. For example:
a * (b/c) = b * (a/c). Proof: we already proved this for
c ≠ 0. Now let
c = 0. Then
a * (b/0) = a * 0 = 0, and
b * (a/0) = b * 0 = 0, and
0 = 0.
Under this definition of division step (3) in the counterargument above is now valid: we can say that
1/0 * 0 = 1 * 0/0. However, in step (4) we say that
0/0 = 1. This theorem does not get stronger:
a/a = 1. Proof: we already proved this for
a ≠ 0. Now let
a = 0. Then
a/a = 0/0 = 0, so
1 = 0and wait shit nevermind
This is why Lean and Isabelle define
1/0 this way. Coq does it too, but as far as I can tell they don’t use it as an optimization.
The Real Mathematicians
A final objection is that I’m a CS person, not a mathmatician, so I don’t understand the math here. But what do the PhDs say?
Lawrence Paulson, professor of computational logic and inventor of Isabelle:
A bit of history: the first logic supported within Isabelle was Martin-Löf’s constructive type theory, and it is still there (CTT). And while developing arithmetic within that formalisation, I came up with a definition (necessarily primitive recursive and executable) of division. It delivered n/0 = 0. Since then a number of people have noticed that defining x/0 = 0 is convenient. This identity holds in quite a few different proof assistants now.
These things are conventions, exactly the same as announcing that x^-n = 1/x^n and that x^0 = 0 [sic].
Leslie Lamport, Math PhD and winner of the 2013 Turing Award:
[In ZF set theory] Since 0 is not in the domain of recip, we know nothing about the value of
1 / 0; it might equal
√2, it might equal R, or it might equal anything else.
looks all fine to me!
I also chatted with a few math graduate student friends and emailed a couple of postdocs I know. So far people nobody’s said that letting
1/0 = 0 is unsound. If any end up getting back to me with that I will include their refutation of this post.
This was originally motivated by how Pony does division. So, is Pony doing the right thing here? No clue. Pony is a programming language, not a formal mathematical system. Consistency is less important than safety, convenience, and context. As a programmer, I don’t like it.5
But is Pony doing something unsound? Absolutely not. It is totally fine to define
1/0 = 0. Nothing breaks and you can’t prove something false. Everybody who was making fun of Pony programmers for being ‘bad at math’ doesn’t actually understand the math behind it.
Don’t make fun of other people. The world is big and we are small.
Update 8 / 12
Some people pointed out I was using some terms ambiguously, which I’ve done my best to fix. A bunch of people also sent in more counterarguments, most of which fell along common themes.
ab = cb => a = cbut with division by zero we have
1 * 0 = 2 * 0 => 1 = 2.
This is a common mistake a lot of people made: assuming that some property of division is somehow part of the definition, not something we have to show is true. But division has no “innate” properties: everything manipulation we make involving it is something we have to prove is a valid manipulation. For any contradiction, you must list out, for every manipulation of the equation you make, the theorem that makes that manipulation valid, and show that the proof of that theorem does not use
1/0 = 0will lead to hidden errors in your program.
As mentioned before, this is not a post about what’s practically a good idea. All I’m arguing is that mathematically, we can extend division in this way without leading to a contradiction. Programming languages are different from mathematical formalisms, and should be different. I prefer that
1/0 is an error, because I’m not using my program to prove theories.
All languages need to make sacrifices of mathematical soundness in favor of safety, practicality, and predictability. It’s the same reason why, in almost every practically-used language,
head has type
[a] -> a, not
[a] -> Maybe a.
We no longer have that division is the inverse of multiplication over its whole domain.
This is a common argument, and arguably is more about the “aesthetics” of division than what’s formalizable. This happens a lot when we extend functions. For example, over the naturals,
a * b is just repeated addition. But if we extend multiplication over the rationals, then we lose that nice definition.
In general, I aesthetically “dislike” definitions and theorems that explicitly include a dependence on a function’s domain, because it leads to “side effects” when we extend them.
0/0 = 0then
lim_(x -> 0) sin(x) / x = sin(0) / 0 = 0, but by L’Hospitals’ Rule
lim_(x -> 0) sin(x) / x = lim_(x -> 0) cos(x) / 1 = 1. So we have
0 = 1.
This was a really clever one. The issue is that the counterargument assumes that if the limit exists and
f(0) is defined, then
lim_(x -> 0) f(x) = f(0). This isn’t always true: take a continuous function and add a point discontinuity. The limit of
sin(x) / x is not
sin(0) / 0, because
sin(x) / x is discontinuous at 0. For the unextended division it’s because
sin(0) / 0 is undefined, while for our extended division it’s a point discontinuity. Funnily enough if we instead picked
x/0 = 1 then
sin(x) / x would be continuous everywhere.
A couple of other people brought up limit arguments, but they all are addressed in a similar way.
- To be clear, I don’t live up to this standard. I try to follow it, but stumble regularly. As part of my tweetstorm I ribbed the original poster at one point and I still feel bad about that. [return]
- An axiom is something we take to be true without proving it. Some example axioms are “0 is a number” and “everything is either equal or unequal.” Different choices of axioms lead to very different mathematical frameworks. Most famously, rejecting the axiom that “every line has exactly one parrallel line that passes through a given point” leads us to non-Euclidian geometries. [return]
- Several people pointed out that there’s a fourth thing you can do: you can say that
1/0is a real number but we don’t know what it is. This is how TLA+ is formalized, though TLC considers
1/0an error. [return]
- A couple of people pointed out we didn’t actually establish this, just showed that some of the common arguments don’t apply. We can prove this to be the case, but the proof gets technical very fast. [return]
- One of developers of Pony reached out to me. They’re planning on writing a more in-depth explanation of why Pony chose
1/0 == 0, which I will linked when available. As I understand it, it’s because Pony forces you to handle all partial functions. Defining
1/0is a “lesser evil” consequence of that. [return]