iTranslated by AI
Why '===' Differs: A Look at Language Design Through Equivalence and Identity
Introduction: The Uncanny Feeling of Talking Past Each Other Despite Using the Same Symbol
The === operator in JavaScript and the === operator in Kotlin look identical.
However, their meanings are completely different.
- In JavaScript,
===is a "strict equality comparison." - In Kotlin,
===is a "referential identity comparison."
Many developers have likely felt that this difference is "confusing" or "hard to remember."
But this is neither a coincidence nor a design flaw.
Rather, this honestly reflects the differences in design philosophy regarding:
- What the language considers safe.
- At which layer the language attempts to prevent misunderstandings.
In this article, using the symbol === as our entry point, we will organize the two concepts of:
- Equivalence
- Identity
and structurally examine "why the meaning of comparison operators diverged across different languages."
Equivalence and Identity Are Different Questions Altogether
First, let's separate two questions that often get mixed up when talking about comparisons.
Equivalence: Are they the same in meaning?
Equivalence is the question: "Are they the same as values?"
- The numbers
1and1 - The strings
"hello"and"hello" - Two data structures with the same content
These are considered equivalent if their meaning is the same, even if they are different entities.
Equivalence = Are they the same in "what they represent"?
This is the protagonist in most business logic and domain designs.
Identity: Are they the same entity?
On the other hand, identity is the question: "Are they really the same 'thing'?"
- Are they the same object in memory?
- Are they the same instance?
- Do they share the same reference?
Identity = "Is it the same existence itself?"
This becomes critical in contexts like caching, mutable state, and lifecycle management.
What is important here is that:
Equivalence and identity are not about which is superior.
They simply have different roles and are needed in different situations.
=== in JavaScript: Eliminating "Discrepancies in Meaning" First
When discussing JavaScript comparison operators, == inevitably takes the heat.
0 == false // true
"" == false // true
null == undefined // true
While "flexible," this poses the problem of implicitly jumping between layers of comparison.
The Boundary Introduced by ===
To address this, JavaScript introduced === as a comparison that does not perform type coercion.
0 === false // false
What === is trying to protect here is:
- Not equivalence itself,
- But rather "consistency of interpretation that is not intended."
In JavaScript, === is an operator that guarantees:
"Can I treat these two as having the same 'meaning'?"
while preventing the language side from injecting any extra interpretation.
In other words, === in JavaScript can be said to be:
- A form of equivalence comparison,
- But an "equivalence that prohibits implicit correction."
=== in Kotlin: Forcing a Separation Between Equivalence and Identity
On the other hand, in Kotlin, the comparison operators are separated from the start.
a == b // Equivalence comparison via equals
a === b // Referential identity comparison
The point here is a strong will to:
Not allow equivalence and identity to be mixed at the symbol level.
Why did Kotlin separate them?
Kotlin (and Java-based languages) are designed with the following as prerequisites:
- Objects
- Mutable state
- Explicit lifecycles
In this world, confusing "same as a value" with "same instance" allows bugs to creep in silently.
Therefore, Kotlin adopted a design where it forces developers to choose every time:
-
==→ "Are they the same in meaning?" -
===→ "Are they the same entity?"
This design is less about being helpful and more about coercion to prevent mistakes.
Different Meanings for the Same Symbol Arise from Different Priorities in Protection
Looking at this so far, the === in JavaScript and the === in Kotlin are trying to solve entirely different problems.
| Language | What === protects |
|---|---|
| JavaScript | Misunderstandings caused by implicit type coercion |
| Kotlin | Confusion between equivalence and identity |
Even when using the same symbol, it is natural for the meanings to diverge if the following differ:
- The anticipated failures
- The historical background
- The types of bugs prevalent in their respective battlefields
A Brief Peek at Other Languages Reveals the Axes of Design
-
Java
-
==: Reference comparison -
equals(): Equivalence comparison
-
-
Swift
-
==: Equivalence comparison via Equatable -
===: Reference identity for classes
-
-
Python
-
==: Equivalence -
is: Identity
-
Every language eventually arrives at the same two questions. What differs are the design priorities regarding:
- Which to make the default
- Where to require explicitness
- Where to prevent accidents
Conclusion: Comparison Operators Are a Gateway to Philosophy
The difference in === is not a matter to be settled by memorizing symbols.
It is packed with a philosophy regarding:
- What kinds of mistakes this language has observed most frequently
- What kind of judgment it wants to entrust to developers
- To what extent the language itself should take responsibility
Discussion