Skip to content

Commit 06125b5

Browse files
committed
Add labels to derivation framework
1 parent ba601be commit 06125b5

File tree

2 files changed

+61
-46
lines changed

2 files changed

+61
-46
lines changed

tests/run/typeclass-derivation2.check

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,5 @@ ListBuffer(0, 0, 11, 0, 22, 0, 33, 1, 0, 0, 11, 0, 22, 1, 1)
44
Cons(Cons(11,Cons(22,Cons(33,Nil))),Cons(Cons(11,Cons(22,Nil)),Nil))
55
ListBuffer(1, 2)
66
Pair(1,2)
7+
Cons(hd = 11, tl = Cons(hd = 22, tl = Cons(hd = 33, tl = Nil())))
8+
Cons(hd = Cons(hd = 11, tl = Cons(hd = 22, tl = Cons(hd = 33, tl = Nil()))), tl = Cons(hd = Cons(hd = 11, tl = Cons(hd = 22, tl = Nil())), tl = Nil()))

tests/run/typeclass-derivation2.scala

Lines changed: 59 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -4,72 +4,85 @@ import scala.annotation.tailrec
44
trait Deriving {
55
import Deriving._
66

7-
protected def caseNames: Array[String]
7+
/** A mirror of case with ordinal number `ordinal` and elements as given by `Product` */
8+
def mirror[T](ordinal: Int, product: Product): CaseMirror[T] =
9+
new CaseMirror(this, ordinal, product)
10+
11+
/** A mirror with elements given as an array */
12+
def mirror[T](ordinal: Int, elems: Array[AnyRef]): CaseMirror[T] =
13+
mirror(ordinal, new ArrayProduct(elems))
14+
15+
/** A mirror with an initial empty array of `numElems` elements, to be filled in. */
16+
def mirror[T](ordinal: Int, numElems: Int): CaseMirror[T] =
17+
mirror(ordinal, new Array[AnyRef](numElems))
18+
19+
/** A mirror of a case with no elements */
20+
def mirror[T](ordinal: Int): CaseMirror[T] =
21+
mirror(ordinal, EmptyProduct)
22+
23+
/** The case and element labels of the described ADT as encoded strings. */
24+
protected def caseLabels: Array[String]
825

926
private final val separator = '\000'
1027

11-
private def cname(ordinal: Int, idx: Int): String = {
12-
val cnames = caseNames(ordinal)
28+
private def label(ordinal: Int, idx: Int): String = {
29+
val labels = caseLabels(ordinal)
1330
@tailrec def separatorPos(from: Int): Int =
14-
if (from == cnames.length || cnames(from) == separator) from
31+
if (from == labels.length || labels(from) == separator) from
1532
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)
33+
@tailrec def findLabel(count: Int, idx: Int): String =
34+
if (idx == labels.length) ""
35+
else if (count == 0) labels.substring(idx, separatorPos(idx))
36+
else findLabel(if (labels(idx) == separator) count - 1 else count, idx + 1)
37+
findLabel(idx, 0)
2138
}
22-
23-
def caseName(ordinal: Int): String = cname(ordinal, 0)
24-
def elementName(ordinal: Int, idx: Int) = cname(ordinal, idx + 1)
2539
}
2640

2741
// Generic deriving infrastructure
2842
object Deriving {
2943

30-
/** The shape of an ADT in a sum of products representation */
31-
enum Shape {
32-
/** A sum with alternative types `Alts` */
33-
case Cases[Alts <: Tuple]
34-
35-
/** A product type `T` with element types `Elems` */
36-
case Case[T, Elems <: Tuple]
37-
}
38-
3944
/** A generic representation of a case in an ADT
45+
* @param deriving The companion object of the ADT
4046
* @param ordinal The ordinal value of the case in the list of the ADT's cases
4147
* @param elems The elements of the case
4248
*/
4349
class CaseMirror[+T](val deriving: Deriving, val ordinal: Int, val elems: Product) {
4450

45-
/** A generic case with elements given as an array */
46-
def this(deriving: Deriving, ordinal: Int, elems: Array[AnyRef]) =
47-
this(deriving, ordinal, new ArrayProduct(elems))
48-
49-
/** A generic case with an initial empty array of `numElems` elements, to be filled in. */
50-
def this(deriving: Deriving, ordinal: Int, numElems: Int) =
51-
this(deriving, ordinal, new Array[AnyRef](numElems))
52-
53-
/** A generic case with no elements */
54-
def this(deriving: Deriving, ordinal: Int) =
55-
this(deriving, ordinal, EmptyProduct)
56-
5751
/** The `n`'th element of this generic case */
5852
def apply(n: Int): Any = elems.productElement(n)
5953

60-
def caseName: String = deriving.caseName(ordinal)
61-
def elementName(idx: Int) = deriving.elementName(ordinal, idx)
54+
/** The name of the constructor of the case reflected by this mirror */
55+
def caseLabel: String = deriving.label(ordinal, 0)
56+
57+
/** The label of the `n`'th element of the case reflected by this mirror */
58+
def elementLabel(n: Int) = deriving.label(ordinal, n + 1)
6259
}
6360

6461
/** A class for mapping between an ADT value and
6562
* the case mirror that represents the value.
6663
*/
6764
abstract class Reflected[T] {
65+
66+
/** The case mirror corresponding to ADT instance `x` */
6867
def reflect(x: T): CaseMirror[T]
69-
def reify(c: CaseMirror[T]): T
68+
69+
/** The ADT instance corresponding to given `mirror` */
70+
def reify(mirror: CaseMirror[T]): T
71+
72+
/** The companion object of the ADT */
7073
def deriving: Deriving
7174
}
7275

76+
/** The shape of an ADT in a sum of products representation */
77+
enum Shape {
78+
79+
/** A sum with alternative types `Alts` */
80+
case Cases[Alts <: Tuple]
81+
82+
/** A product type `T` with element types `Elems` */
83+
case Case[T, Elems <: Tuple]
84+
}
85+
7386
/** Every generic derivation starts with a typeclass instance of this type.
7487
* It informs that type `T` has shape `S` and also allows runtime reflection on `T`.
7588
*/
@@ -108,11 +121,11 @@ object Lst extends Deriving {
108121
Shape.Case[Nil.type, Unit]
109122
)]
110123

