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

Replace internal.Wrapper with RefType. closes #48, #53 #54

Merged
merged 1 commit into from
Aug 16, 2015
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
3 changes: 2 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -170,9 +170,10 @@ lazy val miscSettings = Seq(
import $rootPkg.char._
import $rootPkg.collection._
import $rootPkg.generic._
import $rootPkg.InferenceRule.==>
import $rootPkg.implicits._
import $rootPkg.InferenceRule.==>
import $rootPkg.numeric._
import $rootPkg.RefType.ops._
import $rootPkg.string._
import shapeless.{ ::, HList, HNil }
import shapeless.nat._
Expand Down
8 changes: 8 additions & 0 deletions notes/0.2.2.markdown
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
### Changes

* Introduce the `RefType` type class which abstracts over `shapeless.@@`
and `Refined` and that allows 3rd-party types to be used as result type
for refinements (e.g. [`scalaz.@@`][scalaz.@@]). `RefType` replaces the
now removed `internal.Wrapper` type class. ([#48], [#53], [#54])
* Make the `Refined` constructor private ([#52])

[#48]: https://github.com/fthomas/refined/issues/48
[#52]: https://github.com/fthomas/refined/issues/52
[#53]: https://github.com/fthomas/refined/issues/53
[#54]: https://github.com/fthomas/refined/pull/54
[scalaz.@@]: https://github.com/scalaz/scalaz/blob/v7.1.3/core/src/main/scala/scalaz/package.scala#L103
76 changes: 76 additions & 0 deletions shared/src/main/scala/eu/timepit/refined/RefType.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package eu.timepit.refined

import eu.timepit.refined.internal.{ RefineAux, RefineMAux }
import shapeless.tag.@@

import scala.reflect.macros.blackbox

trait RefType[F[_, _]] extends Serializable {

def unsafeWrap[T, P](t: T): F[T, P]

def unwrap[T, P](tp: F[T, P]): T

def unsafeWrapM[T: c.WeakTypeTag, P: c.WeakTypeTag](c: blackbox.Context)(t: c.Expr[T]): c.Expr[F[T, P]]

def unsafeRewrapM[T: c.WeakTypeTag, A: c.WeakTypeTag, B: c.WeakTypeTag](c: blackbox.Context)(ta: c.Expr[F[T, A]]): c.Expr[F[T, B]]

def refine[P]: RefineAux[F, P] =
new RefineAux[F, P](this)

def refineM[P]: RefineMAux[F, P] =
new RefineMAux[F, P]

def mapRefine[T, P, U](tp: F[T, P])(f: T => U)(implicit p: Predicate[P, U]): Either[String, F[U, P]] =
refine(f(unwrap(tp)))
}

object RefType {

def apply[F[_, _]](implicit rt: RefType[F]): RefType[F] = rt

implicit val refinedRefType: RefType[Refined] =
new RefType[Refined] {
override def unsafeWrap[T, P](t: T): Refined[T, P] =
Refined.unsafeApply(t)

override def unwrap[T, P](tp: Refined[T, P]): T =
tp.get

override def unsafeWrapM[T: c.WeakTypeTag, P: c.WeakTypeTag](c: blackbox.Context)(t: c.Expr[T]): c.Expr[Refined[T, P]] =
c.universe.reify(Refined.unsafeApply[T, P](t.splice))

override def unsafeRewrapM[T: c.WeakTypeTag, A: c.WeakTypeTag, B: c.WeakTypeTag](c: blackbox.Context)(ta: c.Expr[Refined[T, A]]): c.Expr[Refined[T, B]] =
c.universe.reify(ta.splice.asInstanceOf[Refined[T, B]])
}

implicit val tagRefType: RefType[@@] =
new RefType[@@] {
override def unsafeWrap[T, P](t: T): T @@ P =
t.asInstanceOf[T @@ P]

override def unwrap[T, P](tp: T @@ P): T =
tp

override def unsafeWrapM[T: c.WeakTypeTag, P: c.WeakTypeTag](c: blackbox.Context)(t: c.Expr[T]): c.Expr[T @@ P] =
c.universe.reify(t.splice.asInstanceOf[T @@ P])

override def unsafeRewrapM[T: c.WeakTypeTag, A: c.WeakTypeTag, B: c.WeakTypeTag](c: blackbox.Context)(ta: c.Expr[T @@ A]): c.Expr[T @@ B] =
c.universe.reify(ta.splice.asInstanceOf[T @@ B])
}

final class RefTypeOps[F[_, _], T, P](tp: F[T, P])(implicit F: RefType[F]) {

def unwrap: T =
F.unwrap(tp)

def mapRefine[U](f: T => U)(implicit p: Predicate[P, U]): Either[String, F[U, P]] =
F.mapRefine(tp)(f)
}

object ops {

implicit def toRefTypeOps[F[_, _]: RefType, T, P](tp: F[T, P]): RefTypeOps[F, T, P] =
new RefTypeOps(tp)
}
}
14 changes: 7 additions & 7 deletions shared/src/main/scala/eu/timepit/refined/implicits.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ object implicits {
* `F[T, B]` if there is a valid inference rule `A ==> B`. If the
* inference rule is invalid, compilation fails.
*/
implicit def autoInfer[T, A, B, F[_, _]](ta: F[T, A])(
implicit def autoInfer[F[_, _], T, A, B](ta: F[T, A])(
implicit
ir: A ==> B, w: Wrapper[F]
): F[T, B] = macro InferM.macroImpl[T, A, B, F]
ir: A ==> B, rt: RefType[F]
): F[T, B] = macro InferM.macroImpl[F, T, A, B]

/**
* Implicitly wraps (at compile-time) a value of type `T` in
Expand All @@ -25,8 +25,8 @@ object implicits {
*/
implicit def autoRefineV[T, P](t: T)(
implicit
p: Predicate[P, T], w: Wrapper[Refined]
): Refined[T, P] = macro RefineMAux.macroImpl[P, T, Refined]
p: Predicate[P, T], rt: RefType[Refined]
): Refined[T, P] = macro RefineMAux.macroImpl[Refined, T, P]

/**
* Implicitly tags (at compile-time) a value of type `T` with `P` if `t`
Expand All @@ -37,6 +37,6 @@ object implicits {
*/
implicit def autoRefineT[T, P](t: T)(
implicit
p: Predicate[P, T], w: Wrapper[@@]
): T @@ P = macro RefineMAux.macroImpl[P, T, @@]
p: Predicate[P, T], rt: RefType[@@]
): T @@ P = macro RefineMAux.macroImpl[@@, T, P]
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,16 @@ import scala.reflect.macros.blackbox

object InferM {

def macroImpl[T: c.WeakTypeTag, A: c.WeakTypeTag, B: c.WeakTypeTag, F[_, _]](c: blackbox.Context)(ta: c.Expr[F[T, A]])(
ir: c.Expr[A ==> B], w: c.Expr[Wrapper[F]]
def macroImpl[F[_, _], T: c.WeakTypeTag, A: c.WeakTypeTag, B: c.WeakTypeTag](c: blackbox.Context)(ta: c.Expr[F[T, A]])(
ir: c.Expr[A ==> B], rt: c.Expr[RefType[F]]
): c.Expr[F[T, B]] = {
import c.universe._

val inferenceRule = MacroUtils.eval(c)(ir)

if (inferenceRule.isValid) {
val wrapper = MacroUtils.eval(c)(w)
wrapper.rewrapM(c)(ta)
val refType = MacroUtils.eval(c)(rt)
refType.unsafeRewrapM(c)(ta)
} else
c.abort(c.enclosingPosition, s"invalid inference: ${weakTypeOf[A]} ==> ${weakTypeOf[B]}")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,16 @@ package internal

/**
* Helper class that allows the type `T` to be inferred from calls like
* `[[refineV]][P](t)`. See [[http://tpolecat.github.io/2015/07/30/infer.html]]
* for a detailed explanation of this trick.
* `[[RefType.refine]][P](t)`.
*
* See [[http://tpolecat.github.io/2015/07/30/infer.html]] for a detailed
* explanation of this trick.
*/
final class RefineAux[P, F[_, _]] {
final class RefineAux[F[_, _], P](rt: RefType[F]) {

def apply[T](t: T)(implicit p: Predicate[P, T], w: Wrapper[F]): Either[String, F[T, P]] =
def apply[T](t: T)(implicit p: Predicate[P, T]): Either[String, F[T, P]] =
p.validate(t) match {
case None => Right(w.wrap(t))
case None => Right(rt.unsafeWrap(t))
case Some(s) => Left(s)
}
}
18 changes: 10 additions & 8 deletions shared/src/main/scala/eu/timepit/refined/internal/RefineMAux.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,20 @@ import scala.reflect.macros.blackbox

/**
* Helper class that allows the type `T` to be inferred from calls like
* `[[refineMV]][P](t)`. See [[http://tpolecat.github.io/2015/07/30/infer.html]]
* for a detailed explanation of this trick.
* `[[RefType.refineM]][P](t)`.
*
* See [[http://tpolecat.github.io/2015/07/30/infer.html]] for a detailed
* explanation of this trick.
*/
final class RefineMAux[P, F[_, _]] {
final class RefineMAux[F[_, _], P] {

def apply[T](t: T)(implicit p: Predicate[P, T], w: Wrapper[F]): F[T, P] = macro RefineMAux.macroImpl[P, T, F]
def apply[T](t: T)(implicit p: Predicate[P, T], rt: RefType[F]): F[T, P] = macro RefineMAux.macroImpl[F, T, P]
}

object RefineMAux {

def macroImpl[P: c.WeakTypeTag, T: c.WeakTypeTag, F[_, _]](c: blackbox.Context)(t: c.Expr[T])(
p: c.Expr[Predicate[P, T]], w: c.Expr[Wrapper[F]]
def macroImpl[F[_, _], T: c.WeakTypeTag, P: c.WeakTypeTag](c: blackbox.Context)(t: c.Expr[T])(
p: c.Expr[Predicate[P, T]], rt: c.Expr[RefType[F]]
): c.Expr[F[T, P]] = {
import c.universe._

Expand All @@ -33,8 +35,8 @@ object RefineMAux {

predicate.validate(tValue) match {
case None =>
val wrapper = MacroUtils.eval(c)(w)
wrapper.wrapM(c)(t)
val refType = MacroUtils.eval(c)(rt)
refType.unsafeWrapM(c)(t)
case Some(msg) => c.abort(c.enclosingPosition, msg)
}
}
Expand Down
57 changes: 0 additions & 57 deletions shared/src/main/scala/eu/timepit/refined/internal/Wrapper.scala

This file was deleted.

14 changes: 7 additions & 7 deletions shared/src/main/scala/eu/timepit/refined/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ package object refined {
* an `apply` method on it, allowing `refineV` to be called like in the
* given example.
*/
def refineV[P]: RefineAux[P, Refined] = new RefineAux[P, Refined]
def refineV[P]: RefineAux[Refined, P] = RefType[Refined].refine[P]

/**
* Returns `t` with type `T @@ P` on the right if it satisfies the
Expand All @@ -47,7 +47,7 @@ package object refined {
* `apply` method on it, allowing `refineT` to be called like in the given
* example.
*/
def refineT[P]: RefineAux[P, @@] = new RefineAux[P, @@]
def refineT[P]: RefineAux[@@, P] = RefType[@@].refine[P]

/**
* Macro that returns `t` wrapped in `[[Refined]][T, P]` if it satisfies
Expand All @@ -67,7 +67,7 @@ package object refined {
* has an `apply` method on it, allowing `refineMV` to be called like in
* the given example.
*/
def refineMV[P]: RefineMAux[P, Refined] = new RefineMAux[P, Refined]
def refineMV[P]: RefineMAux[Refined, P] = RefType[Refined].refineM[P]

/**
* Macro that returns `t` with type `T @@ P` if it satisfies the predicate
Expand All @@ -88,14 +88,14 @@ package object refined {
* `apply` method on it, allowing `refineMT` to be called like in the given
* example.
*/
def refineMT[P]: RefineMAux[P, @@] = new RefineMAux[P, @@]
def refineMT[P]: RefineMAux[@@, P] = RefType[@@].refineM[P]

@deprecated("refine is deprecated in favor of refineT", "0.2.0")
def refine[P]: RefineAux[P, @@] = new RefineAux[P, @@]
def refine[P]: RefineAux[@@, P] = refineT[P]

@deprecated("refineLit is deprecated in favor of refineMT", "0.2.0")
def refineLit[P]: RefineMAux[P, @@] = new RefineMAux[P, @@]
def refineLit[P]: RefineMAux[@@, P] = refineMT[P]

@deprecated("refineM is deprecated in favor of refineMV", "0.2.0")
def refineM[P]: RefineMAux[P, Refined] = new RefineMAux[P, Refined]
def refineM[P]: RefineMAux[Refined, P] = refineMV[P]
}
26 changes: 26 additions & 0 deletions shared/src/test/scala/eu/timepit/refined/RefTypeSpec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package eu.timepit.refined

import eu.timepit.refined.RefType.ops._
import eu.timepit.refined.numeric.Positive
import org.scalacheck.Prop._
import org.scalacheck.Properties
import shapeless.tag.@@

abstract class RefTypeProperties[F[_, _]](name: String)(implicit rt: RefType[F]) extends Properties(s"RefType[$name]") {

property("unsafeWrap.unwrap ~= id") = forAll { (s: String) =>
rt.unsafeWrap(s).unwrap == s
}

property("mapRefine.success") = secure {
rt.refine[Positive](5).right.flatMap(_.mapRefine(_.toDouble)).isRight
}

property("mapRefine.failure") = secure {
rt.refine[Positive](5).right.flatMap(_.mapRefine(_ - 10)).isLeft
}
}

class RefTypeSpecRefined extends RefTypeProperties[Refined]("Refined")

class RefTypeSpecTag extends RefTypeProperties[@@]("@@")
16 changes: 0 additions & 16 deletions shared/src/test/scala/eu/timepit/refined/WrapperSpec.scala

This file was deleted.