Skip to content

Commit

Permalink
Override equals and hashCode on Chain (#2690)
Browse files Browse the repository at this point in the history
Resolves #2540.
  • Loading branch information
ceedubs authored and kailuowang committed Jan 9, 2019
1 parent 15dd2f7 commit 6c3b3be
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 2 deletions.
20 changes: 19 additions & 1 deletion core/src/main/scala/cats/data/Chain.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package cats
package data

import Chain._
import cats.kernel.instances.StaticMethods

import scala.annotation.tailrec
import scala.collection.immutable.SortedMap
Expand Down Expand Up @@ -394,7 +395,16 @@ sealed abstract class Chain[+A] {
builder.result
}

def hash[AA >: A](implicit hashA: Hash[AA]): Int = StaticMethods.orderedHash((this: Chain[AA]).iterator)

override def toString: String = show(Show.show[A](_.toString))

override def equals(o: Any): Boolean =
if (o.isInstanceOf[Chain[_]])
(this: Chain[Any]).===(o.asInstanceOf[Chain[Any]])(Eq.fromUniversalEquals[Any])
else false

override def hashCode: Int = hash(Hash.fromUniversalHashCode[A])
}

object Chain extends ChainInstances {
Expand Down Expand Up @@ -646,7 +656,15 @@ sealed abstract private[data] class ChainInstances1 extends ChainInstances2 {
new ChainPartialOrder[A] { implicit def A: PartialOrder[A] = A0 }
}

sealed abstract private[data] class ChainInstances2 {
sealed abstract private[data] class ChainInstances2 extends ChainInstances3 {
implicit def catsDataHashForChain[A](implicit A: Hash[A]): Hash[Chain[A]] = new Hash[Chain[A]] {
def eqv(x: Chain[A], y: Chain[A]): Boolean = x === y

def hash(fa: Chain[A]): Int = fa.hash
}
}

sealed abstract private[data] class ChainInstances3 {
implicit def catsDataEqForChain[A](implicit A: Eq[A]): Eq[Chain[A]] = new Eq[Chain[A]] {
def eqv(x: Chain[A], y: Chain[A]): Boolean = x === y
}
Expand Down
2 changes: 2 additions & 0 deletions testkit/src/main/scala/cats/tests/ListWrapper.scala
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ object ListWrapper {

def eqv[A: Eq]: Eq[ListWrapper[A]] = Eq.by(_.list)

def hash[A: Hash]: Hash[ListWrapper[A]] = Hash.by(_.list)

val traverse: Traverse[ListWrapper] = {
val F = Traverse[List]

Expand Down
34 changes: 33 additions & 1 deletion tests/src/test/scala/cats/tests/ChainSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import cats.laws.discipline.{
TraverseFilterTests,
TraverseTests
}
import cats.kernel.laws.discipline.{EqTests, MonoidTests, OrderTests, PartialOrderTests}
import cats.kernel.laws.discipline.{EqTests, HashTests, MonoidTests, OrderTests, PartialOrderTests}
import cats.laws.discipline.arbitrary._

class ChainSuite extends CatsSuite {
Expand Down Expand Up @@ -48,6 +48,12 @@ class ChainSuite extends CatsSuite {
checkAll("Eq[Chain[ListWrapper[Int]]", SerializableTests.serializable(Eq[Chain[ListWrapper[Int]]]))
}

{
implicit val hash = ListWrapper.hash[Int]
checkAll("Chain[ListWrapper[Int]]", HashTests[Chain[ListWrapper[Int]]].hash)
checkAll("Hash[Chain[ListWrapper[Int]]", SerializableTests.serializable(Hash[Chain[ListWrapper[Int]]]))
}

test("show") {
Show[Chain[Int]].show(Chain(1, 2, 3)) should ===("Chain(1, 2, 3)")
Chain.empty[Int].show should ===("Chain()")
Expand Down Expand Up @@ -186,4 +192,30 @@ class ChainSuite extends CatsSuite {
a.distinct.toList should ===(a.toList.distinct)
}
}

test("=== is consistent with == (issue #2540)") {
(Chain.one(1) |+| Chain.one(2) |+| Chain.one(3)) should be(Chain.fromSeq(List(1, 2, 3)))

forAll { (a: Chain[Int], b: Chain[Int]) =>
(a === b) should ===(a == b)
}
}

test("== returns false for non-Chains") {
forAll { (a: Chain[Int], b: Int) =>
(a == b) should ===(false)
}
}

test("== returns false for Chains of different element types") {
forAll { (a: Chain[Option[String]], b: Chain[String]) =>
(a == b) should ===(a.isEmpty && b.isEmpty)
}
}

test("Chain#hashCode is consistent with List#hashCode") {
forAll { (x: Chain[Int]) =>
x.hashCode should ===(x.toList.hashCode)
}
}
}

0 comments on commit 6c3b3be

Please sign in to comment.