From be868c281768a9bf44681fd3e123f21ec0baf1d1 Mon Sep 17 00:00:00 2001 From: "Frank S. Thomas" Date: Fri, 25 Sep 2020 21:36:30 +0200 Subject: [PATCH 1/9] Allow Int and Double literals as universal arguments ... for numeric predicates. --- .../timepit/refined/internal/WitnessAs.scala | 69 ++++++++++++++---- .../eu/timepit/refined/numeric.scala | 9 ++- .../timepit/refined/internal/WitnessAs.scala | 70 ++++++++++++++----- .../eu/timepit/refined/numeric.scala | 9 ++- .../timepit/refined/NumericValidateSpec.scala | 29 ++++++-- .../pureconfig/RefTypeConfigConvertSpec.scala | 4 +- .../shapeless/typeable/TypeableSpec.scala | 2 +- 7 files changed, 142 insertions(+), 50 deletions(-) diff --git a/modules/core/shared/src/main/scala-3.0+/eu/timepit/refined/internal/WitnessAs.scala b/modules/core/shared/src/main/scala-3.0+/eu/timepit/refined/internal/WitnessAs.scala index 29b4e9a19..4b940e71b 100644 --- a/modules/core/shared/src/main/scala-3.0+/eu/timepit/refined/internal/WitnessAs.scala +++ b/modules/core/shared/src/main/scala-3.0+/eu/timepit/refined/internal/WitnessAs.scala @@ -5,24 +5,10 @@ import shapeless.{Nat, Witness} /** * `WitnessAs[A, B]` provides the singleton value of type `A` in `fst` * and `fst` converted to type `B` in `snd`. - * - * The purpose of this type class is to write numeric type class - * instances that work with both literal singleton types and - * `shapeless.Nat`. - * - * Example: {{{ - * scala> import shapeless.nat._5 - * - * scala> WitnessAs[5, Int] - * res1: WitnessAs[5, Int] = WitnessAs(5,5) - * - * scala> WitnessAs[_5, Int] - * res2: WitnessAs[_5, Int] = WitnessAs(Succ(),5) - * }}} */ final case class WitnessAs[A, B](fst: A, snd: B) -object WitnessAs { +object WitnessAs extends WitnessAs1 { def apply[A, B](implicit ev: WitnessAs[A, B]): WitnessAs[A, B] = ev implicit def natWitnessAs[B, A <: Nat](implicit @@ -37,3 +23,56 @@ object WitnessAs { ): WitnessAs[A, B] = WitnessAs(wa.value, wa.value) } + +trait WitnessAs1 { + implicit def intWitnessAsByte[A <: Int](implicit + wa: ValueOf[A] + ): WitnessAs[A, Byte] = + if (wa.value >= Byte.MinValue.toInt && wa.value <= Byte.MaxValue.toInt) + WitnessAs(wa.value, wa.value.toByte) + else sys.error(s"WitnessAs: ${wa.value} is not in [Byte.MinValue, Byte.MaxValue]") + + implicit def intWitnessAsShort[A <: Int](implicit + wa: ValueOf[A] + ): WitnessAs[A, Short] = + if (wa.value >= Short.MinValue.toInt && wa.value <= Short.MaxValue.toInt) + WitnessAs(wa.value, wa.value.toShort) + else sys.error(s"WitnessAs: ${wa.value} is not in [Short.MinValue, Short.MaxValue]") + + implicit def intWitnessAsLong[A <: Int](implicit + wa: ValueOf[A] + ): WitnessAs[A, Long] = + WitnessAs(wa.value, wa.value.toLong) + + implicit def intWitnessAsBigInt[A <: Int](implicit + wa: ValueOf[A] + ): WitnessAs[A, BigInt] = + WitnessAs(wa.value, BigInt(wa.value)) + + implicit def intWitnessAsFloat[A <: Int](implicit + wa: ValueOf[A] + ): WitnessAs[A, Float] = + WitnessAs(wa.value, wa.value.toFloat) + + implicit def intWitnessAsDouble[A <: Int](implicit + wa: ValueOf[A] + ): WitnessAs[A, Double] = + WitnessAs(wa.value, wa.value.toDouble) + + implicit def intWitnessAsBigDecimal[A <: Int](implicit + wa: ValueOf[A] + ): WitnessAs[A, BigDecimal] = + WitnessAs(wa.value, BigDecimal(wa.value)) + + implicit def doubleWitnessAsFloat[A <: Double](implicit + wa: ValueOf[A] + ): WitnessAs[A, Float] = + if (wa.value >= Float.MinValue.toDouble && wa.value <= Float.MaxValue.toDouble) + WitnessAs(wa.value, wa.value.toFloat) + else sys.error(s"WitnessAs: ${wa.value} is not in [Float.MinValue, Float.MaxValue]") + + implicit def doubleWitnessAsBigDecimal[A <: Double](implicit + wa: ValueOf[A] + ): WitnessAs[A, BigDecimal] = + WitnessAs(wa.value, BigDecimal(wa.value)) +} diff --git a/modules/core/shared/src/main/scala-3.0+/eu/timepit/refined/numeric.scala b/modules/core/shared/src/main/scala-3.0+/eu/timepit/refined/numeric.scala index f6de5fcf8..9ac140823 100644 --- a/modules/core/shared/src/main/scala-3.0+/eu/timepit/refined/numeric.scala +++ b/modules/core/shared/src/main/scala-3.0+/eu/timepit/refined/numeric.scala @@ -6,7 +6,6 @@ import eu.timepit.refined.boolean.{And, Not} import eu.timepit.refined.internal.WitnessAs import eu.timepit.refined.numeric._ import shapeless.Nat -import shapeless.nat.{_0, _2} import shapeless.ops.nat.ToInt /** @@ -51,25 +50,25 @@ object numeric extends NumericInference { type GreaterEqual[N] = Not[Less[N]] /** Predicate that checks if a numeric value is positive (> 0). */ - type Positive = Greater[_0] + type Positive = Greater[0] /** Predicate that checks if a numeric value is zero or negative (<= 0). */ type NonPositive = Not[Positive] /** Predicate that checks if a numeric value is negative (< 0). */ - type Negative = Less[_0] + type Negative = Less[0] /** Predicate that checks if a numeric value is zero or positive (>= 0). */ type NonNegative = Not[Negative] /** Predicate that checks if an integral value is evenly divisible by `N`. */ - type Divisible[N] = Modulo[N, _0] + type Divisible[N] = Modulo[N, 0] /** Predicate that checks if an integral value is not evenly divisible by `N`. */ type NonDivisible[N] = Not[Divisible[N]] /** Predicate that checks if an integral value is evenly divisible by 2. */ - type Even = Divisible[_2] + type Even = Divisible[2] /** Predicate that checks if an integral value is not evenly divisible by 2. */ type Odd = Not[Even] diff --git a/modules/core/shared/src/main/scala-3.0-/eu/timepit/refined/internal/WitnessAs.scala b/modules/core/shared/src/main/scala-3.0-/eu/timepit/refined/internal/WitnessAs.scala index 6a8308ea1..a673f3db4 100644 --- a/modules/core/shared/src/main/scala-3.0-/eu/timepit/refined/internal/WitnessAs.scala +++ b/modules/core/shared/src/main/scala-3.0-/eu/timepit/refined/internal/WitnessAs.scala @@ -6,25 +6,10 @@ import shapeless.ops.nat.ToInt /** * `WitnessAs[A, B]` provides the singleton value of type `A` in `fst` * and `fst` converted to type `B` in `snd`. - * - * The purpose of this type class is to write numeric type class - * instances that work with both literal singleton types and - * `shapeless.Nat`. - * - * Example: {{{ - * scala> import eu.timepit.refined.W - * | import shapeless.nat._5 - * - * scala> WitnessAs[W.`5`.T, Int] - * res1: WitnessAs[W.`5`.T, Int] = WitnessAs(5,5) - * - * scala> WitnessAs[_5, Int] - * res2: WitnessAs[_5, Int] = WitnessAs(Succ(),5) - * }}} */ final case class WitnessAs[A, B](fst: A, snd: B) -object WitnessAs { +object WitnessAs extends WitnessAs1 { def apply[A, B](implicit ev: WitnessAs[A, B]): WitnessAs[A, B] = ev implicit def natWitnessAs[B, A <: Nat](implicit @@ -39,3 +24,56 @@ object WitnessAs { ): WitnessAs[A, B] = WitnessAs(wa.value, wa.value) } + +trait WitnessAs1 { + implicit def intWitnessAsByte[A <: Int](implicit + wa: Witness.Aux[A] + ): WitnessAs[A, Byte] = + if (wa.value >= Byte.MinValue.toInt && wa.value <= Byte.MaxValue.toInt) + WitnessAs(wa.value, wa.value.toByte) + else sys.error(s"WitnessAs: ${wa.value} is not in [Byte.MinValue, Byte.MaxValue]") + + implicit def intWitnessAsShort[A <: Int](implicit + wa: Witness.Aux[A] + ): WitnessAs[A, Short] = + if (wa.value >= Short.MinValue.toInt && wa.value <= Short.MaxValue.toInt) + WitnessAs(wa.value, wa.value.toShort) + else sys.error(s"WitnessAs: ${wa.value} is not in [Short.MinValue, Short.MaxValue]") + + implicit def intWitnessAsLong[A <: Int](implicit + wa: Witness.Aux[A] + ): WitnessAs[A, Long] = + WitnessAs(wa.value, wa.value.toLong) + + implicit def intWitnessAsBigInt[A <: Int](implicit + wa: Witness.Aux[A] + ): WitnessAs[A, BigInt] = + WitnessAs(wa.value, BigInt(wa.value)) + + implicit def intWitnessAsFloat[A <: Int](implicit + wa: Witness.Aux[A] + ): WitnessAs[A, Float] = + WitnessAs(wa.value, wa.value.toFloat) + + implicit def intWitnessAsDouble[A <: Int](implicit + wa: Witness.Aux[A] + ): WitnessAs[A, Double] = + WitnessAs(wa.value, wa.value.toDouble) + + implicit def intWitnessAsBigDecimal[A <: Int](implicit + wa: Witness.Aux[A] + ): WitnessAs[A, BigDecimal] = + WitnessAs(wa.value, BigDecimal(wa.value)) + + implicit def doubleWitnessAsFloat[A <: Double](implicit + wa: Witness.Aux[A] + ): WitnessAs[A, Float] = + if (wa.value >= Float.MinValue.toDouble && wa.value <= Float.MaxValue.toDouble) + WitnessAs(wa.value, wa.value.toFloat) + else sys.error(s"WitnessAs: ${wa.value} is not in [Float.MinValue, Float.MaxValue]") + + implicit def doubleWitnessAsBigDecimal[A <: Double](implicit + wa: Witness.Aux[A] + ): WitnessAs[A, BigDecimal] = + WitnessAs(wa.value, BigDecimal(wa.value)) +} diff --git a/modules/core/shared/src/main/scala-3.0-/eu/timepit/refined/numeric.scala b/modules/core/shared/src/main/scala-3.0-/eu/timepit/refined/numeric.scala index 42b8d02c9..9aab069ad 100644 --- a/modules/core/shared/src/main/scala-3.0-/eu/timepit/refined/numeric.scala +++ b/modules/core/shared/src/main/scala-3.0-/eu/timepit/refined/numeric.scala @@ -6,7 +6,6 @@ import eu.timepit.refined.boolean.{And, Not} import eu.timepit.refined.internal.WitnessAs import eu.timepit.refined.numeric._ import shapeless.Nat -import shapeless.nat.{_0, _2} import shapeless.ops.nat.ToInt /** @@ -51,25 +50,25 @@ object numeric extends NumericInference { type GreaterEqual[N] = Not[Less[N]] /** Predicate that checks if a numeric value is positive (> 0). */ - type Positive = Greater[_0] + type Positive = Greater[W.`0`.T] /** Predicate that checks if a numeric value is zero or negative (<= 0). */ type NonPositive = Not[Positive] /** Predicate that checks if a numeric value is negative (< 0). */ - type Negative = Less[_0] + type Negative = Less[W.`0`.T] /** Predicate that checks if a numeric value is zero or positive (>= 0). */ type NonNegative = Not[Negative] /** Predicate that checks if an integral value is evenly divisible by `N`. */ - type Divisible[N] = Modulo[N, _0] + type Divisible[N] = Modulo[N, W.`0`.T] /** Predicate that checks if an integral value is not evenly divisible by `N`. */ type NonDivisible[N] = Not[Divisible[N]] /** Predicate that checks if an integral value is evenly divisible by 2. */ - type Even = Divisible[_2] + type Even = Divisible[W.`2`.T] /** Predicate that checks if an integral value is not evenly divisible by 2. */ type Odd = Not[Even] diff --git a/modules/core/shared/src/test/scala-3.0-/eu/timepit/refined/NumericValidateSpec.scala b/modules/core/shared/src/test/scala-3.0-/eu/timepit/refined/NumericValidateSpec.scala index c84c22c15..32c83aac2 100644 --- a/modules/core/shared/src/test/scala-3.0-/eu/timepit/refined/NumericValidateSpec.scala +++ b/modules/core/shared/src/test/scala-3.0-/eu/timepit/refined/NumericValidateSpec.scala @@ -8,9 +8,19 @@ import shapeless.nat._ class NumericValidateSpec extends Properties("NumericValidate") { - property("Less.isValid") = forAll((d: Double) => isValid[Less[W.`1.0`.T]](d) ?= (d < 1.0)) + property("isValid[Less[1]](d: Double)") = forAll { (d: Double) => + isValid[Less[W.`1`.T]](d) ?= (d < 1) + } + + property("isValid[Less[1.0]](d: Double)") = forAll { (d: Double) => + isValid[Less[W.`1.0`.T]](d) ?= (d < 1.0) + } + + property("isValid[Less[1.0]](d: BigDecimal)") = forAll { (d: BigDecimal) => + isValid[Less[W.`1.0`.T]](d) ?= (d < 1.0) + } - property("Less.showExpr") = secure { + property("showExpr[Less[1.1]](0.1)") = secure { showExpr[Less[W.`1.1`.T]](0.1) ?= "(0.1 < 1.1)" } @@ -30,7 +40,11 @@ class NumericValidateSpec extends Properties("NumericValidate") { showResult[Less[_5]](i) ?= showResult[Less[W.`5`.T]](i) } - property("Greater.isValid") = forAll { (d: Double) => + property("isValid[Greater[1.0]](b: Byte)") = forAll { (b: Byte) => + isValid[Greater[W.`1`.T]](b) ?= (b > 1) + } + + property("isValid[Greater[1.0]](d: Double)") = forAll { (d: Double) => isValid[Greater[W.`1.0`.T]](d) ?= (d > 1.0) } @@ -118,13 +132,17 @@ class NumericValidateSpec extends Properties("NumericValidate") { showExpr[NonDivisible[_2]](4) ?= "!(4 % 2 == 0)" } - property("Even.isValid") = forAll((i: Int) => isValid[Even](i) ?= (i % 2 == 0)) + property("Even.isValid") = forAll { (i: Int) => + isValid[Even](i) ?= (i % 2 == 0) + } property("Even.showExpr") = secure { showExpr[Even](4) ?= "(4 % 2 == 0)" } - property("Odd.isValid") = forAll((i: Int) => isValid[Odd](i) ?= (i % 2 != 0)) + property("Odd.isValid") = forAll { (i: Int) => + isValid[Odd](i) ?= (i % 2 != 0) + } property("Odd.showExpr") = secure { showExpr[Odd](4) ?= "!(4 % 2 == 0)" @@ -184,5 +202,4 @@ class NumericValidateSpec extends Properties("NumericValidate") { property("NonNaN.Double.showExpr") = secure { showExpr[NonNaN](Double.NaN) ?= "(NaN != NaN)" } - } diff --git a/modules/pureconfig/shared/src/test/scala/eu/timepit/refined/pureconfig/RefTypeConfigConvertSpec.scala b/modules/pureconfig/shared/src/test/scala/eu/timepit/refined/pureconfig/RefTypeConfigConvertSpec.scala index c5ecbf70a..313d4846d 100644 --- a/modules/pureconfig/shared/src/test/scala/eu/timepit/refined/pureconfig/RefTypeConfigConvertSpec.scala +++ b/modules/pureconfig/shared/src/test/scala/eu/timepit/refined/pureconfig/RefTypeConfigConvertSpec.scala @@ -24,7 +24,7 @@ class RefTypeConfigConvertSpec extends Properties("RefTypeConfigConvert") { reason = CannotConvert( value = "0", toType = - "eu.timepit.refined.api.Refined[Int,eu.timepit.refined.numeric.Greater[shapeless.nat._0]]", + "eu.timepit.refined.api.Refined[Int,eu.timepit.refined.numeric.Greater[Int(0)]]", because = "Predicate failed: (0 > 0)." ), origin = Some(ConfigOriginFactory.newSimple("String").withLineNumber(1)), @@ -41,7 +41,7 @@ class RefTypeConfigConvertSpec extends Properties("RefTypeConfigConvert") { reason = CannotConvert( value = "0", toType = - "eu.timepit.refined.api.Refined[scala.Int,eu.timepit.refined.numeric.Greater[shapeless.nat._0]]", + "eu.timepit.refined.api.Refined[scala.Int,eu.timepit.refined.numeric.Greater[Int(0)]]", because = "Predicate failed: (0 > 0)." ), origin = Some(ConfigOriginFactory.newSimple("String").withLineNumber(1)), diff --git a/modules/shapeless/shared/src/test/scala/eu/timepit/refined/shapeless/typeable/TypeableSpec.scala b/modules/shapeless/shared/src/test/scala/eu/timepit/refined/shapeless/typeable/TypeableSpec.scala index b981ed812..1f5b794e2 100644 --- a/modules/shapeless/shared/src/test/scala/eu/timepit/refined/shapeless/typeable/TypeableSpec.scala +++ b/modules/shapeless/shared/src/test/scala/eu/timepit/refined/shapeless/typeable/TypeableSpec.scala @@ -20,7 +20,7 @@ class TypeableSpec extends Properties("shapeless") { } property("Typeable describe") = secure { - typeableDescribe[PosInt] ?= "Refined[Int, Greater[_0]]" + typeableDescribe[PosInt] ?= "Refined[Int, Greater[Int(0)]]" } property("Typeable cast success string regex") = secure { From 0557ac8e0b313caf96a628fe09a639cede8ee172 Mon Sep 17 00:00:00 2001 From: "Frank S. Thomas" Date: Sat, 26 Sep 2020 09:51:28 +0200 Subject: [PATCH 2/9] Remove unnecessary WitnessAs1 trait --- .../scala-3.0+/eu/timepit/refined/internal/WitnessAs.scala | 4 +--- .../scala-3.0-/eu/timepit/refined/internal/WitnessAs.scala | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/modules/core/shared/src/main/scala-3.0+/eu/timepit/refined/internal/WitnessAs.scala b/modules/core/shared/src/main/scala-3.0+/eu/timepit/refined/internal/WitnessAs.scala index 4b940e71b..ab0914f04 100644 --- a/modules/core/shared/src/main/scala-3.0+/eu/timepit/refined/internal/WitnessAs.scala +++ b/modules/core/shared/src/main/scala-3.0+/eu/timepit/refined/internal/WitnessAs.scala @@ -8,7 +8,7 @@ import shapeless.{Nat, Witness} */ final case class WitnessAs[A, B](fst: A, snd: B) -object WitnessAs extends WitnessAs1 { +object WitnessAs { def apply[A, B](implicit ev: WitnessAs[A, B]): WitnessAs[A, B] = ev implicit def natWitnessAs[B, A <: Nat](implicit @@ -22,9 +22,7 @@ object WitnessAs extends WitnessAs1 { wa: ValueOf[A] ): WitnessAs[A, B] = WitnessAs(wa.value, wa.value) -} -trait WitnessAs1 { implicit def intWitnessAsByte[A <: Int](implicit wa: ValueOf[A] ): WitnessAs[A, Byte] = diff --git a/modules/core/shared/src/main/scala-3.0-/eu/timepit/refined/internal/WitnessAs.scala b/modules/core/shared/src/main/scala-3.0-/eu/timepit/refined/internal/WitnessAs.scala index a673f3db4..a1397bdd3 100644 --- a/modules/core/shared/src/main/scala-3.0-/eu/timepit/refined/internal/WitnessAs.scala +++ b/modules/core/shared/src/main/scala-3.0-/eu/timepit/refined/internal/WitnessAs.scala @@ -9,7 +9,7 @@ import shapeless.ops.nat.ToInt */ final case class WitnessAs[A, B](fst: A, snd: B) -object WitnessAs extends WitnessAs1 { +object WitnessAs { def apply[A, B](implicit ev: WitnessAs[A, B]): WitnessAs[A, B] = ev implicit def natWitnessAs[B, A <: Nat](implicit @@ -23,9 +23,7 @@ object WitnessAs extends WitnessAs1 { wa: Witness.Aux[A] ): WitnessAs[A, B] = WitnessAs(wa.value, wa.value) -} -trait WitnessAs1 { implicit def intWitnessAsByte[A <: Int](implicit wa: Witness.Aux[A] ): WitnessAs[A, Byte] = From e8736119f0434f2327ab3d7eb6f4c5226f6a168d Mon Sep 17 00:00:00 2001 From: "Frank S. Thomas" Date: Sat, 26 Sep 2020 15:07:43 +0200 Subject: [PATCH 3/9] Revert "Remove unnecessary WitnessAs1 trait" This reverts commit 0557ac8e0b313caf96a628fe09a639cede8ee172. --- .../scala-3.0+/eu/timepit/refined/internal/WitnessAs.scala | 4 +++- .../scala-3.0-/eu/timepit/refined/internal/WitnessAs.scala | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/modules/core/shared/src/main/scala-3.0+/eu/timepit/refined/internal/WitnessAs.scala b/modules/core/shared/src/main/scala-3.0+/eu/timepit/refined/internal/WitnessAs.scala index ab0914f04..4b940e71b 100644 --- a/modules/core/shared/src/main/scala-3.0+/eu/timepit/refined/internal/WitnessAs.scala +++ b/modules/core/shared/src/main/scala-3.0+/eu/timepit/refined/internal/WitnessAs.scala @@ -8,7 +8,7 @@ import shapeless.{Nat, Witness} */ final case class WitnessAs[A, B](fst: A, snd: B) -object WitnessAs { +object WitnessAs extends WitnessAs1 { def apply[A, B](implicit ev: WitnessAs[A, B]): WitnessAs[A, B] = ev implicit def natWitnessAs[B, A <: Nat](implicit @@ -22,7 +22,9 @@ object WitnessAs { wa: ValueOf[A] ): WitnessAs[A, B] = WitnessAs(wa.value, wa.value) +} +trait WitnessAs1 { implicit def intWitnessAsByte[A <: Int](implicit wa: ValueOf[A] ): WitnessAs[A, Byte] = diff --git a/modules/core/shared/src/main/scala-3.0-/eu/timepit/refined/internal/WitnessAs.scala b/modules/core/shared/src/main/scala-3.0-/eu/timepit/refined/internal/WitnessAs.scala index a1397bdd3..a673f3db4 100644 --- a/modules/core/shared/src/main/scala-3.0-/eu/timepit/refined/internal/WitnessAs.scala +++ b/modules/core/shared/src/main/scala-3.0-/eu/timepit/refined/internal/WitnessAs.scala @@ -9,7 +9,7 @@ import shapeless.ops.nat.ToInt */ final case class WitnessAs[A, B](fst: A, snd: B) -object WitnessAs { +object WitnessAs extends WitnessAs1 { def apply[A, B](implicit ev: WitnessAs[A, B]): WitnessAs[A, B] = ev implicit def natWitnessAs[B, A <: Nat](implicit @@ -23,7 +23,9 @@ object WitnessAs { wa: Witness.Aux[A] ): WitnessAs[A, B] = WitnessAs(wa.value, wa.value) +} +trait WitnessAs1 { implicit def intWitnessAsByte[A <: Int](implicit wa: Witness.Aux[A] ): WitnessAs[A, Byte] = From 2bee713cd3ca1d2acaad1bdfcb7a4d5a53396f9e Mon Sep 17 00:00:00 2001 From: "Frank S. Thomas" Date: Sat, 26 Sep 2020 15:12:00 +0200 Subject: [PATCH 4/9] Defer changing Positive to 0.10 --- .../main/scala-3.0+/eu/timepit/refined/numeric.scala | 11 ++++++----- .../main/scala-3.0-/eu/timepit/refined/numeric.scala | 9 +++++---- .../refined/pureconfig/RefTypeConfigConvertSpec.scala | 4 ++-- .../refined/shapeless/typeable/TypeableSpec.scala | 2 +- 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/modules/core/shared/src/main/scala-3.0+/eu/timepit/refined/numeric.scala b/modules/core/shared/src/main/scala-3.0+/eu/timepit/refined/numeric.scala index 9ac140823..465ecf545 100644 --- a/modules/core/shared/src/main/scala-3.0+/eu/timepit/refined/numeric.scala +++ b/modules/core/shared/src/main/scala-3.0+/eu/timepit/refined/numeric.scala @@ -3,10 +3,11 @@ package eu.timepit.refined import eu.timepit.refined.api.{Inference, Validate} import eu.timepit.refined.api.Inference.==> import eu.timepit.refined.boolean.{And, Not} +import eu.timepit.refined.internal.ToInt import eu.timepit.refined.internal.WitnessAs import eu.timepit.refined.numeric._ import shapeless.Nat -import shapeless.ops.nat.ToInt +import shapeless.nat.{_0, _2} /** * Module for numeric predicates. Predicates that take type parameters @@ -50,25 +51,25 @@ object numeric extends NumericInference { type GreaterEqual[N] = Not[Less[N]] /** Predicate that checks if a numeric value is positive (> 0). */ - type Positive = Greater[0] + type Positive = Greater[_0] /** Predicate that checks if a numeric value is zero or negative (<= 0). */ type NonPositive = Not[Positive] /** Predicate that checks if a numeric value is negative (< 0). */ - type Negative = Less[0] + type Negative = Less[_0] /** Predicate that checks if a numeric value is zero or positive (>= 0). */ type NonNegative = Not[Negative] /** Predicate that checks if an integral value is evenly divisible by `N`. */ - type Divisible[N] = Modulo[N, 0] + type Divisible[N] = Modulo[N, _0] /** Predicate that checks if an integral value is not evenly divisible by `N`. */ type NonDivisible[N] = Not[Divisible[N]] /** Predicate that checks if an integral value is evenly divisible by 2. */ - type Even = Divisible[2] + type Even = Divisible[_2] /** Predicate that checks if an integral value is not evenly divisible by 2. */ type Odd = Not[Even] diff --git a/modules/core/shared/src/main/scala-3.0-/eu/timepit/refined/numeric.scala b/modules/core/shared/src/main/scala-3.0-/eu/timepit/refined/numeric.scala index 9aab069ad..42b8d02c9 100644 --- a/modules/core/shared/src/main/scala-3.0-/eu/timepit/refined/numeric.scala +++ b/modules/core/shared/src/main/scala-3.0-/eu/timepit/refined/numeric.scala @@ -6,6 +6,7 @@ import eu.timepit.refined.boolean.{And, Not} import eu.timepit.refined.internal.WitnessAs import eu.timepit.refined.numeric._ import shapeless.Nat +import shapeless.nat.{_0, _2} import shapeless.ops.nat.ToInt /** @@ -50,25 +51,25 @@ object numeric extends NumericInference { type GreaterEqual[N] = Not[Less[N]] /** Predicate that checks if a numeric value is positive (> 0). */ - type Positive = Greater[W.`0`.T] + type Positive = Greater[_0] /** Predicate that checks if a numeric value is zero or negative (<= 0). */ type NonPositive = Not[Positive] /** Predicate that checks if a numeric value is negative (< 0). */ - type Negative = Less[W.`0`.T] + type Negative = Less[_0] /** Predicate that checks if a numeric value is zero or positive (>= 0). */ type NonNegative = Not[Negative] /** Predicate that checks if an integral value is evenly divisible by `N`. */ - type Divisible[N] = Modulo[N, W.`0`.T] + type Divisible[N] = Modulo[N, _0] /** Predicate that checks if an integral value is not evenly divisible by `N`. */ type NonDivisible[N] = Not[Divisible[N]] /** Predicate that checks if an integral value is evenly divisible by 2. */ - type Even = Divisible[W.`2`.T] + type Even = Divisible[_2] /** Predicate that checks if an integral value is not evenly divisible by 2. */ type Odd = Not[Even] diff --git a/modules/pureconfig/shared/src/test/scala/eu/timepit/refined/pureconfig/RefTypeConfigConvertSpec.scala b/modules/pureconfig/shared/src/test/scala/eu/timepit/refined/pureconfig/RefTypeConfigConvertSpec.scala index 313d4846d..c5ecbf70a 100644 --- a/modules/pureconfig/shared/src/test/scala/eu/timepit/refined/pureconfig/RefTypeConfigConvertSpec.scala +++ b/modules/pureconfig/shared/src/test/scala/eu/timepit/refined/pureconfig/RefTypeConfigConvertSpec.scala @@ -24,7 +24,7 @@ class RefTypeConfigConvertSpec extends Properties("RefTypeConfigConvert") { reason = CannotConvert( value = "0", toType = - "eu.timepit.refined.api.Refined[Int,eu.timepit.refined.numeric.Greater[Int(0)]]", + "eu.timepit.refined.api.Refined[Int,eu.timepit.refined.numeric.Greater[shapeless.nat._0]]", because = "Predicate failed: (0 > 0)." ), origin = Some(ConfigOriginFactory.newSimple("String").withLineNumber(1)), @@ -41,7 +41,7 @@ class RefTypeConfigConvertSpec extends Properties("RefTypeConfigConvert") { reason = CannotConvert( value = "0", toType = - "eu.timepit.refined.api.Refined[scala.Int,eu.timepit.refined.numeric.Greater[Int(0)]]", + "eu.timepit.refined.api.Refined[scala.Int,eu.timepit.refined.numeric.Greater[shapeless.nat._0]]", because = "Predicate failed: (0 > 0)." ), origin = Some(ConfigOriginFactory.newSimple("String").withLineNumber(1)), diff --git a/modules/shapeless/shared/src/test/scala/eu/timepit/refined/shapeless/typeable/TypeableSpec.scala b/modules/shapeless/shared/src/test/scala/eu/timepit/refined/shapeless/typeable/TypeableSpec.scala index 1f5b794e2..6ba5640f3 100644 --- a/modules/shapeless/shared/src/test/scala/eu/timepit/refined/shapeless/typeable/TypeableSpec.scala +++ b/modules/shapeless/shared/src/test/scala/eu/timepit/refined/shapeless/typeable/TypeableSpec.scala @@ -20,7 +20,7 @@ class TypeableSpec extends Properties("shapeless") { } property("Typeable describe") = secure { - typeableDescribe[PosInt] ?= "Refined[Int, Greater[Int(0)]]" + typeableDescribe[PosInt] ?= "Refined[Int, Greater[shapeless.nat._0]]" } property("Typeable cast success string regex") = secure { From 035778a36ad09a944105972bb312de68052e3e43 Mon Sep 17 00:00:00 2001 From: "Frank S. Thomas" Date: Sat, 26 Sep 2020 15:16:23 +0200 Subject: [PATCH 5/9] Fix test --- .../eu/timepit/refined/shapeless/typeable/TypeableSpec.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/shapeless/shared/src/test/scala/eu/timepit/refined/shapeless/typeable/TypeableSpec.scala b/modules/shapeless/shared/src/test/scala/eu/timepit/refined/shapeless/typeable/TypeableSpec.scala index 6ba5640f3..b981ed812 100644 --- a/modules/shapeless/shared/src/test/scala/eu/timepit/refined/shapeless/typeable/TypeableSpec.scala +++ b/modules/shapeless/shared/src/test/scala/eu/timepit/refined/shapeless/typeable/TypeableSpec.scala @@ -20,7 +20,7 @@ class TypeableSpec extends Properties("shapeless") { } property("Typeable describe") = secure { - typeableDescribe[PosInt] ?= "Refined[Int, Greater[shapeless.nat._0]]" + typeableDescribe[PosInt] ?= "Refined[Int, Greater[_0]]" } property("Typeable cast success string regex") = secure { From 88d86893f90252a3ce86931b558985223820f7be Mon Sep 17 00:00:00 2001 From: "Frank S. Thomas" Date: Sat, 26 Sep 2020 16:23:35 +0200 Subject: [PATCH 6/9] Document the purpose of WitnessAs --- .../eu/timepit/refined/internal/WitnessAs.scala | 16 ++++++++++++++++ .../eu/timepit/refined/internal/WitnessAs.scala | 16 ++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/modules/core/shared/src/main/scala-3.0+/eu/timepit/refined/internal/WitnessAs.scala b/modules/core/shared/src/main/scala-3.0+/eu/timepit/refined/internal/WitnessAs.scala index 4b940e71b..7e4618db2 100644 --- a/modules/core/shared/src/main/scala-3.0+/eu/timepit/refined/internal/WitnessAs.scala +++ b/modules/core/shared/src/main/scala-3.0+/eu/timepit/refined/internal/WitnessAs.scala @@ -5,6 +5,22 @@ import shapeless.{Nat, Witness} /** * `WitnessAs[A, B]` provides the singleton value of type `A` in `fst` * and `fst` converted to type `B` in `snd`. + * + * The purpose of this type class is to allow literals of other + * types than the base type to be used as arguments in numeric + * predicates. + * + * Example: {{{ + * scala> import eu.timepit.refined.refineV + * | import eu.timepit.refined.api.Refined + * | import eu.timepit.refined.numeric.{Greater, Less} + * + * scala> refineV[Greater[2.718]](BigDecimal(3.141)) + * res0: Either[String, BigDecimal Refined Greater[2.718]] = Right(3.141) + * + * scala> refineV[Less[1]](0.618) + * res1: Either[String, Double Refined Less[1]] = Right(0.618) + * }}} */ final case class WitnessAs[A, B](fst: A, snd: B) diff --git a/modules/core/shared/src/main/scala-3.0-/eu/timepit/refined/internal/WitnessAs.scala b/modules/core/shared/src/main/scala-3.0-/eu/timepit/refined/internal/WitnessAs.scala index a673f3db4..9ebc52ec9 100644 --- a/modules/core/shared/src/main/scala-3.0-/eu/timepit/refined/internal/WitnessAs.scala +++ b/modules/core/shared/src/main/scala-3.0-/eu/timepit/refined/internal/WitnessAs.scala @@ -6,6 +6,22 @@ import shapeless.ops.nat.ToInt /** * `WitnessAs[A, B]` provides the singleton value of type `A` in `fst` * and `fst` converted to type `B` in `snd`. + * + * The purpose of this type class is to allow literals of other + * types than the base type to be used as arguments in numeric + * predicates. + * + * Example: {{{ + * scala> import eu.timepit.refined.{refineV, W} + * | import eu.timepit.refined.api.Refined + * | import eu.timepit.refined.numeric.{Greater, Less} + * + * scala> refineV[Greater[W.`2.718`.T]](BigDecimal(3.141)) + * res0: Either[String, BigDecimal Refined Greater[W.`2.718`.T]] = Right(3.141) + * + * scala> refineV[Less[W.`1`.T]](0.618) + * res1: Either[String, Double Refined Less[W.`1`.T]] = Right(0.618) + * }}} */ final case class WitnessAs[A, B](fst: A, snd: B) From 23e9cef225faa0834bfa499927e9410c9788b16b Mon Sep 17 00:00:00 2001 From: "Frank S. Thomas" Date: Sat, 26 Sep 2020 19:56:57 +0200 Subject: [PATCH 7/9] Check bounds of some WitnessAs instances at compile-time --- .../timepit/refined/internal/WitnessAs.scala | 82 +++++++++---------- 1 file changed, 40 insertions(+), 42 deletions(-) diff --git a/modules/core/shared/src/main/scala-3.0+/eu/timepit/refined/internal/WitnessAs.scala b/modules/core/shared/src/main/scala-3.0+/eu/timepit/refined/internal/WitnessAs.scala index 7e4618db2..692249dd5 100644 --- a/modules/core/shared/src/main/scala-3.0+/eu/timepit/refined/internal/WitnessAs.scala +++ b/modules/core/shared/src/main/scala-3.0+/eu/timepit/refined/internal/WitnessAs.scala @@ -1,5 +1,6 @@ package eu.timepit.refined.internal +import scala.compiletime.{constValue, error} import shapeless.{Nat, Witness} /** @@ -41,54 +42,51 @@ object WitnessAs extends WitnessAs1 { } trait WitnessAs1 { - implicit def intWitnessAsByte[A <: Int](implicit - wa: ValueOf[A] - ): WitnessAs[A, Byte] = - if (wa.value >= Byte.MinValue.toInt && wa.value <= Byte.MaxValue.toInt) - WitnessAs(wa.value, wa.value.toByte) - else sys.error(s"WitnessAs: ${wa.value} is not in [Byte.MinValue, Byte.MaxValue]") + inline implicit def intWitnessAsByte[A <: Int]: WitnessAs[A, Byte] = + inline constValue[A] match { + case a if a >= -128 && a <= 127 => WitnessAs(a, a.toByte) + case a => error(s"WitnessAs: $a is not in [Byte.MinValue, Byte.MaxValue]") + } - implicit def intWitnessAsShort[A <: Int](implicit - wa: ValueOf[A] - ): WitnessAs[A, Short] = - if (wa.value >= Short.MinValue.toInt && wa.value <= Short.MaxValue.toInt) - WitnessAs(wa.value, wa.value.toShort) - else sys.error(s"WitnessAs: ${wa.value} is not in [Short.MinValue, Short.MaxValue]") + inline implicit def intWitnessAsShort[A <: Int]: WitnessAs[A, Short] = + inline constValue[A] match { + case a if a >= -32768 && a <= 32767 => WitnessAs(a, a.toShort) + case a => error(s"WitnessAs: $a is not in [Short.MinValue, Short.MaxValue]") + } - implicit def intWitnessAsLong[A <: Int](implicit - wa: ValueOf[A] - ): WitnessAs[A, Long] = - WitnessAs(wa.value, wa.value.toLong) + inline implicit def intWitnessAsLong[A <: Int]: WitnessAs[A, Long] = { + inline val a = constValue[A] + WitnessAs(a, a.toLong) + } - implicit def intWitnessAsBigInt[A <: Int](implicit - wa: ValueOf[A] - ): WitnessAs[A, BigInt] = - WitnessAs(wa.value, BigInt(wa.value)) + inline implicit def intWitnessAsBigInt[A <: Int]: WitnessAs[A, BigInt] = { + inline val a = constValue[A] + WitnessAs(a, BigInt(a)) + } - implicit def intWitnessAsFloat[A <: Int](implicit - wa: ValueOf[A] - ): WitnessAs[A, Float] = - WitnessAs(wa.value, wa.value.toFloat) + inline implicit def intWitnessAsFloat[A <: Int]: WitnessAs[A, Float] = { + inline val a = constValue[A] + WitnessAs(a, a.toFloat) + } - implicit def intWitnessAsDouble[A <: Int](implicit - wa: ValueOf[A] - ): WitnessAs[A, Double] = - WitnessAs(wa.value, wa.value.toDouble) + inline implicit def intWitnessAsDouble[A <: Int]: WitnessAs[A, Double] = { + inline val a = constValue[A] + WitnessAs(a, a.toDouble) + } - implicit def intWitnessAsBigDecimal[A <: Int](implicit - wa: ValueOf[A] - ): WitnessAs[A, BigDecimal] = - WitnessAs(wa.value, BigDecimal(wa.value)) + inline implicit def intWitnessAsBigDecimal[A <: Int]: WitnessAs[A, BigDecimal] = { + inline val a = constValue[A] + WitnessAs(a, BigDecimal(a)) + } - implicit def doubleWitnessAsFloat[A <: Double](implicit - wa: ValueOf[A] - ): WitnessAs[A, Float] = - if (wa.value >= Float.MinValue.toDouble && wa.value <= Float.MaxValue.toDouble) - WitnessAs(wa.value, wa.value.toFloat) - else sys.error(s"WitnessAs: ${wa.value} is not in [Float.MinValue, Float.MaxValue]") + inline implicit def doubleWitnessAsFloat[A <: Double]: WitnessAs[A, Float] = + inline constValue[A] match { + case a if a >= -3.4028235E38 && a <= 3.4028235E38 => WitnessAs(a, a.toFloat) + case a => error(s"WitnessAs: $a is not in [Float.MinValue, Float.MaxValue]") + } - implicit def doubleWitnessAsBigDecimal[A <: Double](implicit - wa: ValueOf[A] - ): WitnessAs[A, BigDecimal] = - WitnessAs(wa.value, BigDecimal(wa.value)) + inline implicit def doubleWitnessAsBigDecimal[A <: Double]: WitnessAs[A, BigDecimal] = { + inline val a = constValue[A] + WitnessAs(a, BigDecimal(a)) + } } From f73a80c9cf9cba492397cd655fb651c4a17971b4 Mon Sep 17 00:00:00 2001 From: "Frank S. Thomas" Date: Sat, 26 Sep 2020 20:01:42 +0200 Subject: [PATCH 8/9] Use constValue instead of ValueOf --- .../eu/timepit/refined/internal/WitnessAs.scala | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/core/shared/src/main/scala-3.0+/eu/timepit/refined/internal/WitnessAs.scala b/modules/core/shared/src/main/scala-3.0+/eu/timepit/refined/internal/WitnessAs.scala index 692249dd5..4805fe36d 100644 --- a/modules/core/shared/src/main/scala-3.0+/eu/timepit/refined/internal/WitnessAs.scala +++ b/modules/core/shared/src/main/scala-3.0+/eu/timepit/refined/internal/WitnessAs.scala @@ -35,10 +35,10 @@ object WitnessAs extends WitnessAs1 { ): WitnessAs[A, B] = WitnessAs(wa.value, nb.fromInt(ta.apply())) - implicit def singletonWitnessAs[B, A <: B](implicit - wa: ValueOf[A] - ): WitnessAs[A, B] = - WitnessAs(wa.value, wa.value) + inline implicit def singletonWitnessAs[B, A <: B]: WitnessAs[A, B] = { + inline val a = constValue[A] + WitnessAs(a, a) + } } trait WitnessAs1 { From 61ba27f600b37841765871d5948a9d6a82613c67 Mon Sep 17 00:00:00 2001 From: "Frank S. Thomas" Date: Sat, 26 Sep 2020 20:12:06 +0200 Subject: [PATCH 9/9] Use given instead of implicit def --- .../timepit/refined/internal/WitnessAs.scala | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/modules/core/shared/src/main/scala-3.0+/eu/timepit/refined/internal/WitnessAs.scala b/modules/core/shared/src/main/scala-3.0+/eu/timepit/refined/internal/WitnessAs.scala index 4805fe36d..0011833a0 100644 --- a/modules/core/shared/src/main/scala-3.0+/eu/timepit/refined/internal/WitnessAs.scala +++ b/modules/core/shared/src/main/scala-3.0+/eu/timepit/refined/internal/WitnessAs.scala @@ -35,57 +35,57 @@ object WitnessAs extends WitnessAs1 { ): WitnessAs[A, B] = WitnessAs(wa.value, nb.fromInt(ta.apply())) - inline implicit def singletonWitnessAs[B, A <: B]: WitnessAs[A, B] = { + inline given singletonWitnessAs[B, A <: B] as WitnessAs[A, B] = { inline val a = constValue[A] WitnessAs(a, a) } } trait WitnessAs1 { - inline implicit def intWitnessAsByte[A <: Int]: WitnessAs[A, Byte] = + inline given intWitnessAsByte[A <: Int] as WitnessAs[A, Byte] = inline constValue[A] match { case a if a >= -128 && a <= 127 => WitnessAs(a, a.toByte) case a => error(s"WitnessAs: $a is not in [Byte.MinValue, Byte.MaxValue]") } - inline implicit def intWitnessAsShort[A <: Int]: WitnessAs[A, Short] = + inline given intWitnessAsShort[A <: Int] as WitnessAs[A, Short] = inline constValue[A] match { case a if a >= -32768 && a <= 32767 => WitnessAs(a, a.toShort) case a => error(s"WitnessAs: $a is not in [Short.MinValue, Short.MaxValue]") } - inline implicit def intWitnessAsLong[A <: Int]: WitnessAs[A, Long] = { + inline given intWitnessAsLong[A <: Int] as WitnessAs[A, Long] = { inline val a = constValue[A] WitnessAs(a, a.toLong) } - inline implicit def intWitnessAsBigInt[A <: Int]: WitnessAs[A, BigInt] = { + inline given intWitnessAsBigInt[A <: Int] as WitnessAs[A, BigInt] = { inline val a = constValue[A] WitnessAs(a, BigInt(a)) } - inline implicit def intWitnessAsFloat[A <: Int]: WitnessAs[A, Float] = { + inline given intWitnessAsFloat[A <: Int] as WitnessAs[A, Float] = { inline val a = constValue[A] WitnessAs(a, a.toFloat) } - inline implicit def intWitnessAsDouble[A <: Int]: WitnessAs[A, Double] = { + inline given intWitnessAsDouble[A <: Int] as WitnessAs[A, Double] = { inline val a = constValue[A] WitnessAs(a, a.toDouble) } - inline implicit def intWitnessAsBigDecimal[A <: Int]: WitnessAs[A, BigDecimal] = { + inline given intWitnessAsBigDecimal[A <: Int] as WitnessAs[A, BigDecimal] = { inline val a = constValue[A] WitnessAs(a, BigDecimal(a)) } - inline implicit def doubleWitnessAsFloat[A <: Double]: WitnessAs[A, Float] = + inline given doubleWitnessAsFloat[A <: Double] as WitnessAs[A, Float] = inline constValue[A] match { case a if a >= -3.4028235E38 && a <= 3.4028235E38 => WitnessAs(a, a.toFloat) case a => error(s"WitnessAs: $a is not in [Float.MinValue, Float.MaxValue]") } - inline implicit def doubleWitnessAsBigDecimal[A <: Double]: WitnessAs[A, BigDecimal] = { + inline given doubleWitnessAsBigDecimal[A <: Double] as WitnessAs[A, BigDecimal] = { inline val a = constValue[A] WitnessAs(a, BigDecimal(a)) }