fthomas / refined

Refinement types for Scala
MIT License
1.71k stars 154 forks source link

Refine to Either[T Refined Not[P], T Refined P] #166

Open fthomas opened 8 years ago

fthomas commented 8 years ago

At the Typelevel Summit in Oslo @non and @dwijnand asked/talked about refining values into a coproduct of a refined type and its "negation", e.g. something like Either[T Refined Not[P], T Refined P]. I like this idea and want to explore how this can be implemented and added to refined.

fthomas commented 8 years ago

This is a simple and straightforward implementation:

--- a/core/shared/src/main/scala/eu/timepit/refined/internal/RefinePartiallyApplied.scala
+++ b/core/shared/src/main/scala/eu/timepit/refined/internal/RefinePartiallyApplied.scala
@@ -20,4 +21,10 @@ final class RefinePartiallyApplied[F[_, _], P](rt: RefType[F]) {

   def force[T](t: T)(implicit v: Validate[T, P]): F[T, P] =
     apply(t).fold(err => throw new IllegalArgumentException(err), identity)
+
+  def orNot[T](t: T)(implicit v: Validate[T, P]): Either[F[T, Not[P]], F[T, P]] =
+    v.validate(t) match {
+      case Passed(_) => Right(rt.unsafeWrap(t))
+      case Failed(_) => Left(rt.unsafeWrap(t))
+    }
 }

Usage then looks like this:

scala> refineV[Positive].orNot(3)
res1: Either[Refined[Int, Not[Positive]], Refined[Int, Positive]] = Right(3)

scala> refineV[Positive].orNot(-3)
res2: Either[Refined[Int, Not[Positive]], Refined[Int, Positive]] = Left(-3)

This is only half of the story because it won't work with type aliases like type PosInt = Int Refined Positive.

fthomas commented 8 years ago

Btw, this reminds me of Idris' Dec.