You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
1. Don't apply the lifting rules when strictEquality is set.
2. Be more strict about null checking
3. Compensate for (2) by changing errorTerm in parser so that
it has an error type instead of Null.
4. Reorganize implementation to match new docs.
5. Handle Eql instances involving primitive types or Null directly
in the compiler (previously, we were missing some of them, see
pos/multivarsal.scala as a test)
6. Sharpen Eql instance of Proxy to only work for AnyRef arguments.
This avoids false negatives when comparing value types with null.
@@ -9,112 +9,150 @@ the fact that `==` and `!=` are implemented in terms of Java's
9
9
`equals` method, which can also compare values of any two reference
10
10
types.
11
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`.
12
+
Universal equality is convenient. But it is also dangerous since it
13
+
undermines type safety. For instance, let's assume one is left after some refactoring
14
+
with an erroneous program where a value `y` has type `S` instead of the correct type `T`.
15
15
16
16
```scala
17
17
valx= ... // of type T
18
18
valy= ... // of type S, but should be T
19
19
x == y // typechecks, will always yield false
20
20
```
21
21
22
-
If all you do with `y` is compare it to other values of type `T`, the program will
23
-
typecheck but probably give unexpected results.
22
+
If all the program does with `y` is compare it to other values of type `T`, the program will still typecheck, since values of all types can be compared with each other.
23
+
But it will probably give unexpected results and fail at runtime.
24
24
25
25
Multiversal equality is an opt-in way to make universal equality
26
-
safer. The idea is that by declaring an `implicit` value one can
27
-
restrict the types that are legal in comparisons. The example above
28
-
would not typecheck if an implicit was declared like this for type `T`
29
-
(or an analogous one for type `S`):
30
-
26
+
safer. It uses a binary typeclass `Eql` to indicate that values of
27
+
two given types can be compared with each other.
28
+
The example above would not typecheck if `S` or `T` was a class
29
+
that derives `Eql`, e.g.
31
30
```scala
32
-
implicitdefeqT:Eq[T, T] =Eq.derived
31
+
classTderivesEql
33
32
```
34
-
35
-
This definition effectively says that value of type `T` can (only) be
36
-
compared with `==` or `!=` to other values of type `T`. The definition
37
-
is used only for type checking; it has no significance for runtime
33
+
Alternatively, one can also provide the derived implied instance directly, like this:
34
+
```scala
35
+
implied forEql[T, T] =Eql.derived
36
+
```
37
+
This definition effectively says that values of type `T` can (only) be
38
+
compared to other values of type `T` when using `==` or `!=`. The definition
39
+
affects type checking but it has no significance for runtime
38
40
behavior, since `==` always maps to `equals` and `!=` always maps to
39
-
the negation of `equals`. The right hand side of the definition is a value
40
-
that has any `Eq` instance as its type. Here is the definition of class
41
-
`Eq` and its companion object:
42
-
41
+
the negation of `equals`. The right hand side `Eql.derived` of the definition
42
+
is a value that has any `Eql` instance as its type. Here is the definition of class
43
+
`Eql` and its companion object:
43
44
```scala
44
45
packagescala
45
46
importannotation.implicitNotFound
46
47
47
48
@implicitNotFound("Values of types ${L} and ${R} cannot be compared with == or !=")
48
-
sealedtraitEq[-L, -R]
49
+
sealedtraitEql[-L, -R]
49
50
50
-
objectEqextendsEq[Any, Any]
51
+
objectEql {
52
+
objectderivedextendsEql[Any, Any]
53
+
}
51
54
```
52
55
53
-
One can have several `Eq` instances for a type. For example, the four
56
+
One can have several `Eql` instances for a type. For example, the four
54
57
definitions below make values of type `A` and type `B` comparable with
55
58
each other, but not comparable to anything else:
56
59
57
60
```scala
58
-
implicitdefeqA:Eq[A, A] =Eq.derived
59
-
implicitdefeqB:Eq[B, B] =Eq.derived
60
-
implicitdefeqAB:Eq[A, B] =Eq.derived
61
-
implicitdefeqBA:Eq[B, A] =Eq.derived
61
+
implied forEql[A, A] =Eql.derived
62
+
implied forEql[B, B] =Eql.derived
63
+
implied forEql[A, B] =Eql.derived
64
+
implied forEql[B, A] =Eql.derived
62
65
```
66
+
The `scala.Eql` object defines a number of `Eql` instances that together
67
+
define a rule book for what standard types can be compared (more details below).
63
68
64
-
(As usual, the names of the implicit definitions don't matter, we have
65
-
chosen `eqA`, ..., `eqBA` only for illustration).
66
-
67
-
The `scala.Eq` object defines a number of `Eq` implicits that make
68
-
values of types `String`, `Boolean` and `Unit` only comparable to
69
-
values of the same type. They also make numbers only comparable to
70
-
other numbers, sequences only comparable to other
71
-
sequences and sets only comparable to other sets.
72
-
73
-
There's also a "fallback" instance named `eqAny` that allows comparisons
74
-
over all types that do not themselves have an `Eq` instance. `eqAny` is
69
+
There's also a "fallback" instance named `eqlAny` that allows comparisons
70
+
over all types that do not themselves have an `Eql` instance. `eqlAny` is
75
71
defined as follows:
76
72
77
73
```scala
78
-
defeqAny[L, R]:Eq[L, R] =Eq.derived
74
+
defeqlAny[L, R]:Eql[L, R] =Eql.derived
79
75
```
80
76
81
-
Even though `eqAny` is not declared implicit, the compiler will still
82
-
construct an `eqAny` instance as answer to an implicit search for the
83
-
type `Eq[L, R]`, provided that neither `L`nor`R` have `Eq` instances
84
-
defined on them.
77
+
Even though `eqlAny` is not declared `implied`, the compiler will still
78
+
construct an `eqlAny` instance as answer to an implicit search for the
79
+
type `Eql[L, R]`, unless `L`or`R` have `Eql` instances
80
+
defined on them, or the language feature `strictEquality` is enabled
85
81
86
-
The primary motivation for having `eqAny` is backwards compatibility,
87
-
if this is of no concern one can disable `eqAny` by enabling the language
82
+
The primary motivation for having `eqlAny` is backwards compatibility,
83
+
if this is of no concern one can disable `eqlAny` by enabling the language
88
84
feature `strictEquality`. As for all language features this can be either
89
85
done with an import
90
86
91
87
```scala
92
88
importscala.language.strictEquality
93
89
```
94
-
95
90
or with a command line option `-language:strictEquality`.
96
91
97
-
All `enum` types also come with `Eq` instances that make values of the
98
-
`enum` type comparable only to other values of that `enum` type.
92
+
## Deriving Eql Instances
93
+
94
+
Instead of defining `Eql` instances directly, it is often more convenient to derive them. Example:
95
+
```scala
96
+
classBox[T](x: T) derivesEql
97
+
```
98
+
By the usual rules if [typeclass derivation](./derivation.html),
99
+
this generates the following `Eql` instance in the companion object of `Box`:
0 commit comments