Skip to content

Commit 4fb0a61

Browse files
authored
Merge pull request #12419 from Kevin-Lee/add-CanEqual-typeclass-instances-for-Option-Either-Tuple
2 parents ba1308a + 3e963c0 commit 4fb0a61

File tree

3 files changed

+128
-2
lines changed

3 files changed

+128
-2
lines changed

library/src/scala/CanEqual.scala

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,16 @@ object CanEqual {
2626
given canEqualNumber: CanEqual[Number, Number] = derived
2727
given canEqualString: CanEqual[String, String] = derived
2828

29-
// The next three definitions can go into the companion objects of classes
30-
// Seq and Set. For now they are here in order not to have to touch the
29+
// The next five definitions can go into the companion objects of their corresponding
30+
// classes. For now they are here in order not to have to touch the
3131
// source code of these classes
3232
given canEqualSeq[T, U](using eq: CanEqual[T, U]): CanEqual[Seq[T], Seq[U]] = derived
3333
given canEqualSet[T, U](using eq: CanEqual[T, U]): CanEqual[Set[T], Set[U]] = derived
34+
35+
given canEqualOptions[T, U](using eq: CanEqual[T, U]): CanEqual[Option[T], Option[U]] = derived
36+
given canEqualOption[T](using eq: CanEqual[T, T]): CanEqual[Option[T], Option[T]] = derived // for `case None` in pattern matching
37+
38+
given canEqualEither[L1, R1, L2, R2](
39+
using eqL: CanEqual[L1, L2], eqR: CanEqual[R1, R2]
40+
): CanEqual[Either[L1, R1], Either[L2, R2]] = derived
3441
}

library/src/scala/Tuple.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,11 @@ object Tuple {
232232

233233
def fromProductTyped[P <: Product](p: P)(using m: scala.deriving.Mirror.ProductOf[P]): m.MirroredElemTypes =
234234
runtime.Tuples.fromProduct(p).asInstanceOf[m.MirroredElemTypes]
235+
236+
given canEqualEmptyTuple: CanEqual[EmptyTuple, EmptyTuple] = CanEqual.derived
237+
given canEqualTuple[H1, T1 <: Tuple, H2, T2 <: Tuple](
238+
using eqHead: CanEqual[H1, H2], eqTail: CanEqual[T1, T2]
239+
): CanEqual[H1 *: T1, H2 *: T2] = CanEqual.derived
235240
}
236241

237242
/** A tuple of 0 elements */

