1
+ import scala .collection .mutable
2
+ import scala .annotation .tailrec
3
+
4
+ trait Deriving {
5
+ import Deriving ._
6
+
7
+ /** A mirror of case with ordinal number `ordinal` and elements as given by `Product` */
8
+ def mirror (ordinal : Int , product : Product ): Mirror =
9
+ new Mirror (this , ordinal, product)
10
+
11
+ /** A mirror with elements given as an array */
12
+ def mirror (ordinal : Int , elems : Array [AnyRef ]): Mirror =
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 (ordinal : Int , numElems : Int ): Mirror =
17
+ mirror(ordinal, new Array [AnyRef ](numElems))
18
+
19
+ /** A mirror of a case with no elements */
20
+ def mirror (ordinal : Int ): Mirror =
21
+ mirror(ordinal, EmptyProduct )
22
+
23
+ /** The case and element labels of the described ADT as encoded strings. */
24
+ protected def caseLabels : Array [String ]
25
+
26
+ private final val separator = '\000 '
27
+
28
+ private def label (ordinal : Int , idx : Int ): String = {
29
+ val labels = caseLabels(ordinal)
30
+ @ tailrec def separatorPos (from : Int ): Int =
31
+ if (from == labels.length || labels(from) == separator) from
32
+ else separatorPos(from + 1 )
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 )
38
+ }
39
+ }
40
+
41
+ // Generic deriving infrastructure
42
+ object Deriving {
43
+
44
+ /** A generic representation of a case in an ADT
45
+ * @param deriving The companion object of the ADT
46
+ * @param ordinal The ordinal value of the case in the list of the ADT's cases
47
+ * @param elems The elements of the case
48
+ */
49
+ class Mirror (val deriving : Deriving , val ordinal : Int , val elems : Product ) {
50
+
51
+ /** The `n`'th element of this generic case */
52
+ def apply (n : Int ): Any = elems.productElement(n)
53
+
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 )
59
+ }
60
+
61
+ /** A class for mapping between an ADT value and
62
+ * the case mirror that represents the value.
63
+ */
64
+ abstract class Reflected [T ] {
65
+
66
+ /** The case mirror corresponding to ADT instance `x` */
67
+ def reflect (x : T ): Mirror
68
+
69
+ /** The ADT instance corresponding to given `mirror` */
70
+ def reify (mirror : Mirror ): T
71
+
72
+ /** The companion object of the ADT */
73
+ def deriving : Deriving
74
+ }
75
+
76
+ /** The shape of an ADT.
77
+ * This is eithe a product (`Case`) or a sum (`Cases`) of products.
78
+ */
79
+ enum Shape {
80
+
81
+ /** A sum with alternative types `Alts` */
82
+ case Cases [Alts <: Tuple ]
83
+
84
+ /** A product type `T` with element types `Elems` */
85
+ case Case [T , Elems <: Tuple ]
86
+ }
87
+
88
+ /** Every generic derivation starts with a typeclass instance of this type.
89
+ * It informs that type `T` has shape `S` and also implements runtime reflection on `T`.
90
+ */
91
+ abstract class Shaped [T , S <: Shape ] extends Reflected [T ]
92
+
93
+ /** Helper class to turn arrays into products */
94
+ private class ArrayProduct (val elems : Array [AnyRef ]) extends Product {
95
+ def canEqual (that : Any ): Boolean = true
96
+ def productElement (n : Int ) = elems(n)
97
+ def productArity = elems.length
98
+ override def productIterator : Iterator [Any ] = elems.iterator
99
+ def update (n : Int , x : Any ) = elems(n) = x.asInstanceOf [AnyRef ]
100
+ }
101
+
102
+ /** Helper object */
103
+ private object EmptyProduct extends Product {
104
+ def canEqual (that : Any ): Boolean = true
105
+ def productElement (n : Int ) = throw new IndexOutOfBoundsException
106
+ def productArity = 0
107
+ }
108
+ }
109
+
110
+ // An algebraic datatype
111
+ enum Lst [+ T ] {
112
+ case Cons (hd : T , tl : Lst [T ])
113
+ case Nil
114
+ }
115
+
116
+ object Lst extends Deriving {
117
+ // common compiler-generated infrastructure
118
+ import Deriving ._
119
+
120
+ type Shape [T ] = Shape .Cases [(
121
+ Shape .Case [Cons [T ], (T , Lst [T ])],
122
+ Shape .Case [Nil .type , Unit ]
123
+ )]
124
+
125
+ val NilMirror = mirror(1 )
126
+
127
+ implicit def lstShape [T ]: Shaped [Lst [T ], Shape [T ]] = new {
128
+ def reflect (xs : Lst [T ]): Mirror = xs match {
129
+ case xs : Cons [T ] => mirror(0 , xs)
130
+ case Nil => NilMirror
131
+ }
132
+ def reify (c : Mirror ): Lst [T ] = c.ordinal match {
133
+ case 0 => Cons [T ](c(0 ).asInstanceOf , c(1 ).asInstanceOf )
134
+ case 1 => Nil
135
+ }
136
+ def deriving = Lst
137
+ }
138
+
139
+ protected val caseLabels = Array (" Cons\000 hd\000 tl" , " Nil" )
140
+
141
+ // three clauses that could be generated from a `derives` clause
142
+ implicit def LstShow [T : Show ]: Show [Lst [T ]] = Show .derived
143
+ }
144
+
145
+ // A simple product type
146
+ case class Pair [T ](x : T , y : T )
147
+
148
+ object Pair extends Deriving {
149
+ // common compiler-generated infrastructure
150
+ import Deriving ._
151
+
152
+ type Shape [T ] = Shape .Case [Pair [T ], (T , T )]
153
+
154
+ implicit def pairShape [T ]: Shaped [Pair [T ], Shape [T ]] = new {
155
+ def reflect (xy : Pair [T ]) =
156
+ mirror(0 , xy)
157
+ def reify (c : Mirror ): Pair [T ] =
158
+ Pair (c(0 ).asInstanceOf , c(1 ).asInstanceOf )
159
+ def deriving = Pair
160
+ }
161
+
162
+ protected val caseLabels = Array (" Pair\000 x\000 y" )
163
+ }
164
+
165
+ sealed trait Either [+ L , + R ] extends Product derives Eq , Pickler
166
+ case class Left [L ](x : L ) extends Either [L , Nothing ]
167
+ case class Right [R ](x : R ) extends Either [Nothing , R ]
168
+
169
+ object Either extends Deriving {
170
+ import Deriving ._
171
+
172
+ type Shape [L , R ] = Shape .Cases [(
173
+ Shape .Case [Left [L ], L *: Unit ],
174
+ Shape .Case [Right [R ], R *: Unit ]
175
+ )]
176
+
177
+ implicit def eitherShape [L , R ]: Shaped [Either [L , R ], Shape [L , R ]] = new {
178
+ def reflect (e : Either [L , R ]): Mirror = e match {
179
+ case e : Left [L ] => mirror(0 , e)
180
+ case e : Right [R ] => mirror(1 , e)
181
+ }
182
+ def reify (c : Mirror ): Either [L , R ] = c.ordinal match {
183
+ case 0 => Left [L ](c(0 ).asInstanceOf )
184
+ case 1 => Right [R ](c(0 ).asInstanceOf )
185
+ }
186
+ def deriving = Either
187
+ }
188
+
189
+ protected val caseLabels = Array (" Left\000 x" , " Right\000 x" )
190
+
191
+ implicit def EitherShow [L : Show , R : Show ]: Show [Either [L , R ]] = Show .derived
192
+ }
193
+
194
+ trait Show [T ] {
195
+ def show (x : T ): String
196
+ }
197
+ object Show {
198
+ import scala .typelevel ._
199
+ import Deriving ._
200
+
201
+ inline def tryShow [T ](x : T ): String = implicit match {
202
+ case s : Show [T ] => s.show(x)
203
+ }
204
+
205
+ inline def showElems [Elems <: Tuple ](elems : Mirror , n : Int ): List [String ] =
206
+ inline erasedValue[Elems ] match {
207
+ case _ : (elem *: elems1) =>
208
+ val formal = elems.elementLabel(n)
209
+ val actual = tryShow[elem](elems(n).asInstanceOf )
210
+ s " $formal = $actual" :: showElems[elems1](elems, n + 1 )
211
+ case _ : Unit =>
212
+ Nil
213
+ }
214
+
215
+ inline def showCase [T , Elems <: Tuple ](r : Reflected [T ], x : T ): String = {
216
+ val mirror = r.reflect(x)
217
+ val args = showElems[Elems ](mirror, 0 ).mkString(" , " )
218
+ s " ${mirror.caseLabel}( $args) "
219
+ }
220
+
221
+ inline def showCases [T , Alts <: Tuple ](r : Reflected [T ], x : T ): String =
222
+ inline erasedValue[Alts ] match {
223
+ case _ : (Shape .Case [alt, elems] *: alts1) =>
224
+ x match {
225
+ case x : `alt` => showCase[T , elems](r, x)
226
+ case _ => showCases[T , alts1](r, x)
227
+ }
228
+ case _ : Unit =>
229
+ throw new MatchError (x)
230
+ }
231
+
232
+ inline def derived [T , S <: Shape ](implicit ev : Shaped [T , S ]): Show [T ] = new {
233
+ def show (x : T ): String = inline erasedValue[S ] match {
234
+ case _ : Shape .Cases [alts] =>
235
+ showCases[T , alts](ev, x)
236
+ case _ : Shape .Case [_, elems] =>
237
+ showCase[T , elems](ev, x)
238
+ }
239
+ }
240
+
241
+ implicit object IntShow extends Show [Int ] {
242
+ def show (x : Int ): String = x.toString
243
+ }
244
+ }
245
+
246
+ // Tests
247
+ object Test extends App {
248
+ import Deriving ._
249
+
250
+ def showPrintln [T : Show ](x : T ): Unit =
251
+ println(implicitly[Show [T ]].show(x))
252
+
253
+ val zs = Lst .Cons (Left (1 ), Lst .Cons (Right (Pair (2 , 3 )), Lst .Nil ))
254
+ showPrintln(zs) // error
255
+ /* This should print as follows:
256
+ -- Error: typeclass-derivation2.scala:254:17 -----------------------------------
257
+ 254 | showPrintln(zs) // error
258
+ | ^
259
+ |no implicit argument of type Show[Lst[Either[Int, Pair[Int]]]] was found for parameter evidence$5 of method showPrintln in object Test.
260
+ |I found:
261
+ |
262
+ | Lst.LstShow[T](
263
+ | Either.EitherShow[Int, Pair[Int]](Show.IntShow,
264
+ | /* missing */ implicitly[Show[Pair[Int]]]
265
+ | )
266
+ | )
267
+ |
268
+ |But no implicit values were found that match type Show[Pair[Int]].
269
+ */
270
+ }
0 commit comments