Skip to content

Commit

Permalink
Replace internal.Wrapper with RefType. closes #48, #53
Browse files Browse the repository at this point in the history
  • Loading branch information
fthomas committed Aug 16, 2015
1 parent 41c1778 commit 8d3c7cc
Show file tree
Hide file tree
Showing 11 changed files with 149 additions and 105 deletions.
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]
}
28 changes: 28 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,28 @@
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(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 RefTypeSpec extends Properties("RefType") {

include(new RefTypeProperties[Refined]("Refined") {})
include(new RefTypeProperties[@@]("@@") {})
}
16 changes: 0 additions & 16 deletions shared/src/test/scala/eu/timepit/refined/WrapperSpec.scala

This file was deleted.

0 comments on commit 8d3c7cc

Please sign in to comment.