diff --git a/core/src/main/scala/cats/instances/all.scala b/core/src/main/scala/cats/instances/all.scala index 0a34003172..f270ec61f2 100644 --- a/core/src/main/scala/cats/instances/all.scala +++ b/core/src/main/scala/cats/instances/all.scala @@ -47,3 +47,4 @@ trait AllInstancesBinCompat1 trait AllInstancesBinCompat2 extends DurationInstances + with FiniteDurationInstances diff --git a/core/src/main/scala/cats/instances/finiteDuration.scala b/core/src/main/scala/cats/instances/finiteDuration.scala new file mode 100644 index 0000000000..182b7d9905 --- /dev/null +++ b/core/src/main/scala/cats/instances/finiteDuration.scala @@ -0,0 +1,9 @@ +package cats +package instances + +import scala.concurrent.duration.FiniteDuration + +trait FiniteDurationInstances extends cats.kernel.instances.FiniteDurationInstances { + implicit val catsStdShowForFiniteDuration: Show[FiniteDuration] = + Show.fromToString[FiniteDuration] +} diff --git a/core/src/main/scala/cats/instances/package.scala b/core/src/main/scala/cats/instances/package.scala index 0d8ffb2480..6085a85875 100644 --- a/core/src/main/scala/cats/instances/package.scala +++ b/core/src/main/scala/cats/instances/package.scala @@ -14,6 +14,7 @@ package object instances { object eq extends EqInstances object equiv extends EquivInstances object float extends FloatInstances + object finiteDuration extends FiniteDurationInstances object function extends FunctionInstances with FunctionInstancesBinCompat0 object future extends FutureInstances object int extends IntInstances 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 f069d0285a..44779c592c 100644 --- a/kernel-laws/src/test/scala/cats/kernel/laws/LawTests.scala +++ b/kernel-laws/src/test/scala/cats/kernel/laws/LawTests.scala @@ -14,7 +14,7 @@ import Arbitrary.arbitrary import org.scalactic.anyvals.{ PosInt, PosZInt } import org.scalatest.FunSuite -import scala.concurrent.duration.Duration +import scala.concurrent.duration.{ Duration, FiniteDuration } import scala.collection.immutable.{BitSet, Queue} import scala.util.Random @@ -47,6 +47,20 @@ object KernelCheck { Gen.choose(-n * 86400000000000L, n * 86400000000000L).map(Duration(_, NANOSECONDS)))) } + implicit val arbitraryFiniteDuration: Arbitrary[FiniteDuration] = { + // max range is +/- 292 years, but we give ourselves some extra headroom + // to ensure that we can add these things up. they crash on overflow. + val n = (292L * 365) / 50 + Arbitrary(Gen.oneOf( + Gen.choose(-n, n).map(FiniteDuration(_, DAYS)), + Gen.choose(-n * 24L, n * 24L).map(FiniteDuration(_, HOURS)), + Gen.choose(-n * 1440L, n * 1440L).map(FiniteDuration(_, MINUTES)), + Gen.choose(-n * 86400L, n * 86400L).map(FiniteDuration(_, SECONDS)), + Gen.choose(-n * 86400000L, n * 86400000L).map(FiniteDuration(_, MILLISECONDS)), + Gen.choose(-n * 86400000000L, n * 86400000000L).map(FiniteDuration(_, MICROSECONDS)), + Gen.choose(-n * 86400000000000L, n * 86400000000000L).map(FiniteDuration(_, NANOSECONDS)))) + } + // this instance is not available in scalacheck 1.13.2. // remove this once a newer version is available. implicit val cogenBigInt: Cogen[BigInt] = @@ -78,6 +92,19 @@ object KernelCheck { case NANOSECONDS => 6128745701389500153L }) } + + implicit val cogenFiniteDuration: Cogen[FiniteDuration] = + Cogen[Long].contramap { d => + d.length * (d.unit match { + case DAYS => -6307593037248227856L + case HOURS => -3527447467459552709L + case MINUTES => 5955657079535371609L + case SECONDS => 5314272869665647192L + case MILLISECONDS => -2025740217814855607L + case MICROSECONDS => -2965853209268633779L + case NANOSECONDS => 6128745701389500153L + }) + } } class Tests extends FunSuite with Discipline { @@ -132,6 +159,7 @@ class Tests extends FunSuite with Discipline { checkAll("PartialOrder[BitSet]", PartialOrderTests[BitSet].partialOrder) checkAll("Order[BigInt]", OrderTests[BigInt].order) checkAll("Order[Duration]", OrderTests[Duration].order) + checkAll("Order[FiniteDuration]", OrderTests[FiniteDuration].order) checkAll("Order[UUID]", OrderTests[UUID].order) checkAll("Order[List[Int]]", OrderTests[List[Int]] .order) checkAll("Order[Option[String]]", OrderTests[Option[String]].order) @@ -187,6 +215,8 @@ class Tests extends FunSuite with Discipline { checkAll("CommutativeGroup[BigInt]", SerializableTests.serializable(CommutativeGroup[BigInt])) checkAll("CommutativeGroup[Duration]", CommutativeGroupTests[Duration].commutativeGroup) checkAll("CommutativeGroup[Duration]", SerializableTests.serializable(CommutativeGroup[Duration])) + checkAll("CommutativeGroup[FiniteDuration]", CommutativeGroupTests[FiniteDuration].commutativeGroup) + checkAll("CommutativeGroup[FiniteDuration]", SerializableTests.serializable(CommutativeGroup[FiniteDuration])) checkAll("Hash[Unit]" , HashTests[Unit].hash) @@ -198,6 +228,7 @@ class Tests extends FunSuite with Discipline { checkAll("Hash[Char]" , HashTests[Char].hash) checkAll("Hash[Int]" , HashTests[Int].hash) checkAll("Hash[Duration]", HashTests[Duration].hash) + checkAll("Hash[FiniteDuration]", HashTests[FiniteDuration].hash) // NOTE: Do not test for Float/Double/Long. These types' // `##` is different from `hashCode`. See [[scala.runtime.Statics.anyHash]]. diff --git a/kernel/src/main/scala/cats/kernel/instances/all.scala b/kernel/src/main/scala/cats/kernel/instances/all.scala index 1af2f0b0da..75d1503545 100644 --- a/kernel/src/main/scala/cats/kernel/instances/all.scala +++ b/kernel/src/main/scala/cats/kernel/instances/all.scala @@ -1,7 +1,7 @@ package cats.kernel package instances -package object all extends AllInstances +package object all extends AllInstances with AllInstancesBinCompat0 trait AllInstances extends BigDecimalInstances @@ -34,3 +34,6 @@ trait AllInstances with UnitInstances with UUIDInstances with VectorInstances + + +trait AllInstancesBinCompat0 extends FiniteDurationInstances diff --git a/kernel/src/main/scala/cats/kernel/instances/finiteDuration.scala b/kernel/src/main/scala/cats/kernel/instances/finiteDuration.scala new file mode 100644 index 0000000000..3efa92d283 --- /dev/null +++ b/kernel/src/main/scala/cats/kernel/instances/finiteDuration.scala @@ -0,0 +1,32 @@ +package cats.kernel +package instances + +import scala.concurrent.duration.{ Duration, FiniteDuration } + + +trait FiniteDurationInstances { + implicit val catsKernelStdOrderForFiniteDuration: Order[FiniteDuration] with Hash[FiniteDuration] = new FiniteDurationOrder + implicit val catsKernelStdGroupForFiniteDuration: CommutativeGroup[FiniteDuration] = new FiniteDurationGroup +} +class FiniteDurationOrder extends Order[FiniteDuration] with Hash[FiniteDuration] { + def hash(x: FiniteDuration): Int = x.hashCode() + + def compare(x: FiniteDuration, y: FiniteDuration): Int = x compare y + + override def eqv(x: FiniteDuration, y: FiniteDuration): Boolean = x == y + override def neqv(x: FiniteDuration, y: FiniteDuration): Boolean = x != y + override def gt(x: FiniteDuration, y: FiniteDuration): Boolean = x > y + override def gteqv(x: FiniteDuration, y: FiniteDuration): Boolean = x >= y + override def lt(x: FiniteDuration, y: FiniteDuration): Boolean = x < y + override def lteqv(x: FiniteDuration, y: FiniteDuration): Boolean = x <= y + + override def min(x: FiniteDuration, y: FiniteDuration): FiniteDuration = x min y + override def max(x: FiniteDuration, y: FiniteDuration): FiniteDuration = x max y +} + +class FiniteDurationGroup extends CommutativeGroup[FiniteDuration] { + def empty: FiniteDuration = Duration.Zero + def inverse(x: FiniteDuration): FiniteDuration = -x + def combine(x: FiniteDuration, y: FiniteDuration): FiniteDuration = x + y + override def remove(x: FiniteDuration, y: FiniteDuration): FiniteDuration = x - y +} diff --git a/tests/src/test/scala/cats/tests/FiniteDurationSuite.scala b/tests/src/test/scala/cats/tests/FiniteDurationSuite.scala new file mode 100644 index 0000000000..35cd201a8b --- /dev/null +++ b/tests/src/test/scala/cats/tests/FiniteDurationSuite.scala @@ -0,0 +1,15 @@ +package cats +package tests + +import cats.laws.discipline.SerializableTests + +import scala.concurrent.duration.{ DurationInt, FiniteDuration } + +class FiniteDurationSuite extends CatsSuite { + checkAll("Show[FiniteDuration]", SerializableTests.serializable(Show[FiniteDuration])) + + test("show works for FiniteDuration"){ + Show[FiniteDuration].show(23.minutes) should ===("23 minutes") + Show[FiniteDuration].show(10.seconds) should === ("10 seconds") + } +}