Skip to content

Commit ba601be

Browse files
committed
Handle labels
1 parent f3d9330 commit ba601be

File tree

1 file changed

+140
-48
lines changed

1 file changed

+140
-48
lines changed

tests/run/typeclass-derivation2.scala

Lines changed: 140 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,34 @@
11
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+
}
226

327
// Generic deriving infrastructure
428
object Deriving {
529

630
/** The shape of an ADT in a sum of products representation */
731
enum Shape {
8-
932
/** A sum with alternative types `Alts` */
1033
case Cases[Alts <: Tuple]
1134

@@ -17,24 +40,41 @@ object Deriving {
1740
* @param ordinal The ordinal value of the case in the list of the ADT's cases
1841
* @param elems The elements of the case
1942
*/
20-
class CaseMirror[+T](val ordinal: Int, val elems: Product) {
43+
class CaseMirror[+T](val deriving: Deriving, val ordinal: Int, val elems: Product) {
2144

2245
/** 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))
2548

2649
/** 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))
2952

3053
/** 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)
3356

3457
/** The `n`'th element of this generic case */
3558
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
3671
}
3772

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+
3878
/** Helper class to turn arrays into products */
3979
private class ArrayProduct(val elems: Array[AnyRef]) extends Product {
4080
def canEqual(that: Any): Boolean = true
@@ -50,19 +90,6 @@ object Deriving {
5090
def productElement(n: Int) = throw new IndexOutOfBoundsException
5191
def productArity = 0
5292
}
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]
6693
}
6794

6895
// An algebraic datatype
@@ -72,7 +99,7 @@ enum Lst[+T] // derives Eq, Pickler
7299
case Nil
73100
}
74101

75-
object Lst {
102+
object Lst extends Deriving {
76103
// common compiler-generated infrastructure
77104
import Deriving._
78105

@@ -81,40 +108,47 @@ object Lst {
81108
Shape.Case[Nil.type, Unit]
82109
)]
83110

84-
val NilMirror = new CaseMirror[Nil.type](1)
111+
val NilMirror = new CaseMirror[Nil.type](Lst, 1)
85112

86113
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)
89116
case Nil => NilMirror
90117
}
91-
def fromMirror(c: CaseMirror[Lst[T]]): Lst[T] = c.ordinal match {
118+
def reify(c: CaseMirror[Lst[T]]): Lst[T] = c.ordinal match {
92119
case 0 => Cons[T](c(0).asInstanceOf, c(1).asInstanceOf)
93120
case 1 => Nil
94121
}
122+
def deriving = Lst
95123
}
96124

97-
// two clauses that could be generated from a `derives` clause
125+
protected val caseNames = Array("Cons\000hd\000tl", "Nil")
126+
127+
// three clauses that could be generated from a `derives` clause
98128
implicit def LstEq[T: Eq]: Eq[Lst[T]] = Eq.derived
99129
implicit def LstPickler[T: Pickler]: Pickler[Lst[T]] = Pickler.derived
130+
implicit def LstShow[T: Show]: Show[Lst[T]] = Show.derived
100131
}
101132

102133
// A simple product type
103134
case class Pair[T](x: T, y: T) // derives Eq, Pickler
104135

105-
object Pair {
136+
object Pair extends Deriving {
106137
// common compiler-generated infrastructure
107138
import Deriving._
108139

109140
type Shape[T] = Shape.Case[Pair[T], (T, T)]
110141

111142
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] =
115146
Pair(c(0).asInstanceOf, c(1).asInstanceOf)
147+
def deriving = Pair
116148
}
117149

150+
protected val caseNames = Array("Pair\000x\000y")
151+
118152
// two clauses that could be generated from a `derives` clause
119153
implicit def PairEq[T: Eq]: Eq[Pair[T]] = Eq.derived
120154
implicit def PairPickler[T: Pickler]: Pickler[Pair[T]] = Pickler.derived
@@ -142,25 +176,25 @@ object Eq {
142176
true
143177
}
144178

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)
147181

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 =
149183
inline erasedValue[Alts] match {
150184
case _: (Shape.Case[alt, elems] *: alts1) =>
151185
x match {
152186
case x: `alt` =>
153187
y match {
154-
case y: `alt` => eqlCase[T, elems](mapper, x, y)
188+
case y: `alt` => eqlCase[T, elems](r, x, y)
155189
case _ => false
156190
}
157-
case _ => eqlCases[T, alts1](mapper, x, y)
191+
case _ => eqlCases[T, alts1](r, x, y)
158192
}
159193
case _: Unit =>
160194
false
161195
}
162196

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 {
164198
def eql(x: T, y: T): Boolean = inline erasedValue[S] match {
165199
case _: Shape.Cases[alts] =>
166200
eqlCases[T, alts](ev, x, y)
@@ -198,18 +232,18 @@ object Pickler {
198232
case _: Unit =>
199233
}
200234

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)
203237

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 =
205239
inline erasedValue[Alts] match {
206240
case _: (Shape.Case[alt, elems] *: alts1) =>
207241
x match {
208242
case x: `alt` =>
209243
buf += n
210-
pickleCase[T, elems](mapper, buf, x)
244+
pickleCase[T, elems](r, buf, x)
211245
case _ =>
212-
pickleCases[T, alts1](mapper, buf, x, n + 1)
246+
pickleCases[T, alts1](r, buf, x, n + 1)
213247
}
214248
case _: Unit =>
215249
}
@@ -226,22 +260,22 @@ object Pickler {
226260
case _: Unit =>
227261
}
228262

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 = {
230264
inline val size = constValue[Tuple.Size[Elems]]
231265
inline if (size == 0)
232-
mapper.fromMirror(new CaseMirror[T](ordinal))
266+
r.reify(new CaseMirror[T](r.deriving, ordinal))
233267
else {
234268
val elems = new Array[Object](size)
235269
unpickleElems[Elems](buf, elems, 0)
236-
mapper.fromMirror(new CaseMirror[T](ordinal, elems))
270+
r.reify(new CaseMirror[T](r.deriving, ordinal, elems))
237271
}
238272
}
239273

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 =
241275
inline erasedValue[Alts] match {
242276
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)
245279
case _ =>
246280
throw new IndexOutOfBoundsException(s"unexpected ordinal number: $ordinal")
247281
}
@@ -267,6 +301,59 @@ object Pickler {
267301
}
268302
}
269303

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+
270357
// Tests
271358
object Test extends App {
272359
import Deriving._
@@ -317,4 +404,9 @@ object Test extends App {
317404
println(p1a)
318405
assert(p1 == p1a)
319406
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)
320412
}

0 commit comments

Comments
 (0)