1
1
import scala .collection .mutable
2
+ import scala .annotation .tailrec
3
+
4
+ trait Deriving {
5
+ import Deriving ._
6
+
7
+ protected def caseNames : Array [String ]
8
+
9
+ private final val separator = '\000 '
10
+
11
+ private def cname (ordinal : Int , idx : Int ): String = {
12
+ val cnames = caseNames(ordinal)
13
+ @ tailrec def separatorPos (from : Int ): Int =
14
+ if (from == cnames.length || cnames(from) == separator) from
15
+ else separatorPos(from + 1 )
16
+ @ tailrec def findName (count : Int , idx : Int ): String =
17
+ if (idx == cnames.length) " "
18
+ else if (count == 0 ) cnames.substring(idx, separatorPos(idx))
19
+ else findName(if (cnames(idx) == separator) count - 1 else count, idx + 1 )
20
+ findName(idx, 0 )
21
+ }
22
+
23
+ def caseName (ordinal : Int ): String = cname(ordinal, 0 )
24
+ def elementName (ordinal : Int , idx : Int ) = cname(ordinal, idx + 1 )
25
+ }
2
26
3
27
// Generic deriving infrastructure
4
28
object Deriving {
5
29
6
30
/** The shape of an ADT in a sum of products representation */
7
31
enum Shape {
8
-
9
32
/** A sum with alternative types `Alts` */
10
33
case Cases [Alts <: Tuple ]
11
34
@@ -17,24 +40,41 @@ object Deriving {
17
40
* @param ordinal The ordinal value of the case in the list of the ADT's cases
18
41
* @param elems The elements of the case
19
42
*/
20
- class CaseMirror [+ T ](val ordinal : Int , val elems : Product ) {
43
+ class CaseMirror [+ T ](val deriving : Deriving , val ordinal : Int , val elems : Product ) {
21
44
22
45
/** A generic case with elements given as an array */
23
- def this (ordinal : Int , elems : Array [AnyRef ]) =
24
- this (ordinal, new ArrayProduct (elems))
46
+ def this (deriving : Deriving , ordinal : Int , elems : Array [AnyRef ]) =
47
+ this (deriving, ordinal, new ArrayProduct (elems))
25
48
26
49
/** A generic case with an initial empty array of `numElems` elements, to be filled in. */
27
- def this (ordinal : Int , numElems : Int ) =
28
- this (ordinal, new Array [AnyRef ](numElems))
50
+ def this (deriving : Deriving , ordinal : Int , numElems : Int ) =
51
+ this (deriving, ordinal, new Array [AnyRef ](numElems))
29
52
30
53
/** A generic case with no elements */
31
- def this (ordinal : Int ) =
32
- this (ordinal, EmptyProduct )
54
+ def this (deriving : Deriving , ordinal : Int ) =
55
+ this (deriving, ordinal, EmptyProduct )
33
56
34
57
/** The `n`'th element of this generic case */
35
58
def apply (n : Int ): Any = elems.productElement(n)
59
+
60
+ def caseName : String = deriving.caseName(ordinal)
61
+ def elementName (idx : Int ) = deriving.elementName(ordinal, idx)
62
+ }
63
+
64
+ /** A class for mapping between an ADT value and
65
+ * the case mirror that represents the value.
66
+ */
67
+ abstract class Reflected [T ] {
68
+ def reflect (x : T ): CaseMirror [T ]
69
+ def reify (c : CaseMirror [T ]): T
70
+ def deriving : Deriving
36
71
}
37
72
73
+ /** Every generic derivation starts with a typeclass instance of this type.
74
+ * It informs that type `T` has shape `S` and also allows runtime reflection on `T`.
75
+ */
76
+ abstract class Shaped [T , S <: Shape ] extends Reflected [T ]
77
+
38
78
/** Helper class to turn arrays into products */
39
79
private class ArrayProduct (val elems : Array [AnyRef ]) extends Product {
40
80
def canEqual (that : Any ): Boolean = true
@@ -50,19 +90,6 @@ object Deriving {
50
90
def productElement (n : Int ) = throw new IndexOutOfBoundsException
51
91
def productArity = 0
52
92
}
53
-
54
- /** A class for mapping between an ADT value and
55
- * the generic case that represents the value
56
- */
57
- abstract class MirrorMapper [T ] {
58
- def toMirror (x : T ): CaseMirror [T ]
59
- def fromMirror (c : CaseMirror [T ]): T
60
- }
61
-
62
- /** Every generic derivation starts with a typeclass instance of this type.
63
- * It informs that type `T` has shape `S` and is backed by the extended generic mapper.
64
- */
65
- abstract class Shaped [T , S <: Shape ] extends MirrorMapper [T ]
66
93
}
67
94
68
95
// An algebraic datatype
@@ -72,7 +99,7 @@ enum Lst[+T] // derives Eq, Pickler
72
99
case Nil
73
100
}
74
101
75
- object Lst {
102
+ object Lst extends Deriving {
76
103
// common compiler-generated infrastructure
77
104
import Deriving ._
78
105
@@ -81,40 +108,47 @@ object Lst {
81
108
Shape .Case [Nil .type , Unit ]
82
109
)]
83
110
84
- val NilMirror = new CaseMirror [Nil .type ](1 )
111
+ val NilMirror = new CaseMirror [Nil .type ](Lst , 1 )
85
112
86
113
implicit def lstShape [T ]: Shaped [Lst [T ], Shape [T ]] = new {
87
- def toMirror (xs : Lst [T ]): CaseMirror [Lst [T ]] = xs match {
88
- case xs : Cons [T ] => new CaseMirror [Cons [T ]](0 , xs)
114
+ def reflect (xs : Lst [T ]): CaseMirror [Lst [T ]] = xs match {
115
+ case xs : Cons [T ] => new CaseMirror [Cons [T ]](Lst , 0 , xs)
89
116
case Nil => NilMirror
90
117
}
91
- def fromMirror (c : CaseMirror [Lst [T ]]): Lst [T ] = c.ordinal match {
118
+ def reify (c : CaseMirror [Lst [T ]]): Lst [T ] = c.ordinal match {
92
119
case 0 => Cons [T ](c(0 ).asInstanceOf , c(1 ).asInstanceOf )
93
120
case 1 => Nil
94
121
}
122
+ def deriving = Lst
95
123
}
96
124
97
- // two clauses that could be generated from a `derives` clause
125
+ protected val caseNames = Array (" Cons\000 hd\000 tl" , " Nil" )
126
+
127
+ // three clauses that could be generated from a `derives` clause
98
128
implicit def LstEq [T : Eq ]: Eq [Lst [T ]] = Eq .derived
99
129
implicit def LstPickler [T : Pickler ]: Pickler [Lst [T ]] = Pickler .derived
130
+ implicit def LstShow [T : Show ]: Show [Lst [T ]] = Show .derived
100
131
}
101
132
102
133
// A simple product type
103
134
case class Pair [T ](x : T , y : T ) // derives Eq, Pickler
104
135
105
- object Pair {
136
+ object Pair extends Deriving {
106
137
// common compiler-generated infrastructure
107
138
import Deriving ._
108
139
109
140
type Shape [T ] = Shape .Case [Pair [T ], (T , T )]
110
141
111
142
implicit def pairShape [T ]: Shaped [Pair [T ], Shape [T ]] = new {
112
- def toMirror (xy : Pair [T ]) =
113
- new CaseMirror [Pair [T ]](0 , xy)
114
- def fromMirror (c : CaseMirror [Pair [T ]]): Pair [T ] =
143
+ def reflect (xy : Pair [T ]) =
144
+ new CaseMirror [Pair [T ]](Pair , 0 , xy)
145
+ def reify (c : CaseMirror [Pair [T ]]): Pair [T ] =
115
146
Pair (c(0 ).asInstanceOf , c(1 ).asInstanceOf )
147
+ def deriving = Pair
116
148
}
117
149
150
+ protected val caseNames = Array (" Pair\000 x\000 y" )
151
+
118
152
// two clauses that could be generated from a `derives` clause
119
153
implicit def PairEq [T : Eq ]: Eq [Pair [T ]] = Eq .derived
120
154
implicit def PairPickler [T : Pickler ]: Pickler [Pair [T ]] = Pickler .derived
@@ -142,25 +176,25 @@ object Eq {
142
176
true
143
177
}
144
178
145
- inline def eqlCase [T , Elems <: Tuple ](mapper : MirrorMapper [T ], x : T , y : T ) =
146
- eqlElems[Elems ](mapper.toMirror (x), mapper.toMirror (y), 0 )
179
+ inline def eqlCase [T , Elems <: Tuple ](r : Reflected [T ], x : T , y : T ) =
180
+ eqlElems[Elems ](r.reflect (x), r.reflect (y), 0 )
147
181
148
- inline def eqlCases [T , Alts <: Tuple ](mapper : MirrorMapper [T ], x : T , y : T ): Boolean =
182
+ inline def eqlCases [T , Alts <: Tuple ](r : Reflected [T ], x : T , y : T ): Boolean =
149
183
inline erasedValue[Alts ] match {
150
184
case _ : (Shape .Case [alt, elems] *: alts1) =>
151
185
x match {
152
186
case x : `alt` =>
153
187
y match {
154
- case y : `alt` => eqlCase[T , elems](mapper , x, y)
188
+ case y : `alt` => eqlCase[T , elems](r , x, y)
155
189
case _ => false
156
190
}
157
- case _ => eqlCases[T , alts1](mapper , x, y)
191
+ case _ => eqlCases[T , alts1](r , x, y)
158
192
}
159
193
case _ : Unit =>
160
194
false
161
195
}
162
196
163
- inline def derived [T , S <: Shape ](implicit ev : Shaped [T , S ]) < : Eq [T ] = new {
197
+ inline def derived [T , S <: Shape ](implicit ev : Shaped [T , S ]): Eq [T ] = new {
164
198
def eql (x : T , y : T ): Boolean = inline erasedValue[S ] match {
165
199
case _ : Shape .Cases [alts] =>
166
200
eqlCases[T , alts](ev, x, y)
@@ -198,18 +232,18 @@ object Pickler {
198
232
case _ : Unit =>
199
233
}
200
234
201
- inline def pickleCase [T , Elems <: Tuple ](mapper : MirrorMapper [T ], buf : mutable.ListBuffer [Int ], x : T ): Unit =
202
- pickleElems[Elems ](buf, mapper.toMirror (x), 0 )
235
+ inline def pickleCase [T , Elems <: Tuple ](r : Reflected [T ], buf : mutable.ListBuffer [Int ], x : T ): Unit =
236
+ pickleElems[Elems ](buf, r.reflect (x), 0 )
203
237
204
- inline def pickleCases [T , Alts <: Tuple ](mapper : MirrorMapper [T ], buf : mutable.ListBuffer [Int ], x : T , n : Int ): Unit =
238
+ inline def pickleCases [T , Alts <: Tuple ](r : Reflected [T ], buf : mutable.ListBuffer [Int ], x : T , n : Int ): Unit =
205
239
inline erasedValue[Alts ] match {
206
240
case _ : (Shape .Case [alt, elems] *: alts1) =>
207
241
x match {
208
242
case x : `alt` =>
209
243
buf += n
210
- pickleCase[T , elems](mapper , buf, x)
244
+ pickleCase[T , elems](r , buf, x)
211
245
case _ =>
212
- pickleCases[T , alts1](mapper , buf, x, n + 1 )
246
+ pickleCases[T , alts1](r , buf, x, n + 1 )
213
247
}
214
248
case _ : Unit =>
215
249
}
@@ -226,22 +260,22 @@ object Pickler {
226
260
case _ : Unit =>
227
261
}
228
262
229
- inline def unpickleCase [T , Elems <: Tuple ](mapper : MirrorMapper [T ], buf : mutable.ListBuffer [Int ], ordinal : Int ): T = {
263
+ inline def unpickleCase [T , Elems <: Tuple ](r : Reflected [T ], buf : mutable.ListBuffer [Int ], ordinal : Int ): T = {
230
264
inline val size = constValue[Tuple .Size [Elems ]]
231
265
inline if (size == 0 )
232
- mapper.fromMirror (new CaseMirror [T ](ordinal))
266
+ r.reify (new CaseMirror [T ](r.deriving, ordinal))
233
267
else {
234
268
val elems = new Array [Object ](size)
235
269
unpickleElems[Elems ](buf, elems, 0 )
236
- mapper.fromMirror (new CaseMirror [T ](ordinal, elems))
270
+ r.reify (new CaseMirror [T ](r.deriving, ordinal, elems))
237
271
}
238
272
}
239
273
240
- inline def unpickleCases [T , Alts <: Tuple ](mapper : MirrorMapper [T ], buf : mutable.ListBuffer [Int ], ordinal : Int , n : Int ): T =
274
+ inline def unpickleCases [T , Alts <: Tuple ](r : Reflected [T ], buf : mutable.ListBuffer [Int ], ordinal : Int , n : Int ): T =
241
275
inline erasedValue[Alts ] match {
242
276
case _ : (Shape .Case [_, elems] *: alts1) =>
243
- if (n == ordinal) unpickleCase[T , elems](mapper , buf, ordinal)
244
- else unpickleCases[T , alts1](mapper , buf, ordinal, n + 1 )
277
+ if (n == ordinal) unpickleCase[T , elems](r , buf, ordinal)
278
+ else unpickleCases[T , alts1](r , buf, ordinal, n + 1 )
245
279
case _ =>
246
280
throw new IndexOutOfBoundsException (s " unexpected ordinal number: $ordinal" )
247
281
}
@@ -267,6 +301,59 @@ object Pickler {
267
301
}
268
302
}
269
303
304
+ // A third typeclass, making use of names
305
+ trait Show [T ] {
306
+ def show (x : T ): String
307
+ }
308
+ object Show {
309
+ import scala .typelevel ._
310
+ import Deriving ._
311
+
312
+ inline def tryShow [T ](x : T ): String = implicit match {
313
+ case s : Show [T ] => s.show(x)
314
+ }
315
+
316
+ inline def showElems [Elems <: Tuple ](elems : CaseMirror [_], n : Int ): List [String ] =
317
+ inline erasedValue[Elems ] match {
318
+ case _ : (elem *: elems1) =>
319
+ val formal = elems.elementName(n)
320
+ val actual = tryShow[elem](elems(n).asInstanceOf )
321
+ s " $formal = $actual" :: showElems[elems1](elems, n + 1 )
322
+ case _ : Unit =>
323
+ Nil
324
+ }
325
+
326
+ inline def showCase [T , Elems <: Tuple ](r : Reflected [T ], x : T ): String = {
327
+ val mirror = r.reflect(x)
328
+ val args = showElems[Elems ](mirror, 0 ).mkString(" , " )
329
+ s " ${mirror.caseName}( $args) "
330
+ }
331
+
332
+ inline def showCases [T , Alts <: Tuple ](r : Reflected [T ], x : T ): String =
333
+ inline erasedValue[Alts ] match {
334
+ case _ : (Shape .Case [alt, elems] *: alts1) =>
335
+ x match {
336
+ case x : `alt` => showCase[T , elems](r, x)
337
+ case _ => showCases[T , alts1](r, x)
338
+ }
339
+ case _ : Unit =>
340
+ throw new MatchError (x)
341
+ }
342
+
343
+ inline def derived [T , S <: Shape ](implicit ev : Shaped [T , S ]): Show [T ] = new {
344
+ def show (x : T ): String = inline erasedValue[S ] match {
345
+ case _ : Shape .Cases [alts] =>
346
+ showCases[T , alts](ev, x)
347
+ case _ : Shape .Case [_, elems] =>
348
+ showCase[T , elems](ev, x)
349
+ }
350
+ }
351
+
352
+ implicit object IntShow extends Show [Int ] {
353
+ def show (x : Int ): String = x.toString
354
+ }
355
+ }
356
+
270
357
// Tests
271
358
object Test extends App {
272
359
import Deriving ._
@@ -317,4 +404,9 @@ object Test extends App {
317
404
println(p1a)
318
405
assert(p1 == p1a)
319
406
assert(eqp.eql(p1, p1a))
407
+
408
+ def showPrintln [T : Show ](x : T ): Unit =
409
+ println(implicitly[Show [T ]].show(x))
410
+ showPrintln(xs)
411
+ showPrintln(xss)
320
412
}
0 commit comments