From 4ffd3a1b8e8d9cee5311828fd534a2ea5af7bf40 Mon Sep 17 00:00:00 2001 From: Tongfei Chen Date: Fri, 6 Oct 2017 07:40:03 -0400 Subject: [PATCH] more instances for Hash (#1712): Queue/Duration (#1950) * more instances to Hash: Queue/Duration * more instances to Hash: Queue/Duration --- .../scala/cats/kernel/laws/LawTests.scala | 5 ++++ .../cats/kernel/instances/StaticMethods.scala | 30 +++++++++++++++++++ .../cats/kernel/instances/duration.scala | 6 ++-- .../scala/cats/kernel/instances/list.scala | 17 +---------- .../scala/cats/kernel/instances/queue.scala | 7 +++++ .../scala/cats/kernel/instances/stream.scala | 12 +------- .../scala/cats/kernel/instances/vector.scala | 14 ++------- 7 files changed, 50 insertions(+), 41 deletions(-) diff --git a/kernel-laws/src/test/scala/cats/kernel/laws/LawTests.scala b/kernel-laws/src/test/scala/cats/kernel/laws/LawTests.scala index 27438cc10a..c6623899ac 100644 --- a/kernel-laws/src/test/scala/cats/kernel/laws/LawTests.scala +++ b/kernel-laws/src/test/scala/cats/kernel/laws/LawTests.scala @@ -111,10 +111,12 @@ class LawTests extends FunSuite with Discipline { laws[HashLaws, BigDecimal].check(_.hash) laws[HashLaws, BigInt].check(_.hash) laws[HashLaws, UUID].check(_.hash) + laws[HashLaws, Duration].check(_.hash) laws[HashLaws, List[Int]].check(_.hash) laws[HashLaws, Option[String]].check(_.hash) laws[HashLaws, List[String]].check(_.hash) laws[HashLaws, Vector[Int]].check(_.hash) + laws[HashLaws, Queue[Int]].check(_.hash) laws[HashLaws, Stream[Int]].check(_.hash) laws[HashLaws, Set[Int]].check(_.hash) laws[HashLaws, (Int, String)].check(_.hash) @@ -139,6 +141,7 @@ class LawTests extends FunSuite with Discipline { laws[HashLaws, List[Int]].check(_.sameAsUniversalHash) laws[HashLaws, Option[String]].check(_.sameAsUniversalHash) laws[HashLaws, List[String]].check(_.sameAsUniversalHash) + laws[HashLaws, Queue[Int]].check(_.sameAsUniversalHash) laws[HashLaws, Vector[Int]].check(_.sameAsUniversalHash) laws[HashLaws, Stream[Int]].check(_.sameAsUniversalHash) laws[HashLaws, Set[Int]].check(_.sameAsUniversalHash) @@ -160,11 +163,13 @@ class LawTests extends FunSuite with Discipline { laws[HashLaws, BigDecimal].check(_.sameAsScalaHashing) laws[HashLaws, BigInt].check(_.sameAsScalaHashing) laws[HashLaws, UUID].check(_.sameAsScalaHashing) + laws[HashLaws, Duration].check(_.sameAsScalaHashing) laws[HashLaws, Option[HasHash[Int]]].check(_.hash) laws[HashLaws, List[HasHash[Int]]].check(_.hash) laws[HashLaws, Vector[HasHash[Int]]].check(_.hash) laws[HashLaws, Stream[HasHash[Int]]].check(_.hash) + laws[HashLaws, Queue[HasHash[Int]]].check(_.hash) laws[OrderLaws, List[HasEq[Int]]].check(_.eqv) laws[OrderLaws, Option[HasEq[Int]]].check(_.eqv) diff --git a/kernel/src/main/scala/cats/kernel/instances/StaticMethods.scala b/kernel/src/main/scala/cats/kernel/instances/StaticMethods.scala index 85944bc1ea..4b47e9da24 100644 --- a/kernel/src/main/scala/cats/kernel/instances/StaticMethods.scala +++ b/kernel/src/main/scala/cats/kernel/instances/StaticMethods.scala @@ -96,4 +96,34 @@ object StaticMethods { h = mix(h, _2Hash) finalizeHash(h, 2) } + + // adapted from [[scala.util.hashing.MurmurHash3]], + // but modified standard `Any#hashCode` to `ev.hash`. + def listHash[A](x: List[A])(implicit A: Hash[A]): Int = { + import scala.util.hashing.MurmurHash3._ + var n = 0 + var h = seqSeed + var elems = x + while (!elems.isEmpty) { + val head = elems.head + val tail = elems.tail + h = mix(h, A.hash(head)) + n += 1 + elems = tail + } + finalizeHash(h, n) + } + + // adapted from scala.util.hashing.MurmurHash3 + def orderedHash[A](xs: TraversableOnce[A])(implicit A: Hash[A]): Int = { + import scala.util.hashing.MurmurHash3._ + var n = 0 + var h = seqSeed + xs foreach { x => + h = mix(h, A.hash(x)) + n += 1 + } + finalizeHash(h, n) + } + } diff --git a/kernel/src/main/scala/cats/kernel/instances/duration.scala b/kernel/src/main/scala/cats/kernel/instances/duration.scala index fbbfca600e..406ed025e7 100644 --- a/kernel/src/main/scala/cats/kernel/instances/duration.scala +++ b/kernel/src/main/scala/cats/kernel/instances/duration.scala @@ -6,7 +6,7 @@ import scala.concurrent.duration.Duration package object duration extends DurationInstances trait DurationInstances { - implicit val catsKernelStdOrderForDuration: Order[Duration] = new DurationOrder + implicit val catsKernelStdOrderForDuration: Order[Duration] with Hash[Duration] = new DurationOrder implicit val catsKernelStdGroupForDuration: CommutativeGroup[Duration] = new DurationGroup } @@ -18,7 +18,9 @@ trait DurationInstances { * The value Duration.Undefined breaks our laws, because undefined * values are not equal to themselves. */ -class DurationOrder extends Order[Duration] { +class DurationOrder extends Order[Duration] with Hash[Duration] { + def hash(x: Duration): Int = x.hashCode() + def compare(x: Duration, y: Duration): Int = x compare y override def eqv(x: Duration, y: Duration): Boolean = x == y diff --git a/kernel/src/main/scala/cats/kernel/instances/list.scala b/kernel/src/main/scala/cats/kernel/instances/list.scala index afdf3c97d4..7e15e0bf05 100644 --- a/kernel/src/main/scala/cats/kernel/instances/list.scala +++ b/kernel/src/main/scala/cats/kernel/instances/list.scala @@ -62,22 +62,7 @@ class ListPartialOrder[A](implicit ev: PartialOrder[A]) extends PartialOrder[Lis } class ListHash[A](implicit ev: Hash[A]) extends ListEq[A]()(ev) with Hash[List[A]] { - // adapted from [[scala.util.hashing.MurmurHash3]], - // but modified standard `Any#hashCode` to `ev.hash`. - import scala.util.hashing.MurmurHash3._ - def hash(x: List[A]): Int = { - var n = 0 - var h = seqSeed - var elems = x - while (!elems.isEmpty) { - val head = elems.head - val tail = elems.tail - h = mix(h, ev.hash(head)) - n += 1 - elems = tail - } - finalizeHash(h, n) - } + def hash(x: List[A]): Int = StaticMethods.listHash(x)(ev) } class ListEq[A](implicit ev: Eq[A]) extends Eq[List[A]] { diff --git a/kernel/src/main/scala/cats/kernel/instances/queue.scala b/kernel/src/main/scala/cats/kernel/instances/queue.scala index 2018e9f300..af93560b22 100644 --- a/kernel/src/main/scala/cats/kernel/instances/queue.scala +++ b/kernel/src/main/scala/cats/kernel/instances/queue.scala @@ -15,6 +15,9 @@ trait QueueInstances extends QueueInstances1 { trait QueueInstances1 extends QueueInstances2 { implicit def catsKernelStdPartialOrderForQueue[A: PartialOrder]: PartialOrder[Queue[A]] = new QueuePartialOrder[A] + + implicit def catsKernelStdHashForQueue[A: Hash]: Hash[Queue[A]] = + new QueueHash[A] } trait QueueInstances2 { @@ -28,6 +31,10 @@ class QueueOrder[A](implicit ev: Order[A]) extends Order[Queue[A]] { else StaticMethods.iteratorCompare(xs.iterator, ys.iterator) } +class QueueHash[A](implicit ev: Hash[A]) extends QueueEq[A] with Hash[Queue[A]] { + def hash(x: Queue[A]): Int = StaticMethods.orderedHash(x) +} + class QueuePartialOrder[A](implicit ev: PartialOrder[A]) extends PartialOrder[Queue[A]] { def partialCompare(xs: Queue[A], ys: Queue[A]): Double = if (xs eq ys) 0.0 diff --git a/kernel/src/main/scala/cats/kernel/instances/stream.scala b/kernel/src/main/scala/cats/kernel/instances/stream.scala index 3d001b43ba..da7201c267 100644 --- a/kernel/src/main/scala/cats/kernel/instances/stream.scala +++ b/kernel/src/main/scala/cats/kernel/instances/stream.scala @@ -36,17 +36,7 @@ class StreamPartialOrder[A](implicit ev: PartialOrder[A]) extends PartialOrder[S } class StreamHash[A](implicit ev: Hash[A]) extends StreamEq[A]()(ev) with Hash[Stream[A]] { - import scala.util.hashing.MurmurHash3._ - // adapted from scala.util.hashing.MurmurHash3 - def hash(xs: Stream[A]): Int = { - var n = 0 - var h = seqSeed - xs foreach { x => - h = mix(h, ev.hash(x)) - n += 1 - } - finalizeHash(h, n) - } + def hash(xs: Stream[A]): Int = StaticMethods.orderedHash(xs) } class StreamEq[A](implicit ev: Eq[A]) extends Eq[Stream[A]] { diff --git a/kernel/src/main/scala/cats/kernel/instances/vector.scala b/kernel/src/main/scala/cats/kernel/instances/vector.scala index cc08e0fe6c..c7b15101e8 100644 --- a/kernel/src/main/scala/cats/kernel/instances/vector.scala +++ b/kernel/src/main/scala/cats/kernel/instances/vector.scala @@ -35,18 +35,8 @@ class VectorPartialOrder[A](implicit ev: PartialOrder[A]) extends PartialOrder[V else StaticMethods.iteratorPartialCompare(xs.iterator, ys.iterator) } -class VectorHash[A](implicit ev: Hash[A]) extends VectorEq[A]()(ev) with Hash[Vector[A]] { - // adapted from scala.util.hashing - import scala.util.hashing.MurmurHash3._ - def hash(xs: Vector[A]): Int = { - var n = 0 - var h = seqSeed - xs foreach { x => - h = mix(h, ev.hash(x)) - n += 1 - } - finalizeHash(h, n) - } +class VectorHash[A](implicit ev: Hash[A]) extends VectorEq[A] with Hash[Vector[A]] { + def hash(xs: Vector[A]): Int = StaticMethods.orderedHash(xs) } class VectorEq[A](implicit ev: Eq[A]) extends Eq[Vector[A]] {