diff --git a/docs/utils.md b/docs/utils.md
index cd5924a37..960595682 100644
--- a/docs/utils.md
+++ b/docs/utils.md
@@ -94,7 +94,7 @@ xml("link xpath("A//B/*[1]")
-res12: javax.xml.xpath.XPathExpression = com.sun.org.apache.xpath.internal.jaxp.XPathExpressionImpl@28c16218
+res12: javax.xml.xpath.XPathExpression = com.sun.org.apache.xpath.internal.jaxp.XPathExpressionImpl@433df4a4
scala> xpath("A//B/*[1")
:18: error: XPath predicate failed: javax.xml.transform.TransformerException: Expected ], but found:
diff --git a/notes/0.2.2.markdown b/notes/0.2.2.markdown
index 06568cb59..5c4e8ebe1 100644
--- a/notes/0.2.2.markdown
+++ b/notes/0.2.2.markdown
@@ -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/issues/54
+[scalaz.@@]: https://github.com/scalaz/scalaz/blob/v7.1.3/core/src/main/scala/scalaz/package.scala#L103
diff --git a/shared/src/main/scala/eu/timepit/refined/RefType.scala b/shared/src/main/scala/eu/timepit/refined/RefType.scala
new file mode 100644
index 000000000..bd66669c5
--- /dev/null
+++ b/shared/src/main/scala/eu/timepit/refined/RefType.scala
@@ -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])
+ }
+
+ 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)
+ }
+}
diff --git a/shared/src/main/scala/eu/timepit/refined/implicits.scala b/shared/src/main/scala/eu/timepit/refined/implicits.scala
index 9fce10582..67a4d7c2a 100644
--- a/shared/src/main/scala/eu/timepit/refined/implicits.scala
+++ b/shared/src/main/scala/eu/timepit/refined/implicits.scala
@@ -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
@@ -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`
@@ -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]
}
diff --git a/shared/src/main/scala/eu/timepit/refined/internal/InferM.scala b/shared/src/main/scala/eu/timepit/refined/internal/InferM.scala
index ae826fab5..d9712cbce 100644
--- a/shared/src/main/scala/eu/timepit/refined/internal/InferM.scala
+++ b/shared/src/main/scala/eu/timepit/refined/internal/InferM.scala
@@ -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]}")
}
diff --git a/shared/src/main/scala/eu/timepit/refined/internal/RefineAux.scala b/shared/src/main/scala/eu/timepit/refined/internal/RefineAux.scala
index 489f771a1..55ccceed7 100644
--- a/shared/src/main/scala/eu/timepit/refined/internal/RefineAux.scala
+++ b/shared/src/main/scala/eu/timepit/refined/internal/RefineAux.scala
@@ -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)
}
}
diff --git a/shared/src/main/scala/eu/timepit/refined/internal/RefineMAux.scala b/shared/src/main/scala/eu/timepit/refined/internal/RefineMAux.scala
index 7ca9ff9fc..25bf5c352 100644
--- a/shared/src/main/scala/eu/timepit/refined/internal/RefineMAux.scala
+++ b/shared/src/main/scala/eu/timepit/refined/internal/RefineMAux.scala
@@ -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._
@@ -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)
}
}
diff --git a/shared/src/main/scala/eu/timepit/refined/internal/Wrapper.scala b/shared/src/main/scala/eu/timepit/refined/internal/Wrapper.scala
deleted file mode 100644
index 35c94b64b..000000000
--- a/shared/src/main/scala/eu/timepit/refined/internal/Wrapper.scala
+++ /dev/null
@@ -1,57 +0,0 @@
-package eu.timepit.refined
-package internal
-
-import shapeless.tag.@@
-
-import scala.reflect.macros.blackbox
-
-/**
- * Type class for wrapping a value of type `T` into `F` together with a
- * phantom type `P`. Instances must satisfy the following law:
- * `forall t, unwrap(wrap(t)) == t`.
- */
-trait Wrapper[F[_, _]] extends Serializable {
-
- def wrap[T, P](t: T): F[T, P]
-
- def unwrap[T, P](tp: F[T, P]): T
-
- def wrapM[T: c.WeakTypeTag, P: c.WeakTypeTag](c: blackbox.Context)(t: c.Expr[T]): c.Expr[F[T, P]]
-
- def rewrapM[T: c.WeakTypeTag, A: c.WeakTypeTag, B: c.WeakTypeTag](c: blackbox.Context)(ta: c.Expr[F[T, A]]): c.Expr[F[T, B]]
-}
-
-object Wrapper {
-
- def apply[F[_, _]](implicit w: Wrapper[F]): Wrapper[F] = w
-
- implicit def refinedWrapper: Wrapper[Refined] =
- new Wrapper[Refined] {
- override def wrap[T, P](t: T): Refined[T, P] =
- Refined.unsafeApply(t)
-
- override def unwrap[T, P](tp: Refined[T, P]): T =
- tp.get
-
- override def wrapM[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 rewrapM[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 def tagWrapper: Wrapper[@@] =
- new Wrapper[@@] {
- override def wrap[T, P](t: T): T @@ P =
- t.asInstanceOf[T @@ P]
-
- override def unwrap[T, P](tp: T @@ P): T =
- tp
-
- override def wrapM[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 rewrapM[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])
- }
-}
diff --git a/shared/src/main/scala/eu/timepit/refined/package.scala b/shared/src/main/scala/eu/timepit/refined/package.scala
index 1bfd8ca0d..649ba36a3 100644
--- a/shared/src/main/scala/eu/timepit/refined/package.scala
+++ b/shared/src/main/scala/eu/timepit/refined/package.scala
@@ -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
@@ -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
@@ -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
@@ -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]
}
diff --git a/shared/src/test/scala/eu/timepit/refined/RefTypeSpec.scala b/shared/src/test/scala/eu/timepit/refined/RefTypeSpec.scala
new file mode 100644
index 000000000..e06d92f81
--- /dev/null
+++ b/shared/src/test/scala/eu/timepit/refined/RefTypeSpec.scala
@@ -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.@@
+
+class RefTypeSpec extends Properties("RefType") {
+
+ def wrapperLaw[F[_, _]](implicit rt: RefType[F]) = forAll { (s: String) =>
+ rt.unsafeWrap(s).unwrap == s
+ }
+
+ property("wrapLaw.Refined") = wrapperLaw[Refined]
+
+ property("wrapLaw.@@") = wrapperLaw[@@]
+
+ property("mapRefine.success") = secure {
+ RefType[Refined].refineM[Positive](5).mapRefine(_.toDouble).isRight
+ }
+
+ property("mapRefine.failure") = secure {
+ RefType[Refined].refineM[Positive](5).mapRefine(_ - 10).isLeft
+ }
+}
diff --git a/shared/src/test/scala/eu/timepit/refined/WrapperSpec.scala b/shared/src/test/scala/eu/timepit/refined/WrapperSpec.scala
deleted file mode 100644
index c5a74ae71..000000000
--- a/shared/src/test/scala/eu/timepit/refined/WrapperSpec.scala
+++ /dev/null
@@ -1,16 +0,0 @@
-package eu.timepit.refined
-
-import eu.timepit.refined.internal.Wrapper
-import org.scalacheck.Prop.forAll
-import org.scalacheck.Properties
-import shapeless.tag.@@
-
-class WrapperSpec extends Properties("Wrapper") {
-
- def wrapperLaw[F[_, _]](implicit w: Wrapper[F]) = forAll { (s: String) =>
- w.unwrap(w.wrap(s)) == s
- }
-
- property("Refined") = wrapperLaw[Refined]
- property("@@") = wrapperLaw[@@]
-}