111-
val NilMirror = new CaseMirror[Nil.type](Lst, 1)
124+
val NilMirror = mirror[Nil.type](1)
112125

113126
implicit def lstShape[T]: Shaped[Lst[T], Shape[T]] = new {
114127
def reflect(xs: Lst[T]): CaseMirror[Lst[T]] = xs match {
115-
case xs: Cons[T] => new CaseMirror[Cons[T]](Lst, 0, xs)
128+
case xs: Cons[T] => mirror[Cons[T]](0, xs)
116129
case Nil => NilMirror
117130
}
118131
def reify(c: CaseMirror[Lst[T]]): Lst[T] = c.ordinal match {
@@ -122,7 +135,7 @@ object Lst extends Deriving {
122135
def deriving = Lst
123136
}
124137

125-
protected val caseNames = Array("Cons\000hd\000tl", "Nil")
138+
protected val caseLabels = Array("Cons\000hd\000tl", "Nil")
126139

127140
// three clauses that could be generated from a `derives` clause
128141
implicit def LstEq[T: Eq]: Eq[Lst[T]] = Eq.derived
@@ -141,13 +154,13 @@ object Pair extends Deriving {
141154

142155
implicit def pairShape[T]: Shaped[Pair[T], Shape[T]] = new {
143156
def reflect(xy: Pair[T]) =
144-
new CaseMirror[Pair[T]](Pair, 0, xy)
157+
mirror[Pair[T]](0, xy)
145158
def reify(c: CaseMirror[Pair[T]]): Pair[T] =
146159
Pair(c(0).asInstanceOf, c(1).asInstanceOf)
147160
def deriving = Pair
148161
}
149162

150-
protected val caseNames = Array("Pair\000x\000y")
163+
protected val caseLabels = Array("Pair\000x\000y")
151164

152165
// two clauses that could be generated from a `derives` clause
153166
implicit def PairEq[T: Eq]: Eq[Pair[T]] = Eq.derived
@@ -263,11 +276,11 @@ object Pickler {
263276
inline def unpickleCase[T, Elems <: Tuple](r: Reflected[T], buf: mutable.ListBuffer[Int], ordinal: Int): T = {
264277
inline val size = constValue[Tuple.Size[Elems]]
265278
inline if (size == 0)
266-
r.reify(new CaseMirror[T](r.deriving, ordinal))
279+
r.reify(r.deriving.mirror[T](ordinal))
267280
else {
268281
val elems = new Array[Object](size)
269282
unpickleElems[Elems](buf, elems, 0)
270-
r.reify(new CaseMirror[T](r.deriving, ordinal, elems))
283+
r.reify(r.deriving.mirror[T](ordinal, elems))
271284
}
272285
}
273286

@@ -301,7 +314,7 @@ object Pickler {
301314
}
302315
}
303316

304-
// A third typeclass, making use of names
317+
// A third typeclass, making use of labels
305318
trait Show[T] {
306319
def show(x: T): String
307320
}
@@ -316,7 +329,7 @@ object Show {
316329
inline def showElems[Elems <: Tuple](elems: CaseMirror[_], n: Int): List[String] =
317330
inline erasedValue[Elems] match {
318331
case _: (elem *: elems1) =>
319-
val formal = elems.elementName(n)
332+
val formal = elems.elementLabel(n)
320333
val actual = tryShow[elem](elems(n).asInstanceOf)
321334
s"$formal = $actual" :: showElems[elems1](elems, n + 1)
322335
case _: Unit =>
@@ -326,7 +339,7 @@ object Show {
326339
inline def showCase[T, Elems <: Tuple](r: Reflected[T], x: T): String = {
327340
val mirror = r.reflect(x)
328341
val args = showElems[Elems](mirror, 0).mkString(", ")
329-
s"${mirror.caseName}($args)"
342+
s"${mirror.caseLabel}($args)"
330343
}
331344

332345
inline def showCases[T, Alts <: Tuple](r: Reflected[T], x: T): String =

0 commit comments

Comments
 (0)