@@ -166,6 +166,17 @@ object Tuple:
166
166
case EmptyTuple => Y
167
167
case x1 *: xs1 => x1 *: Concat [xs1, Y ]
168
168
169
+ /** An infix shorthand for `Concat[X, Y]` */
170
+ infix type ++ [X <: Tuple , + Y <: Tuple ] = Concat [X , Y ]
171
+
172
+ /** The index of `Y` in tuple `X` as a literal constant Int,
173
+ * or `Size[X]` if `Y` does not occur in `X`
174
+ */
175
+ type IndexOf [X <: Tuple , Y ] <: Int = X match
176
+ case Y *: _ => 0
177
+ case x *: xs => S [IndexOf [xs, Y ]]
178
+ case EmptyTuple => 0
179
+
169
180
/** Fold a tuple `(T1, ..., Tn)` into `F[T1, F[... F[Tn, Z]...]]]` */
170
181
type Fold [Tup <: Tuple , Z , F [_, _]] = Tup match
171
182
case EmptyTuple => Z
@@ -258,6 +269,42 @@ object Tuple:
258
269
*/
259
270
type Union [T <: Tuple ] = Fold [T , Nothing , [x, y] =>> x | y]
260
271
272
+ /** A type level Boolean indicating whether the tuple `X` conforms
273
+ * to the tuple `Y`. This means:
274
+ * - the two tuples have the same number of elements
275
+ * - for corresponding elements `x` in `X` and `y` in `Y`, `x` matches `y`.
276
+ * @pre The elements of `X` are assumed to be singleton types
277
+ */
278
+ type Conforms [X <: Tuple , Y <: Tuple ] <: Boolean = Y match
279
+ case EmptyTuple =>
280
+ X match
281
+ case EmptyTuple => true
282
+ case _ => false
283
+ case y *: ys =>
284
+ X match
285
+ case `y` *: xs => Conforms [xs, ys]
286
+ case _ => false
287
+
288
+ /** A type level Boolean indicating whether the tuple `X` has an element
289
+ * that matches `Y`.
290
+ * @pre The elements of `X` are assumed to be singleton types
291
+ */
292
+ type Contains [X <: Tuple , Y ] <: Boolean = X match
293
+ case Y *: _ => true
294
+ case x *: xs => Contains [xs, Y ]
295
+ case EmptyTuple => false
296
+
297
+ /** A type level Boolean indicating whether the type `Y` contains
298
+ * none of the elements of `X`.
299
+ * @pre The elements of `X` and `Y` are assumed to be singleton types
300
+ */
301
+ type Disjoint [X <: Tuple , Y <: Tuple ] <: Boolean = X match
302
+ case x *: xs =>
303
+ Contains [Y , x] match
304
+ case true => false
305
+ case false => Disjoint [xs, Y ]
306
+ case EmptyTuple => true
307
+
261
308
/** Empty tuple */
262
309
def apply (): EmptyTuple = EmptyTuple
263
310
@@ -297,6 +344,31 @@ object Tuple:
297
344
def fromProduct (product : Product ): Tuple =
298
345
runtime.Tuples .fromProduct(product)
299
346
347
+ extension [X <: Tuple ](inline x : X )
348
+
349
+ /** The index (starting at 0) of the first element in the type `X` of `x`
350
+ * that matches type `Y`.
351
+ */
352
+ inline def indexOfType [Y ] = constValue[IndexOf [X , Y ]]
353
+
354
+ /** A boolean indicating whether there is an element in the type `X` of `x`
355
+ * that matches type `Y`.
356
+ */
357
+
358
+ inline def containsType [Y ] = constValue[Contains [X , Y ]]
359
+
360
+ /* Note: It would be nice to add the following two extension methods:
361
+
362
+ inline def indexOf[Y: Precise](y: Y) = constValue[IndexOf[X, Y]]
363
+ inline def containsType[Y: Precise](y: Y) = constValue[Contains[X, Y]]
364
+
365
+ because we could then move indexOf/contains completely to the value level.
366
+ But this requires `Y` to be inferred precisely, and therefore a mechanism
367
+ like the `Precise` context bound used above, which does not yet exist.
368
+ */
369
+
370
+ end extension
371
+
300
372
def fromProductTyped [P <: Product ](p : P )(using m : scala.deriving.Mirror .ProductOf [P ]): m.MirroredElemTypes =
301
373
runtime.Tuples .fromProduct(p).asInstanceOf [m.MirroredElemTypes ]
302
374
0 commit comments