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

Unify numeric type class instances #483

Merged
merged 14 commits into from
Apr 21, 2018
6 changes: 4 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ cache:
- "$HOME/.ivy2/cache"
- "$HOME/.sbt/boot/"
script:
- sbt ++$TRAVIS_SCALA_VERSION validate
- sbt ++$TRAVIS_SCALA_VERSION validateJVM
- sbt ++$TRAVIS_SCALA_VERSION validateJS
after_success:
- codecov
notifications:
Expand All @@ -31,7 +32,8 @@ matrix:
before_install:
- curl https://raw.githubusercontent.com/scala-native/scala-native/master/scripts/travis_setup.sh | bash -x
script:
- sbt ++$TRAVIS_SCALA_VERSION compileNative validate
- sbt ++$TRAVIS_SCALA_VERSION compileNative validateJVM
- sbt ++$TRAVIS_SCALA_VERSION validateJS
- scala: 2.13.0-M3 # Remember to update this in build.sbt, too.
script:
- sbt ++$TRAVIS_SCALA_VERSION coreJVM/compile
Expand Down
80 changes: 9 additions & 71 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,6 @@ lazy val moduleCrossSettings = Def.settings(
)

lazy val moduleJvmSettings = Def.settings(
fork in Test := true,
mimaPreviousArtifacts := {
val hasPredecessor = !unreleasedModules.value.contains(moduleName.value)
latestVersionInSeries.value match {
Expand All @@ -317,74 +316,7 @@ lazy val moduleJvmSettings = Def.settings(
mimaBinaryIssueFilters ++= {
import com.typesafe.tools.mima.core._
Seq(
ProblemFilters.exclude[ReversedMissingMethodProblem](
"eu.timepit.refined.scalacheck.StringInstances.nonEmptyStringArbitrary"),
ProblemFilters.exclude[DirectMissingMethodProblem]("eu.timepit.refined.api.Refined.get"),
ProblemFilters.exclude[DirectMissingMethodProblem](
"eu.timepit.refined.api.Refined.get$extension"),
ProblemFilters.exclude[MissingClassProblem]("eu.timepit.refined.util.time$"),
ProblemFilters.exclude[MissingClassProblem]("eu.timepit.refined.util.time"),
ProblemFilters.exclude[DirectMissingMethodProblem](
"eu.timepit.refined.numeric.moduloValidateNat"),
ProblemFilters.exclude[DirectMissingMethodProblem](
"eu.timepit.refined.numeric.moduloValidateWit"),
ProblemFilters.exclude[DirectMissingMethodProblem](
"eu.timepit.refined.NumericValidate.moduloValidateNat"),
ProblemFilters.exclude[DirectMissingMethodProblem](
"eu.timepit.refined.NumericValidate.moduloValidateWit"),
ProblemFilters.exclude[DirectMissingMethodProblem]("eu.timepit.refined.types.*"),
ProblemFilters.exclude[IncompatibleResultTypeProblem]("eu.timepit.refined.types.*"),
ProblemFilters.exclude[MissingClassProblem]("eu.timepit.refined.types.*"),
ProblemFilters.exclude[MissingTypesProblem]("eu.timepit.refined.types.*"),
ProblemFilters.exclude[ReversedMissingMethodProblem]("eu.timepit.refined.types.*"),
ProblemFilters.exclude[DirectMissingMethodProblem]("eu.timepit.refined.char.*"),
ProblemFilters.exclude[MissingClassProblem]("eu.timepit.refined.CharValidate"),
ProblemFilters.exclude[MissingTypesProblem]("eu.timepit.refined.char$*"),
ProblemFilters.exclude[DirectMissingMethodProblem]("eu.timepit.refined.boolean.*"),
ProblemFilters.exclude[MissingClassProblem]("eu.timepit.refined.BooleanValidate"),
ProblemFilters.exclude[MissingTypesProblem]("eu.timepit.refined.boolean$*"),
ProblemFilters.exclude[DirectMissingMethodProblem]("eu.timepit.refined.generic.*"),
ProblemFilters.exclude[MissingClassProblem]("eu.timepit.refined.GenericValidate"),
ProblemFilters.exclude[MissingTypesProblem]("eu.timepit.refined.generic$*"),
ProblemFilters.exclude[DirectMissingMethodProblem]("eu.timepit.refined.numeric.*"),
ProblemFilters.exclude[MissingClassProblem]("eu.timepit.refined.NumericValidate"),
ProblemFilters.exclude[MissingTypesProblem]("eu.timepit.refined.numeric*"),
ProblemFilters.exclude[DirectMissingMethodProblem]("eu.timepit.refined.string.*"),
ProblemFilters.exclude[MissingClassProblem]("eu.timepit.refined.StringValidate"),
ProblemFilters.exclude[MissingTypesProblem]("eu.timepit.refined.string*"),
ProblemFilters.exclude[DirectMissingMethodProblem]("eu.timepit.refined.collection.*"),
ProblemFilters.exclude[MissingClassProblem]("eu.timepit.refined.CollectionValidate"),
ProblemFilters.exclude[MissingTypesProblem]("eu.timepit.refined.collection*"),
ProblemFilters.exclude[DirectMissingMethodProblem]("eu.timepit.refined.string#IPv4.*"),
ProblemFilters.exclude[DirectMissingMethodProblem]("eu.timepit.refined.string#IPv6.*"),
ProblemFilters.exclude[MissingTypesProblem]("eu.timepit.refined.eval$"),
ProblemFilters.exclude[DirectMissingMethodProblem]("eu.timepit.refined.eval.evalValidate"),
ProblemFilters.exclude[MissingClassProblem]("eu.timepit.refined.EvalValidate"),
ProblemFilters.exclude[DirectMissingMethodProblem](
"eu.timepit.refined.jsonpath.string.jsonPathValidate"),
ProblemFilters.exclude[MissingTypesProblem]("eu.timepit.refined.jsonpath.string$*"),
ProblemFilters.exclude[MissingClassProblem]("eu.timepit.refined.jsonpath.StringValidate"),
ProblemFilters.exclude[ReversedMissingMethodProblem](
"eu.timepit.refined.NumericInference.greaterEqualInference"),
ProblemFilters.exclude[ReversedMissingMethodProblem](
"eu.timepit.refined.NumericInference.lessEqualInference"),
ProblemFilters.exclude[ReversedMissingMethodProblem](
"eu.timepit.refined.scalacheck.StringInstances.stringSizeArbitrary"),
ProblemFilters.exclude[IncompatibleMethTypeProblem](
"eu.timepit.refined.scalacheck.numeric.*"),
ProblemFilters.exclude[IncompatibleMethTypeProblem](
"eu.timepit.refined.scalacheck.NumericInstances.*"),
ProblemFilters.exclude[IncompatibleMethTypeProblem]("eu.timepit.refined.scalacheck.all.*"),
ProblemFilters.exclude[DirectMissingMethodProblem](
"eu.timepit.refined.scalacheck.NumericInstances.*"),
ProblemFilters.exclude[ReversedMissingMethodProblem](
"eu.timepit.refined.scalacheck.NumericInstances.*"),
ProblemFilters.exclude[InheritedNewAbstractMethodProblem]("eu.timepit.refined.types.*"),
ProblemFilters.exclude[ReversedMissingMethodProblem](
"eu.timepit.refined.api.RefinedType.dealias"),
ProblemFilters.exclude[ReversedMissingMethodProblem](
"eu.timepit.refined.scalacheck.RefTypeInstances.checkArbitraryRefinedType")
)
)
}
)

Expand Down Expand Up @@ -549,7 +481,7 @@ addCommandsAlias("testJS", allSubprojectsJS.map(_ + "/test"))
addCommandsAlias("testJVM", allSubprojectsJVM.map(_ + "/test"))

addCommandsAlias(
"validate",
"validateJVM",
Seq(
"clean",
"scalafmtCheck",
Expand All @@ -558,7 +490,6 @@ addCommandsAlias(
"scalastyle",
"test:scalastyle",
"mimaReportBinaryIssues",
"testJS",
"coverage",
"testJVM",
"coverageReport",
Expand All @@ -569,3 +500,10 @@ addCommandsAlias(
"packageSrc"
)
)

addCommandsAlias(
"validateJS",
Seq(
"testJS"
)
)
2 changes: 1 addition & 1 deletion latestVersion.sbt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
latestVersion in ThisBuild := "0.8.7"

latestVersionInSeries in ThisBuild := Some("0.8.7")
latestVersionInSeries in ThisBuild := None

unreleasedModules in ThisBuild := Set(
"refined-scopt",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
package eu.timepit.refined.api

import eu.timepit.refined.boolean.And
import eu.timepit.refined.internal.Adjacent
import eu.timepit.refined.internal.{Adjacent, WitnessAs}
import eu.timepit.refined.numeric.{Greater, GreaterEqual, Less, LessEqual}
import shapeless.{Nat, Witness}
import shapeless.ops.nat.ToInt

/**
* Type class defining the maximum value of a given type
Expand Down Expand Up @@ -37,18 +35,12 @@ trait MaxInstances extends LowPriorityMaxInstances {
): Max[F[T, GreaterEqual[N]]] =
Max.instance(rt.unsafeWrap(mt.max))

implicit def lessEqualMaxWit[F[_, _], T, N <: T](
implicit rt: RefType[F],
wn: Witness.Aux[N]
): Max[F[T, LessEqual[N]]] =
Max.instance(rt.unsafeWrap(wn.value))

implicit def lessEqualMaxNat[F[_, _], T, N <: Nat](
implicit rt: RefType[F],
tn: ToInt[N],
nt: Numeric[T]
implicit def lessEqualMax[F[_, _], T, N](
implicit
rt: RefType[F],
wn: WitnessAs[N, T]
): Max[F[T, LessEqual[N]]] =
Max.instance(rt.unsafeWrap(nt.fromInt(tn())))
Max.instance(rt.unsafeWrap(wn.snd))

implicit def lessMax[F[_, _], T, N](
implicit rt: RefType[F],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
package eu.timepit.refined.api

import eu.timepit.refined.boolean.And
import eu.timepit.refined.internal.Adjacent
import eu.timepit.refined.internal.{Adjacent, WitnessAs}
import eu.timepit.refined.numeric.{Greater, GreaterEqual, Less, LessEqual}
import shapeless.{Nat, Witness}
import shapeless.ops.nat.ToInt

/**
* Type class defining the minimum value of a given type
Expand Down Expand Up @@ -37,18 +35,12 @@ trait MinInstances extends LowPriorityMinInstances {
): Min[F[T, LessEqual[N]]] =
Min.instance(rt.unsafeWrap(mt.min))

implicit def greaterEqualMinWit[F[_, _], T, N <: T](
implicit rt: RefType[F],
wn: Witness.Aux[N]
): Min[F[T, GreaterEqual[N]]] =
Min.instance(rt.unsafeWrap(wn.value))

implicit def greaterEqualMinNat[F[_, _], T, N <: Nat](
implicit rt: RefType[F],
tn: ToInt[N],
nt: Numeric[T]
implicit def greaterEqualMin[F[_, _], T, N](
implicit
rt: RefType[F],
wn: WitnessAs[N, T]
): Min[F[T, GreaterEqual[N]]] =
Min.instance(rt.unsafeWrap(nt.fromInt(tn())))
Min.instance(rt.unsafeWrap(wn.snd))

implicit def greaterMin[F[_, _], T, N](
implicit rt: RefType[F],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ import eu.timepit.refined.boolean.Not
import eu.timepit.refined.collection._
import eu.timepit.refined.generic.Equal
import eu.timepit.refined.internal.Resources
import eu.timepit.refined.numeric.{GreaterEqual, LessEqual}
import eu.timepit.refined.numeric.{GreaterEqual, Interval}
import shapeless.Witness
import shapeless.nat._0

/** Module for collection predicates. */
object collection extends CollectionInference {
Expand Down Expand Up @@ -86,7 +87,7 @@ object collection extends CollectionInference {
* Predicate that checks if the size of a `Traversable` is less than
* or equal to `N`.
*/
type MaxSize[N] = Size[LessEqual[N]]
type MaxSize[N] = Size[Interval.Closed[_0, N]]

/** Predicate that checks if a `Traversable` is not empty. */
type NonEmpty = Not[Empty]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ package eu.timepit.refined
import eu.timepit.refined.api.{Inference, Validate}
import eu.timepit.refined.api.Inference.==>
import eu.timepit.refined.generic._
import eu.timepit.refined.internal.WitnessAs
import shapeless._
import shapeless.ops.coproduct.ToHList
import shapeless.ops.hlist.ToList
import shapeless.ops.nat.ToInt
import shapeless.ops.record.Keys

/** Module for generic predicates. */
Expand Down Expand Up @@ -36,19 +36,10 @@ object generic extends GenericInference {
final case class Supertype[U]()

object Equal {
implicit def equalValidateWit[T, U <: T](
implicit wu: Witness.Aux[U]
implicit def equalValidate[T, U](
implicit wu: WitnessAs[U, T]
): Validate.Plain[T, Equal[U]] =
Validate.fromPredicate(_ == wu.value, t => s"($t == ${wu.value})", Equal(wu.value))

implicit def equalValidateNat[N <: Nat, T](
implicit tn: ToInt[N],
wn: Witness.Aux[N],
nt: Numeric[T]
): Validate.Plain[T, Equal[N]] = {
val n = nt.fromInt(tn())
Validate.fromPredicate(_ == n, t => s"($t == $n)", Equal(wn.value))
}
Validate.fromPredicate(_ == wu.snd, t => s"($t == ${wu.snd})", Equal(wu.fst))
}

object ConstructorNames {
Expand Down Expand Up @@ -105,17 +96,10 @@ object generic extends GenericInference {

private[refined] trait GenericInference {

implicit def equalValidateInferenceWit[T, U <: T, P](
implicit v: Validate[T, P],
wu: Witness.Aux[U]
implicit def equalValidateInference[T, U, P](
implicit
v: Validate[T, P],
wu: WitnessAs[U, T]
): Equal[U] ==> P =
Inference(v.isValid(wu.value), s"equalValidateInferenceWit(${v.showExpr(wu.value)})")

implicit def equalValidateInferenceNat[T, N <: Nat, P](
implicit v: Validate[T, P],
nt: Numeric[T],
tn: ToInt[N]
): Equal[N] ==> P =
Inference(v.isValid(nt.fromInt(tn())),
s"equalValidateInferenceNat(${v.showExpr(nt.fromInt(tn()))})")
Inference(v.isValid(wu.snd), s"equalValidateInference(${v.showExpr(wu.snd)})")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package eu.timepit.refined.internal

import shapeless.{Nat, Witness}
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 {
def apply[A, B](implicit ev: WitnessAs[A, B]): WitnessAs[A, B] = ev

implicit def natWitnessAs[B, A <: Nat](
implicit
wa: Witness.Aux[A],
ta: ToInt[A],
nb: Numeric[B]
): WitnessAs[A, B] =
WitnessAs(wa.value, nb.fromInt(ta.apply()))

implicit def singletonWitnessAs[B, A <: B](
implicit wa: Witness.Aux[A]
): WitnessAs[A, B] =
WitnessAs(wa.value, wa.value)
}
Loading