Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

more instances for Hash (#1712): Queue/Duration #1950

Merged
merged 2 commits into from
Oct 6, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions kernel-laws/src/test/scala/cats/kernel/laws/LawTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -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)
Expand Down
30 changes: 30 additions & 0 deletions kernel/src/main/scala/cats/kernel/instances/StaticMethods.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

}
6 changes: 4 additions & 2 deletions kernel/src/main/scala/cats/kernel/instances/duration.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand All @@ -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
Expand Down
17 changes: 1 addition & 16 deletions kernel/src/main/scala/cats/kernel/instances/list.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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]] {
Expand Down
7 changes: 7 additions & 0 deletions kernel/src/main/scala/cats/kernel/instances/queue.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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
Expand Down
12 changes: 1 addition & 11 deletions kernel/src/main/scala/cats/kernel/instances/stream.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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]] {
Expand Down
14 changes: 2 additions & 12 deletions kernel/src/main/scala/cats/kernel/instances/vector.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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]] {
Expand Down