-
Notifications
You must be signed in to change notification settings - Fork 1.1k
/
Copy pathTuple.scala
341 lines (281 loc) · 11.7 KB
/
Tuple.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
package scala
import annotation.showAsInfix
import compiletime.*
import compiletime.ops.int.*
/** Tuple of arbitrary arity */
sealed trait Tuple extends Product {
import Tuple.*
/** Create a copy of this tuple as an Array */
inline def toArray: Array[Object] =
runtime.Tuples.toArray(this)
/** Create a copy of this tuple as a List */
inline def toList: List[Union[this.type]] =
this.productIterator.toList
.asInstanceOf[List[Union[this.type]]]
/** Create a copy of this tuple as an IArray */
inline def toIArray: IArray[Object] =
runtime.Tuples.toIArray(this)
/** Return a copy of `this` tuple with an element appended */
inline def :* [This >: this.type <: Tuple, L] (x: L): This :* L =
runtime.Tuples.append(x, this).asInstanceOf[This :* L]
/** Return a new tuple by prepending the element to `this` tuple.
* This operation is O(this.size)
*/
inline def *: [H, This >: this.type <: Tuple] (x: H): H *: This =
runtime.Tuples.cons(x, this).asInstanceOf[H *: This]
/** Get the i-th element of this tuple.
* Equivalent to productElement but with a precise return type.
*/
inline def apply[This >: this.type <: Tuple](n: Int): Elem[This, n.type] =
runtime.Tuples.apply(this, n).asInstanceOf[Elem[This, n.type]]
/** Get the head of this tuple */
inline def head[This >: this.type <: Tuple]: Head[This] =
runtime.Tuples.apply(this, 0).asInstanceOf[Head[This]]
/** Get the initial part of the tuple without its last element */
inline def init[This >: this.type <: Tuple]: Init[This] =
runtime.Tuples.init(this).asInstanceOf[Init[This]]
/** Get the last of this tuple */
inline def last[This >: this.type <: Tuple]: Last[This] =
runtime.Tuples.last(this).asInstanceOf[Last[This]]
/** Get the tail of this tuple.
* This operation is O(this.size)
*/
inline def tail[This >: this.type <: Tuple]: Tail[This] =
runtime.Tuples.tail(this).asInstanceOf[Tail[This]]
/** Return a new tuple by concatenating `this` tuple with `that` tuple.
* This operation is O(this.size + that.size)
*/
inline def ++ [This >: this.type <: Tuple](that: Tuple): This ++ that.type =
runtime.Tuples.concat(this, that).asInstanceOf[This ++ that.type]
/** Return the size (or arity) of the tuple */
inline def size[This >: this.type <: Tuple]: Size[This] =
runtime.Tuples.size(this).asInstanceOf[Size[This]]
/** Given two tuples, `(a1, ..., an)` and `(b1, ..., bn)`, returns a tuple
* `((a1, b1), ..., (an, bn))`. If the two tuples have different sizes,
* the extra elements of the larger tuple will be disregarded.
* The result is typed as `((A1, B1), ..., (An, Bn))` if at least one of the
* tuple types has a `EmptyTuple` tail. Otherwise the result type is
* `(A1, B1) *: ... *: (Ai, Bi) *: Tuple`
*/
inline def zip[This >: this.type <: Tuple, T2 <: Tuple](t2: T2): Zip[This, T2] =
runtime.Tuples.zip(this, t2).asInstanceOf[Zip[This, T2]]
/** Called on a tuple `(a1, ..., an)`, returns a new tuple `(f(a1), ..., f(an))`.
* The result is typed as `(F[A1], ..., F[An])` if the tuple type is fully known.
* If the tuple is of the form `a1 *: ... *: Tuple` (that is, the tail is not known
* to be the cons type.
*/
inline def map[F[_]](f: [t] => t => F[t]): Map[this.type, F] =
runtime.Tuples.map(this, f).asInstanceOf[Map[this.type, F]]
/** Given a tuple `(a1, ..., am)`, returns the tuple `(a1, ..., an)` consisting
* of its first n elements.
*/
inline def take[This >: this.type <: Tuple](n: Int): Take[This, n.type] =
runtime.Tuples.take(this, n).asInstanceOf[Take[This, n.type]]
/** Given a tuple `(a1, ..., am)`, returns the tuple `(an+1, ..., am)` consisting
* all its elements except the first n ones.
*/
inline def drop[This >: this.type <: Tuple](n: Int): Drop[This, n.type] =
runtime.Tuples.drop(this, n).asInstanceOf[Drop[This, n.type]]
/** Given a tuple `(a1, ..., am)`, returns a pair of the tuple `(a1, ..., an)`
* consisting of the first n elements, and the tuple `(an+1, ..., am)` consisting
* of the remaining elements.
*/
inline def splitAt[This >: this.type <: Tuple](n: Int): Split[This, n.type] =
runtime.Tuples.splitAt(this, n).asInstanceOf[Split[This, n.type]]
/** Given a tuple `(a1, ..., am)`, returns the reversed tuple `(am, ..., a1)`
* consisting all its elements.
*/
inline def reverse[This >: this.type <: Tuple]: Reverse[This] =
runtime.Tuples.reverse(this).asInstanceOf[Reverse[This]]
}
object Tuple {
/** Type of a tuple with an element appended */
type Append[X <: Tuple, Y] <: NonEmptyTuple = X match {
case EmptyTuple => Y *: EmptyTuple
case x *: xs => x *: Append[xs, Y]
}
/** An infix shorthand for `Append[X, Y]` */
infix type :*[X <: Tuple, Y] = Append[X, Y]
/** Type of the head of a tuple */
type Head[X <: Tuple] = X match {
case x *: _ => x
}
/** Type of the initial part of the tuple without its last element */
type Init[X <: Tuple] <: Tuple = X match {
case _ *: EmptyTuple => EmptyTuple
case x *: xs =>
x *: Init[xs]
}
/** Type of the tail of a tuple */
type Tail[X <: Tuple] <: Tuple = X match {
case _ *: xs => xs
}
/** Type of the last element of a tuple */
type Last[X <: Tuple] = X match {
case x *: EmptyTuple => x
case _ *: xs => Last[xs]
}
/** Type of the concatenation of two tuples */
type Concat[X <: Tuple, +Y <: Tuple] <: Tuple = X match {
case EmptyTuple => Y
case x1 *: xs1 => x1 *: Concat[xs1, Y]
}
/** An infix shorthand for `Concat[X, Y]` */
infix type ++[X <: Tuple, +Y <: Tuple] = Concat[X, Y]
/** Type of the element at position N in the tuple X */
type Elem[X <: Tuple, N <: Int] = X match {
case x *: xs =>
N match {
case 0 => x
case S[n1] => Elem[xs, n1]
}
}
/** Literal constant Int size of a tuple */
type Size[X <: Tuple] <: Int = X match {
case EmptyTuple => 0
case x *: xs => S[Size[xs]]
}
/** Fold a tuple `(T1, ..., Tn)` into `F[T1, F[... F[Tn, Z]...]]]` */
type Fold[Tup <: Tuple, Z, F[_, _]] = Tup match
case EmptyTuple => Z
case h *: t => F[h, Fold[t, Z, F]]
/** Converts a tuple `(T1, ..., Tn)` to `(F[T1], ..., F[Tn])` */
type Map[Tup <: Tuple, F[_ <: Union[Tup]]] <: Tuple = Tup match {
case EmptyTuple => EmptyTuple
case h *: t => F[h] *: Map[t, F]
}
/** Converts a tuple `(T1, ..., Tn)` to a flattened `(..F[T1], ..., ..F[Tn])` */
type FlatMap[Tup <: Tuple, F[_ <: Union[Tup]] <: Tuple] <: Tuple = Tup match {
case EmptyTuple => EmptyTuple
case h *: t => Concat[F[h], FlatMap[t, F]]
}
/** Filters out those members of the tuple for which the predicate `P` returns `false`.
* A predicate `P[X]` is a type that can be either `true` or `false`. For example:
* ```scala
* type IsString[x] <: Boolean = x match {
* case String => true
* case _ => false
* }
* summon[Tuple.Filter[(1, "foo", 2, "bar"), IsString] =:= ("foo", "bar")]
* ```
* @syntax markdown
*/
type Filter[Tup <: Tuple, P[_ <: Union[Tup]] <: Boolean] <: Tuple = Tup match {
case EmptyTuple => EmptyTuple
case h *: t => P[h] match {
case true => h *: Filter[t, P]
case false => Filter[t, P]
}
}
/** Given two tuples, `A1 *: ... *: An * At` and `B1 *: ... *: Bn *: Bt`
* where at least one of `At` or `Bt` is `EmptyTuple`,
* returns the tuple type `(A1, B1) *: ... *: (An, Bn) *: EmptyTuple`.
*/
type Zip[T1 <: Tuple, T2 <: Tuple] <: Tuple = (T1, T2) match {
case (h1 *: t1, h2 *: t2) => (h1, h2) *: Zip[t1, t2]
case _ => EmptyTuple
}
/** Converts a tuple `(F[T1], ..., F[Tn])` to `(T1, ... Tn)` */
type InverseMap[X <: Tuple, F[_]] <: Tuple = X match {
case F[x] *: t => x *: InverseMap[t, F]
case EmptyTuple => EmptyTuple
}
/** Implicit evidence. IsMappedBy[F][X] is present in the implicit scope iff
* X is a tuple for which each element's type is constructed via `F`. E.g.
* (F[A1], ..., F[An]), but not `(F[A1], B2, ..., F[An])` where B2 does not
* have the shape of `F[A]`.
*/
type IsMappedBy[F[_]] = [X <: Tuple] =>> X =:= Map[InverseMap[X, F], F]
/** Type of the reversed tuple */
type Reverse[X <: Tuple] = ReverseOnto[X, EmptyTuple]
/** Prepends all elements of a tuple in reverse order onto the other tuple */
type ReverseOnto[From <: Tuple, +To <: Tuple] <: Tuple = From match
case x *: xs => ReverseOnto[xs, x *: To]
case EmptyTuple => To
/** Transforms a tuple `(T1, ..., Tn)` into `(T1, ..., Ti)`. */
type Take[T <: Tuple, N <: Int] <: Tuple = N match {
case 0 => EmptyTuple
case S[n1] => T match {
case EmptyTuple => EmptyTuple
case x *: xs => x *: Take[xs, n1]
}
}
/** Transforms a tuple `(T1, ..., Tn)` into `(Ti+1, ..., Tn)`. */
type Drop[T <: Tuple, N <: Int] <: Tuple = N match {
case 0 => T
case S[n1] => T match {
case EmptyTuple => EmptyTuple
case x *: xs => Drop[xs, n1]
}
}
/** Splits a tuple (T1, ..., Tn) into a pair of two tuples `(T1, ..., Ti)` and
* `(Ti+1, ..., Tn)`.
*/
type Split[T <: Tuple, N <: Int] = (Take[T, N], Drop[T, N])
/** Given a tuple `(T1, ..., Tn)`, returns a union of its
* member types: `T1 | ... | Tn`. Returns `Nothing` if the tuple is empty.
*/
type Union[T <: Tuple] = Fold[T, Nothing, [x, y] =>> x | y]
/** A type level Boolean indicating whether the tuple `X` has an element
* that matches `Y`.
* @pre The elements of `X` are assumed to be singleton types
*/
type Contains[X <: Tuple, Y] <: Boolean = X match
case Y *: _ => true
case _ *: xs => Contains[xs, Y]
case EmptyTuple => false
/** A type level Boolean indicating whether the type `Y` contains
* none of the elements of `X`.
* @pre The elements of `X` and `Y` are assumed to be singleton types
*/
type Disjoint[X <: Tuple, Y <: Tuple] <: Boolean = X match
case x *: xs => Contains[Y, x] match
case true => false
case false => Disjoint[xs, Y]
case EmptyTuple => true
/** Empty tuple */
def apply(): EmptyTuple = EmptyTuple
/** Tuple with one element */
def apply[T](x: T): T *: EmptyTuple = Tuple1(x)
/** Matches an empty tuple. */
def unapply(x: EmptyTuple): true = true
/** Convert an array into a tuple of unknown arity and types */
def fromArray[T](xs: Array[T]): Tuple = {
val xs2 = xs match {
case xs: Array[Object] => xs
case xs => xs.map(_.asInstanceOf[Object])
}
runtime.Tuples.fromArray(xs2)
}
/** Convert an immutable array into a tuple of unknown arity and types */
def fromIArray[T](xs: IArray[T]): Tuple = {
val xs2: IArray[Object] = xs match {
case xs: IArray[Object] @unchecked => xs
case _ =>
xs.map(_.asInstanceOf[Object])
}
runtime.Tuples.fromIArray(xs2)
}
/** Convert a Product into a tuple of unknown arity and types */
def fromProduct(product: Product): Tuple =
runtime.Tuples.fromProduct(product)
def fromProductTyped[P <: Product](p: P)(using m: scala.deriving.Mirror.ProductOf[P]): m.MirroredElemTypes =
runtime.Tuples.fromProduct(p).asInstanceOf[m.MirroredElemTypes]
given canEqualEmptyTuple: CanEqual[EmptyTuple, EmptyTuple] = CanEqual.derived
given canEqualTuple[H1, T1 <: Tuple, H2, T2 <: Tuple](
using eqHead: CanEqual[H1, H2], eqTail: CanEqual[T1, T2]
): CanEqual[H1 *: T1, H2 *: T2] = CanEqual.derived
}
/** A tuple of 0 elements */
type EmptyTuple = EmptyTuple.type
/** A tuple of 0 elements. */
case object EmptyTuple extends Tuple {
override def toString(): String = "()"
}
/** Tuple of arbitrary non-zero arity */
sealed trait NonEmptyTuple extends Tuple
@showAsInfix
sealed abstract class *:[+H, +T <: Tuple] extends NonEmptyTuple
object *: {
def unapply[H, T <: Tuple](x: H *: T): (H, T) = (x.head, x.tail)
}