@@ -3,18 +3,65 @@ import scala.collection.mutable
3
3
// Generic deriving infrastructure
4
4
object Deriving {
5
5
6
+ /** The shape of an ADT in a sum of products representation */
6
7
enum Shape {
8
+
9
+ /** A sum with alternative types `Alts` */
7
10
case Cases [Alts <: Tuple ]
11
+
12
+ /** A product type `T` with element types `Elems` */
8
13
case Case [T , Elems <: Tuple ]
9
14
}
10
15
11
- case class GenericCase [+ T ](ordinal : Int , elems : Array [Object ])
16
+ /** A generic representation of a case in an ADT
17
+ * @param ordinal The ordinal value of the case in the list of the ADT's cases
18
+ * @param elems The elements of the case
19
+ */
20
+ class GenericCase [+ T ](val ordinal : Int , val elems : Product ) {
21
+
22
+ /** A generic case with elements given as an array */
23
+ def this (ordinal : Int , elems : Array [AnyRef ]) =
24
+ this (ordinal, new ArrayProduct (elems))
25
+
26
+ /** 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))
29
+
30
+ /** A generic case with no elements */
31
+ def this (ordinal : Int ) =
32
+ this (ordinal, EmptyProduct )
33
+
34
+ /** The `n`'th element of this generic case */
35
+ def apply (n : Int ): Any = elems.productElement(n)
36
+ }
37
+
38
+ /** Helper class to turn arrays into products */
39
+ private class ArrayProduct (val elems : Array [AnyRef ]) extends Product {
40
+ def canEqual (that : Any ): Boolean = true
41
+ def productElement (n : Int ) = elems(n)
42
+ def productArity = elems.length
43
+ override def productIterator : Iterator [Any ] = elems.iterator
44
+ def update (n : Int , x : Any ) = elems(n) = x.asInstanceOf [AnyRef ]
45
+ }
46
+
47
+ /** Helper object */
48
+ private object EmptyProduct extends Product {
49
+ def canEqual (that : Any ): Boolean = true
50
+ def productElement (n : Int ) = throw new IndexOutOfBoundsException
51
+ def productArity = 0
52
+ }
12
53
54
+ /** A class for mapping between an ADT value and
55
+ * the generic case that represents the value
56
+ */
13
57
abstract class GenericMapper [T ] {
14
58
def toGenericCase (x : T ): GenericCase [T ]
15
59
def fromGenericCase (c : GenericCase [T ]): T
16
60
}
17
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
+ */
18
65
abstract class HasShape [T , S <: Shape ] extends GenericMapper [T ]
19
66
}
20
67
@@ -34,13 +81,15 @@ object Lst {
34
81
Shape .Case [Nil .type , Unit ]
35
82
)]
36
83
84
+ val NilCase = new GenericCase [Nil .type ](1 )
85
+
37
86
implicit def lstShape [T ]: HasShape [Lst [T ], LstShape [T ]] = new {
38
87
def toGenericCase (xs : Lst [T ]): GenericCase [Lst [T ]] = xs match {
39
- case Cons (x, xs1) => GenericCase [Cons [T ]](0 , Array (x. asInstanceOf , xs1) )
40
- case Nil => GenericCase [ Nil . type ]( 1 , Array ())
41
- }
88
+ case xs : Cons [ T ] => new GenericCase [Cons [T ]](0 , xs )
89
+ case Nil => NilCase
90
+ }
42
91
def fromGenericCase (c : GenericCase [Lst [T ]]): Lst [T ] = c.ordinal match {
43
- case 0 => Cons [T ](c.elems (0 ).asInstanceOf , c.elems (1 ).asInstanceOf )
92
+ case 0 => Cons [T ](c(0 ).asInstanceOf , c(1 ).asInstanceOf )
44
93
case 1 => Nil
45
94
}
46
95
}
@@ -61,9 +110,9 @@ object Pair {
61
110
62
111
implicit def pairShape [T ]: HasShape [Pair [T ], PairShape [T ]] = new {
63
112
def toGenericCase (xy : Pair [T ]) =
64
- GenericCase [Pair [T ]](0 , Array (xy._1. asInstanceOf , xy._2. asInstanceOf ) )
113
+ new GenericCase [Pair [T ]](0 , xy )
65
114
def fromGenericCase (c : GenericCase [Pair [T ]]): Pair [T ] =
66
- Pair (c.elems (0 ).asInstanceOf , c.elems (1 ).asInstanceOf )
115
+ Pair (c(0 ).asInstanceOf , c(1 ).asInstanceOf )
67
116
}
68
117
69
118
// two clauses that could be generated from a `derives` clause
@@ -84,7 +133,7 @@ object Eq {
84
133
case eq : Eq [T ] => eq.eql(x, y)
85
134
}
86
135
87
- inline def eqlElems [Elems <: Tuple ](xs : Array [ Object ], ys : Array [ Object ], n : Int ): Boolean =
136
+ inline def eqlElems [Elems <: Tuple ](xs : GenericCase [_ ], ys : GenericCase [_ ], n : Int ): Boolean =
88
137
inline erasedValue[Elems ] match {
89
138
case _ : (elem *: elems1) =>
90
139
tryEql[elem](xs(n).asInstanceOf , ys(n).asInstanceOf ) &&
@@ -94,7 +143,7 @@ object Eq {
94
143
}
95
144
96
145
inline def eqlCase [T , Elems <: Tuple ](mapper : GenericMapper [T ], x : T , y : T ) =
97
- eqlElems[Elems ](mapper.toGenericCase(x).elems , mapper.toGenericCase(y).elems , 0 )
146
+ eqlElems[Elems ](mapper.toGenericCase(x), mapper.toGenericCase(y), 0 )
98
147
99
148
inline def eqlCases [T , Alts <: Tuple ](mapper : GenericMapper [T ], x : T , y : T ): Boolean =
100
149
inline erasedValue[Alts ] match {
@@ -141,7 +190,7 @@ object Pickler {
141
190
case pkl : Pickler [T ] => pkl.pickle(buf, x)
142
191
}
143
192
144
- inline def pickleElems [Elems <: Tuple ](buf : mutable.ListBuffer [Int ], elems : Array [ AnyRef ], n : Int ): Unit =
193
+ inline def pickleElems [Elems <: Tuple ](buf : mutable.ListBuffer [Int ], elems : GenericCase [_ ], n : Int ): Unit =
145
194
inline erasedValue[Elems ] match {
146
195
case _ : (elem *: elems1) =>
147
196
tryPickle[elem](buf, elems(n).asInstanceOf [elem])
@@ -150,7 +199,7 @@ object Pickler {
150
199
}
151
200
152
201
inline def pickleCase [T , Elems <: Tuple ](mapper : GenericMapper [T ], buf : mutable.ListBuffer [Int ], x : T ): Unit =
153
- pickleElems[Elems ](buf, mapper.toGenericCase(x).elems , 0 )
202
+ pickleElems[Elems ](buf, mapper.toGenericCase(x), 0 )
154
203
155
204
inline def pickleCases [T , Alts <: Tuple ](mapper : GenericMapper [T ], buf : mutable.ListBuffer [Int ], x : T , n : Int ): Unit =
156
205
inline erasedValue[Alts ] match {
@@ -178,9 +227,14 @@ object Pickler {
178
227
}
179
228
180
229
inline def unpickleCase [T , Elems <: Tuple ](mapper : GenericMapper [T ], buf : mutable.ListBuffer [Int ], ordinal : Int ): T = {
181
- val elems = new Array [Object ](constValue[Tuple .Size [Elems ]])
182
- unpickleElems[Elems ](buf, elems, 0 )
183
- mapper.fromGenericCase(GenericCase (ordinal, elems))
230
+ inline val size = constValue[Tuple .Size [Elems ]]
231
+ inline if (size == 0 )
232
+ mapper.fromGenericCase(new GenericCase [T ](ordinal))
233
+ else {
234
+ val elems = new Array [Object ](size)
235
+ unpickleElems[Elems ](buf, elems, 0 )
236
+ mapper.fromGenericCase(new GenericCase [T ](ordinal, elems))
237
+ }
184
238
}
185
239
186
240
inline def unpickleCases [T , Alts <: Tuple ](mapper : GenericMapper [T ], buf : mutable.ListBuffer [Int ], ordinal : Int , n : Int ): T =
0 commit comments