/
RefType.scala
208 lines (178 loc) · 7.08 KB
/
RefType.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
package eu.timepit.refined.api
import eu.timepit.refined.internal._
import scala.reflect.macros.blackbox
import shapeless.tag.@@
/**
* Type class that allows `F` to be used as carrier type of a refinement.
* The first type parameter of `F` is the base type that is being refined
* by its second type parameter which is the type-level predicate that
* denotes the refinement. Consequently, `F[T, P]` is a phantom type
* that only contains a value of type `T`.
*
* The library provides instances of `[[RefType]]` for
* - the `[[Refined]]` value class
* - and `shapeless.tag.@@` which is a subtype of its first parameter
* (i.e. `(T @@ P) <: T`)
*/
trait RefType[F[_, _]] extends Serializable {
def unsafeWrap[T, P](t: T): F[T, P]
def unwrap[T](tp: F[T, _]): T
def unsafeRewrap[T, A, B](ta: F[T, A]): F[T, B]
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]]
/**
* Returns a value of type `T` refined as `F[T, P]` on the right if
* it satisfies the predicate `P`, or an error message on the left
* otherwise.
*
* Example: {{{
* scala> import eu.timepit.refined.api.{ Refined, RefType }
* | import eu.timepit.refined.numeric.Positive
*
* scala> RefType[Refined].refine[Positive](10)
* res0: Either[String, Refined[Int, Positive]] = Right(10)
* }}}
*
* Note: The return type is `[[internal.RefinePartiallyApplied]][F, P]`,
* which has an `apply` method on it, allowing the type `T` to be
* inferred from its argument.
*/
def refine[P]: RefinePartiallyApplied[F, P] =
new RefinePartiallyApplied(this)
/**
* Macro that returns a value of type `T` refined as `F[T, P]` if
* it satisfies the predicate `P`, or fails to compile otherwise.
*
* Example: {{{
* scala> import eu.timepit.refined.api.{ Refined, RefType }
* | import eu.timepit.refined.numeric.Positive
*
* scala> RefType[Refined].refineM[Positive](10)
* res0: Refined[Int, Positive] = 10
* }}}
*
* Note: `M` stands for '''m'''acro.
*
* Note: The return type is `[[internal.RefineMPartiallyApplied]][F, P]`,
* which has an `apply` method on it, allowing the type `T` to be
* inferred from its argument.
*/
def refineM[P]: RefineMPartiallyApplied[F, P] =
new RefineMPartiallyApplied
/**
* Macro that returns a value of type `T` refined as `F[T, P]` if
* it satisfies the predicate `P`, or fails to compile otherwise.
*
* Example: {{{
* scala> import eu.timepit.refined.api.{ Refined, RefType }
* | import eu.timepit.refined.numeric.Positive
*
* scala> RefType[Refined].refineMF[Long, Positive](10)
* res0: Refined[Long, Positive] = 10
* }}}
*
* Note: `M` stands for '''m'''acro and `F` for '''f'''ully applied.
*
* Note: The return type is `[[internal.RefineMFullyApplied]][F, T, P]`,
* which has an `apply` method on it, allowing `refineMF` to be called
* like in the given example. In contrast to `[[refineM]]`, the type
* `T` needs to be specified before `apply` can be called.
*/
def refineMF[T, P]: RefineMFullyApplied[F, T, P] =
new RefineMFullyApplied
def mapRefine[T, P, U](tp: F[T, P])(f: T => U)(
implicit v: Validate[U, P]): Either[String, F[U, P]] =
refine(f(unwrap(tp)))
def coflatMapRefine[T, P, U](tp: F[T, P])(f: F[T, P] => U)(
implicit v: Validate[U, P]): Either[String, F[U, P]] =
refine(f(tp))
}
object RefType {
/** Returns a `RefType` for the given type `F` from the implicit scope. */
def apply[F[_, _]](implicit rt: RefType[F]): RefType[F] = rt
/**
* Returns a value of type `T` refined as `FTP` on the right if it
* satisfies the predicate in `FTP`, or an error message on the left
* otherwise.
*
* Example: {{{
* scala> import eu.timepit.refined.api.{ Refined, RefType }
* | import eu.timepit.refined.numeric.Positive
*
* scala> type PosInt = Int Refined Positive
* scala> RefType.applyRef[PosInt](10)
* res0: Either[String, PosInt] = Right(10)
* }}}
*
* Note: The return type is `[[internal.ApplyRefPartiallyApplied]][FTP]`,
* which has an `apply` method on it, allowing `applyRef` to be called
* like in the given example.
*/
def applyRef[FTP]: ApplyRefPartiallyApplied[FTP] =
new ApplyRefPartiallyApplied
/**
* Macro that returns a value of type `T` refined as `FTP` if it
* satisfies the predicate in `FTP`, or fails to compile otherwise.
*
* Example: {{{
* scala> import eu.timepit.refined.api.{ Refined, RefType }
* | import eu.timepit.refined.numeric.Positive
*
* scala> type PosInt = Int Refined Positive
* scala> RefType.applyRefM[PosInt](10)
* res0: PosInt = 10
* }}}
*
* Note: `M` stands for '''m'''acro.
*
* Note: The return type is `[[internal.ApplyRefMPartiallyApplied]][FTP]`,
* which has an `apply` method on it, allowing `applyRefM` to be called
* like in the given example.
*/
def applyRefM[FTP]: ApplyRefMPartiallyApplied[FTP] =
new ApplyRefMPartiallyApplied
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](tp: Refined[T, _]): T =
tp.value
override def unsafeRewrap[T, A, B](ta: Refined[T, A]): Refined[T, B] =
Refined.unsafeApply(ta.value)
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.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(Refined.unsafeApply(ta.splice.value))
}
implicit val tagRefType: RefType[@@] =
new RefType[@@] {
override def unsafeWrap[T, P](t: T): T @@ P =
t.asInstanceOf[T @@ P]
override def unwrap[T](tp: T @@ _): T =
tp
override def unsafeRewrap[T, A, B](ta: T @@ A): T @@ B =
ta.asInstanceOf[T @@ B]
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 v: Validate[U, P]): Either[String, F[U, P]] =
F.mapRefine(tp)(f)
def coflatMapRefine[U](f: F[T, P] => U)(implicit v: Validate[U, P]): Either[String, F[U, P]] =
F.coflatMapRefine(tp)(f)
}
object ops {
implicit def toRefTypeOps[F[_, _]: RefType, T, P](tp: F[T, P]): RefTypeOps[F, T, P] =
new RefTypeOps(tp)
}
}