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
Even though `eqlAny` is not declared as `given`, the compiler will still construct an `eqlAny` instance as answer to an implicit search for the
78
-
type `Eql[L, R]`, unless `L` or `R` have `Eql` instances
77
+
Even though `canEqualAny` is not declared as `given`, the compiler will still construct an `canEqualAny` instance as answer to an implicit search for the
78
+
type `CanEqual[L, R]`, unless `L` or `R` have `CanEqual` instances
79
79
defined on them, or the language feature `strictEquality` is enabled.
80
80
81
-
The primary motivation for having `eqlAny` is backwards compatibility.
82
-
If this is of no concern, one can disable `eqlAny` by enabling the language
81
+
The primary motivation for having `canEqualAny` is backwards compatibility.
82
+
If this is of no concern, one can disable `canEqualAny` by enabling the language
83
83
feature `strictEquality`. As for all language features this can be either
or with a command line option `-language:strictEquality`.
90
90
91
-
## Deriving Eql Instances
91
+
## Deriving CanEqual Instances
92
92
93
-
Instead of defining `Eql` instances directly, it is often more convenient to derive them. Example:
93
+
Instead of defining `CanEqual` instances directly, it is often more convenient to derive them. Example:
94
94
```scala
95
-
classBox[T](x: T) derivesEql
95
+
classBox[T](x: T) derivesCanEqual
96
96
```
97
97
By the usual rules of [type class derivation](./derivation.md),
98
-
this generates the following `Eql` instance in the companion object of `Box`:
98
+
this generates the following `CanEqual` instance in the companion object of `Box`:
99
99
```scala
100
-
given [T, U](usingEql[T, U]) as Eql[Box[T], Box[U]] =Eql.derived
100
+
given [T, U](usingCanEqual[T, U]) as CanEqual[Box[T], Box[U]] =CanEqual.derived
101
101
```
102
102
That is, two boxes are comparable with `==` or `!=` if their elements are. Examples:
103
103
```scala
104
-
newBox(1) ==newBox(1L) // ok since there is an instance for `Eql[Int, Long]`
104
+
newBox(1) ==newBox(1L) // ok since there is an instance for `CanEqual[Int, Long]`
105
105
newBox(1) ==newBox("a") // error: can't compare
106
106
newBox(1) ==1// error: can't compare
107
107
```
@@ -112,31 +112,31 @@ The precise rules for equality checking are as follows.
112
112
113
113
If the `strictEquality` feature is enabled then
114
114
a comparison using `x == y` or `x != y` between values `x: T` and `y: U`
115
-
is legal if there is a `given` of type `Eql[T, U]`.
115
+
is legal if there is a `given` of type `CanEqual[T, U]`.
116
116
117
117
In the default case where the `strictEquality` feature is not enabled the comparison is
118
118
also legal if
119
119
120
120
1.`T` and `U` are the same, or
121
121
2. one of `T`, `U` is a subtype of the _lifted_ version of the other type, or
122
-
3. neither `T` nor `U` have a _reflexive_`Eql` instance.
122
+
3. neither `T` nor `U` have a _reflexive_`CanEqual` instance.
123
123
124
124
Explanations:
125
125
126
126
-_lifting_ a type `S` means replacing all references to abstract types
127
127
in covariant positions of `S` by their upper bound, and replacing
128
128
all refinement types in covariant positions of `S` by their parent.
129
-
- a type `T` has a _reflexive_`Eql` instance if the implicit search for `Eql[T, T]`
129
+
- a type `T` has a _reflexive_`CanEqual` instance if the implicit search for `CanEqual[T, T]`
130
130
succeeds.
131
131
132
-
## Predefined Eql Instances
132
+
## Predefined CanEqual Instances
133
133
134
-
The `Eql` object defines instances for comparing
134
+
The `CanEqual` object defines instances for comparing
135
135
- the primitive types `Byte`, `Short`, `Char`, `Int`, `Long`, `Float`, `Double`, `Boolean`, and `Unit`,
136
136
-`java.lang.Number`, `java.lang.Boolean`, and `java.lang.Character`,
137
137
-`scala.collection.Seq`, and `scala.collection.Set`.
138
138
139
-
Instances are defined so that every one of these types has a _reflexive_`Eql` instance, and the following holds:
139
+
Instances are defined so that every one of these types has a _reflexive_`CanEqual` instance, and the following holds:
140
140
141
141
- Primitive numeric types can be compared with each other.
142
142
- Primitive numeric types can be compared with subtypes of `java.lang.Number` (and _vice versa_).
@@ -152,7 +152,7 @@ Instances are defined so that every one of these types has a _reflexive_ `Eql` i
152
152
153
153
## Why Two Type Parameters?
154
154
155
-
One particular feature of the `Eql` type is that it takes _two_ type parameters, representing the types of the two items to be compared. By contrast, conventional
155
+
One particular feature of the `CanEqual` type is that it takes _two_ type parameters, representing the types of the two items to be compared. By contrast, conventional
156
156
implementations of an equality type class take only a single type parameter which represents the common type of _both_ operands.
157
157
One type parameter is simpler than two, so why go through the additional complication? The reason has to do with the fact that, rather than coming up with a type class where no operation existed before,
158
158
we are dealing with a refinement of pre-existing, universal equality. It is best illustrated through an example.
@@ -174,39 +174,39 @@ does not work, since it refers to the covariant parameter `T` in a nonvariant co
174
174
```
175
175
This generic version of `contains` is the one used in the current (Scala 2.13) version of `List`.
176
176
It looks different but it admits exactly the same applications as the `contains(x: Any)` definition we started with.
177
-
However, we can make it more useful (i.e. restrictive) by adding an `Eql` parameter:
177
+
However, we can make it more useful (i.e. restrictive) by adding a `CanEqual` parameter:
This version of `contains` is equality-safe! More precisely, given
182
182
`x: T`, `xs: List[T]` and `y: U`, then `xs.contains(y)` is type-correct if and only if
183
183
`x == y` is type-correct.
184
184
185
-
Unfortunately, the crucial ability to "lift" equality type checking from simple equality and pattern matching to arbitrary user-defined operations gets lost if we restrict ourselves to an equality class with a single type parameter. Consider the following signature of `contains` with a hypothetical `Eql1[T]` type class:
185
+
Unfortunately, the crucial ability to "lift" equality type checking from simple equality and pattern matching to arbitrary user-defined operations gets lost if we restrict ourselves to an equality class with a single type parameter. Consider the following signature of `contains` with a hypothetical `CanEqual1[T]` type class:
This version could be applied just as widely as the original `contains(x: Any)` method,
190
-
since the `Eql1[Any]` fallback is always available! So we have gained nothing. What got lost in the transition to a single parameter type class was the original rule that `Eql[A, B]` is available only if neither `A` nor `B` have a reflexive `Eql` instance. That rule simply cannot be expressed if there is a single type parameter for `Eql`.
190
+
since the `CanEqual1[Any]` fallback is always available! So we have gained nothing. What got lost in the transition to a single parameter type class was the original rule that `CanEqual[A, B]` is available only if neither `A` nor `B` have a reflexive `CanEqual` instance. That rule simply cannot be expressed if there is a single type parameter for `CanEqual`.
191
191
192
192
The situation is different under `-language:strictEquality`. In that case,
193
-
the `Eql[Any, Any]` or `Eql1[Any]` instances would never be available, and the
193
+
the `CanEqual[Any, Any]` or `CanEqual1[Any]` instances would never be available, and the
194
194
single and two-parameter versions would indeed coincide for most practical purposes.
195
195
196
-
But assuming `-language:strictEquality` immediately and everywhere poses migration problems which might well be unsurmountable. Consider again `contains`, which is in the standard library. Parameterizing it with the `Eql` type class as in (1) is an immediate win since it rules out non-sensical applications while still allowing all sensible ones.
196
+
But assuming `-language:strictEquality` immediately and everywhere poses migration problems which might well be unsurmountable. Consider again `contains`, which is in the standard library. Parameterizing it with the `CanEqual` type class as in (1) is an immediate win since it rules out non-sensical applications while still allowing all sensible ones.
197
197
So it can be done almost at any time, modulo binary compatibility concerns.
198
-
On the other hand, parameterizing `contains` with `Eql1` as in (2) would make `contains`
199
-
unusable for all types that have not yet declared an `Eql1` instance, including all
198
+
On the other hand, parameterizing `contains` with `CanEqual1` as in (2) would make `contains`
199
+
unusable for all types that have not yet declared a `CanEqual1` instance, including all
200
200
types coming from Java. This is clearly unacceptable. It would lead to a situation where,
201
-
rather than migrating existing libraries to use safe equality, the only upgrade path is to have parallel libraries, with the new version only catering to types deriving `Eql1` and the old version dealing with everything else. Such a split of the ecosystem would be very problematic, which means the cure is likely to be worse than the disease.
201
+
rather than migrating existing libraries to use safe equality, the only upgrade path is to have parallel libraries, with the new version only catering to types deriving `CanEqual1` and the old version dealing with everything else. Such a split of the ecosystem would be very problematic, which means the cure is likely to be worse than the disease.
202
202
203
203
For these reasons, it looks like a two-parameter type class is the only way forward because it can take the existing ecosystem where it is and migrate it towards a future where more and more code uses safe equality.
204
204
205
205
In applications where `-language:strictEquality` is the default one could also introduce a one-parameter type alias such as
206
206
```scala
207
-
typeEq[-T] =Eql[T, T]
207
+
typeEq[-T] =CanEqual[T, T]
208
208
```
209
-
Operations needing safe equality could then use this alias instead of the two-parameter `Eql` class. But it would only
209
+
Operations needing safe equality could then use this alias instead of the two-parameter `CanEqual` class. But it would only
210
210
work under `-language:strictEquality`, since otherwise the universal `Eq[Any]` instance would be available everywhere.
0 commit comments