4.3 KiB
title | date | tags | draft | |
---|---|---|---|---|
Equality | 2023-09-15T05:36:53.757Z |
|
true |
When learning type theory, it's important to make a distinction between several kinds of "same"-ness. Whenever you talk about two things being equal, you'd want to qualify it with one of these in order to make it clear which you're referring to, since they're very different concepts.
-
Definitional, or judgmental equality. This is usually written with
\equiv
in math. If you seex \equiv y
, this says "I've definedx
and $y$ to be the same, so anytime you seex
, you can replace it withy
and vice versa."[!NOTE] Technically speaking, definitional and judgmental are two separate concepts but coincide in many type theories. You could think of multiple levels of equalities like this:
- Does
2
equal2
? Yes. This is the same object, and equality is reflexive. - Does
s(s(z))
equal2
? Well, typically this is how natural numbers are defined in type systems:2 \equiv s(s(z))
. By definition they are identical. - Does
1 + 1
equal2
? The operation+
is a function, and requires a β-reduction to evaluate any further. This choice really comes down to the choice of the type theory designer; if you wanted a more intensional type theory, you could choose to not allow β-reduction during evaluation of identities. Most type theories do allow this though.
- Does
-
Propositional equality. This describes a value that potentially holds true or false. This is typically written
=
in math.
Helpful tips
-
You can talk about propositional equalities conditionally, like "if
a
equalsb
, thenc
is true". This kind of expression doesn't really make sense for judgmental equality, like in the following example:Suppose you made the judgment:
x = 2
. It makes no sense to then say "if $x$ equals2
, thenc
is true." You can just skip right toc
is true, because you've defined it to be true, so it's useless even thinking about the case where it's false because that would be a part of a completely different world entirely. -
Here's a concrete example of the difference between judgmental and propositional equality. Consider these 2 expressions:
2 + 3 = 3 + 2
\forall x\ y \in \mathbb{N} . (x + y = y + x)
In the first case, performing β-reduction on both terms reduces to $5 = 5$, which is definitionally equal by virtue of being the exact same symbol.
In the second case however, β-reduction is impossible. Without a specific
x
ory
, there's actually no way to reduce either side more. You would need some other constructs to prove it, such as induction. -
Suppose you were expressing these concepts in a language like TypeScript. The analog of a judgment would be something like:
type Foo = string;
Any time you're using
Foo
, you really don't have to compare it withstring
, because just through a simple substitution you will get the same symbol:function sayHello(who: string) { console.log(`Hello, ${who}`); } function run(foo: Foo) { sayHello(foo); }
On the other hand, a propositional equality type might look something like this instead:
class Equal<A, B> { private constructor() {} static refl<A>(): Equal<A, A> {} }
You could have a value of this type, and query on its truthiness:
type TwoEqualsTwo = Equal<Two, 2>; function trans<A, B, C>(x: Equal<A, B>, y: Equal<B, C>): Equal<A, C> { // magic... }
Notation gotcha
The syntax we use in most math papers and other literature and the syntax we use for equalities in various dependently-typed programming languages are annoyingly different:
Math | Agda | Coq | Idris | |
---|---|---|---|---|
Judgmental | x \equiv y |
x = y |
x := y |
x = y |
Propositional | x = y |
x ≡ y |
x = y |
x = y |
Usually there'll be some other pre-fix way of writing the propositional equals
in your language too. In Agda, it's Path {A} x y
.