tests/neg/equality1.scala

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,118 @@ object equality1 {
33
class A
44
class B
55
new A == new B // error: cannot compare
6+
7+
case class Foo(n: Int) derives CanEqual
8+
9+
sealed trait Status derives CanEqual
10+
object Status {
11+
case class Active(since: Int) extends Status
12+
case object Pending extends Status
13+
case object Inactive extends Status
14+
}
15+
16+
enum Color derives CanEqual {
17+
case Red
18+
case Green
19+
case Blue
20+
}
21+
22+
val option1a: Option[Int] = Some(1)
23+
val option1b: Option[Int] = Some(1)
24+
option1a == option1b
25+
26+
option1a match {
27+
case Some(1) =>
28+
println("1")
29+
case Some(n) =>
30+
println("Not 1")
31+
case None => // This None case doesn't work without CanEqual.canEqualOption[T]
32+
println("None")
33+
}
34+
35+
1 == '1'
36+
val option2a: Option[Int] = Some(1)
37+
val option2b: Option[Char] = Some('1')
38+
option2a == option2b
39+
40+
val option3a: Option[Foo] = Some(Foo(1))
41+
val option3b: Option[Foo] = Some(Foo(1))
42+
option3a == option3b
43+
44+
val option4a: Option[Status] = Some(Status.Active(2020))
45+
val option4b: Option[Status] = Some(Status.Pending)
46+
val option4c: Option[Status] = Some(Status.Inactive)
47+
option4a == option4b
48+
option4b == option4c
49+
50+
val option5a: Option[Color] = Some(Color.Red)
51+
val option5b: Option[Color] = Some(Color.Green)
52+
val option5c: Option[Color] = Some(Color.Blue)
53+
option5a == option5b
54+
option5b == option5c
55+
56+
val optionError1a: Option[Int] = Some(1)
57+
val optionError1b: Option[String] = Some("1")
58+
optionError1a == optionError1b // error: cannot compare
59+
60+
val optionError2a: Option[Char] = Some('a')
61+
val optionError2b: Option[String] = Some("a")
62+
optionError2a == optionError2b // error: cannot compare
63+
64+
val optionTuple1a: Option[(Int, String)] = Some((1, "OK"))
65+
val optionTuple1b: Option[(Int, String)] = Some((1, "OK"))
66+
optionTuple1a == optionTuple1b
67+
68+
'a' == 97
69+
val optionTuple2a: Option[(Int, Char)] = Some((1, 'a'))
70+
val optionTuple2b: Option[(Int, Int)] = Some((1, 97))
71+
optionTuple2a == optionTuple2b
72+
73+
val optionTupleError1a: Option[(Int, String)] = Some((1, "OK"))
74+
val optionTupleError1b: Option[(String, Int)] = Some(("OK", 1))
75+
optionTupleError1a == optionTupleError1b // error: cannot compare
76+
77+
val eitherL1a: Either[String, Int] = Left("Error")
78+
val eitherL1b: Either[String, Int] = Left("Error")
79+
eitherL1a == eitherL1b
80+
81+
val eitherR1a: Either[String, Int] = Right(999)
82+
val eitherR1b: Either[String, Int] = Right(999)
83+
eitherR1a == eitherR1b
84+
85+
val eitherErrorL1a: Either[String, Int] = Left("Error")
86+
val eitherErrorL1b: Either[Char, Int] = Left('E')
87+
eitherErrorL1a == eitherErrorL1b // error: cannot compare
88+
89+
val eitherErrorR1a: Either[String, Int] = Right(999)
90+
val eitherErrorR1b: Either[String, String] = Right("999")
91+
eitherErrorR1a == eitherErrorR1b // error: cannot compare
92+
93+
94+
val eitherTupleL1a: Either[(String, Long), (Int, Boolean)] = Left(("Error", 123L))
95+
val eitherTupleL1b: Either[(String, Long), (Int, Boolean)] = Left(("Error", 123L))
96+
eitherTupleL1a == eitherTupleL1b
97+
98+
val eitherTupleR1a: Either[(String, Long), (Int, Boolean)] = Right((999, true))
99+
val eitherTupleR1b: Either[(String, Long), (Int, Boolean)] = Right((999, true))
100+
eitherTupleR1a == eitherTupleR1b
101+
102+
val eitherTupleErrorL1a: Either[(String, Long), (Int, Boolean)] = Left(("Error", 123L))
103+
val eitherTupleErrorL1b: Either[(Long, String), (Int, Boolean)] = Left((123L, "Error"))
104+
eitherTupleErrorL1a == eitherTupleErrorL1b // error: cannot compare
105+
106+
val eitherTupleErrorR1a: Either[(String, Long), (Int, Boolean)] = Right((999, true))
107+
val eitherTupleErrorR1b: Either[(String, Long), (Boolean, Int)] = Right((true, 999))
108+
eitherTupleErrorR1a == eitherTupleErrorR1b // error: cannot compare
109+
110+
(1, "a") == (1, "a")
111+
(1, "a", true) == (1, "a", true)
112+
(1, "a", true, 't') == (1, "a", true, 't')
113+
(1, "a", true, 't', 10L) == (1, "a", true, 't', 10L)
114+
115+
(1, "a") == (1, 'a') // error: cannot compare
116+
(1, "a") == ("a", 1) // error: cannot compare
117+
(1, "a") == (1, "a", true) // error: cannot compare
118+
(1, "a", true, 't', 10L) == (1, "a", 1.5D, 't', 10L) // error: cannot compare
119+
6120
}

0 commit comments

Comments
 (0)