Skip to content

Commit d6f4088

Browse files
committed
Section on multiversal equality
1 parent 70b2f38 commit d6f4088

File tree

2 files changed

+99
-0
lines changed

2 files changed

+99
-0
lines changed
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
---
2+
layout: doc-page
3+
title: "Multiversal Equality"
4+
---
5+
6+
Previously, Scala had universal equality: Two values of any types
7+
could be compared with each other with `==` and `!=`. This came from
8+
the fact that `==` and `!=` are implemented in terms of Java's
9+
`equals` method, which can also compare values of any two reference
10+
types.
11+
12+
Universal equality is convenient but also dangerous since it
13+
undermines type safety. Say you have an erroneous program where
14+
a value `y` has type `S` instead of the expected type `T`.
15+
16+
val x = ... // of type T
17+
val y = ... // of type S, but should be T
18+
x == y // typechecks, will always yield false
19+
20+
If all you do with `y` is compare it to other values of type `T`, the program will
21+
typecheck but probably give unexpected results.
22+
23+
Multiversal equaliy is an opt-in way to make universal equality
24+
safer. The idea is that by declaring an `implicit` value one can
25+
restrict the types that are legal in comparisons. The example above
26+
would not typecheck if an implicit was declared like this for type `T`
27+
(or an analogous one for type `S`):
28+
29+
implicit def eqT: Eq[T, T] = Eq
30+
31+
This definition effectively says that value of type `T` can (only) be
32+
compared with `==` or `!=` to other values of type `T`. The definition
33+
is used only for type checking; it has no significance for runtime
34+
behavior, since `==` always maps to `equals` and `!=` alwatys maps to
35+
the negation of `equals`. The right hand side of the definition is a value
36+
that has any `Eq` instance as its type. Here is the definition of class
37+
`Eq` and its companion object:
38+
39+
package scala
40+
import annotation.implicitNotFound
41+
42+
@implicitNotFound("Values of types ${L} and ${R} cannot be compared with == or !=")
43+
sealed trait Eq[-L, -R]
44+
45+
object Eq extends Eq[Any, Any]
46+
47+
One can have several `Eq` instances for a type. For example, the four
48+
definitions below make values of type `A` and type `B` comparable with
49+
each other, but not comparable to anything else:
50+
51+
implicit def eqA : Eq[A, A] = Eq
52+
implicit def eqB : Eq[B, B] = Eq
53+
implicit def eqAB: Eq[A, B] = Eq
54+
implicit def eqBA: Eq[B, A] = Eq
55+
56+
(As usual, the names of the implicit definitions don't matter, we have
57+
chosen `eqA`, ..., `eqBA` only for illustration).
58+
59+
The `dotty.DottyPredef` object defines a number of `Eq`
60+
implicits. `dotty.DottyPredef` is a temporary `Predef`-like object.
61+
The contents of this object are by default imported into every
62+
program. Once dotty becomes standard Scala, `DottyPredef` will go away
63+
and its contents will be merged with `scala.Predef`.
64+
65+
The `Eq` instances defined by `DottyPredef` make values of types
66+
`String`, `Boolean` and `Unit` only comparable to values of the same
67+
type. They also make numbers only comparable to other numbers, and
68+
sequences only comparable to other sequences. There's also a
69+
"fallback" instance `eqAny` that allows comparisons over types that do
70+
not themeselves have an `Eq` instance. `eqAny` is defined as follows:
71+
72+
implicit def eqAny[L, R]: Eq[L, R] = Eq
73+
74+
The primary motivation for having `eqAny` is backwards compatibility,
75+
if this is of no concern one can disable `eqAny` by unimporting it
76+
from `DottyPredef` like this
77+
78+
import dotty.DottyPredef.{eqAny => _, _}
79+
80+
All `enum` types also come with `Eq` instances that make values of the
81+
`enum` type comparable only to other values of that `enum` type.
82+
83+
The precise rules for equality checking are as follows.
84+
85+
1. A comparison using `x == y` or `x != y` between values `x: T` and `y: U`
86+
is legal if either `T` and `U` are the same, or one of the types is a subtype
87+
of the other, or an implicit value of type `scala.Eq[T, U]` is found.
88+
89+
2. The usual rules for implicit search apply also to `Eq` instances,
90+
with one modification: The value `eqAny` in `dotty.DottyPredef` is
91+
eligible only if neither `T` nor `U` have a reflexive `Eq`
92+
instance themselves. Here, a type `T` has a reflexive `Eq`
93+
instance if the implicit search for `Eq[T, T]` where `eqAny` is
94+
not eligible is successful.
95+
96+
More on multiversal equality is found in a [blog post]
97+
and a [Github issue].

docs/sidebar.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ sidebar:
1515
url: docs/reference/adts.html
1616
- title: Enum Translation
1717
url: docs/reference/desugarEnums.html
18+
-title: Multiversal Equality
19+
url: docs/reference/multiversal-equality.html
1820
- title: By-Name Implicits
1921
url: docs/reference/implicit-by-name-parameters.html
2022
- title: Usage

0 commit comments

Comments
 (0)