Skip to content

Commit 254b72a

Browse files
committed
Improvements to tuples: more new types and methods
1 parent 5157438 commit 254b72a

File tree

1 file changed

+72
-0
lines changed

1 file changed

+72
-0
lines changed

Diff for: library/src/scala/Tuple.scala

+72
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,17 @@ object Tuple:
166166
case EmptyTuple => Y
167167
case x1 *: xs1 => x1 *: Concat[xs1, Y]
168168

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+
169180
/** Fold a tuple `(T1, ..., Tn)` into `F[T1, F[... F[Tn, Z]...]]]` */
170181
type Fold[Tup <: Tuple, Z, F[_, _]] = Tup match
171182
case EmptyTuple => Z
@@ -258,6 +269,42 @@ object Tuple:
258269
*/
259270
type Union[T <: Tuple] = Fold[T, Nothing, [x, y] =>> x | y]
260271

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+
261308
/** Empty tuple */
262309
def apply(): EmptyTuple = EmptyTuple
263310

@@ -297,6 +344,31 @@ object Tuple:
297344
def fromProduct(product: Product): Tuple =
298345
runtime.Tuples.fromProduct(product)
299346

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+
300372
def fromProductTyped[P <: Product](p: P)(using m: scala.deriving.Mirror.ProductOf[P]): m.MirroredElemTypes =
301373
runtime.Tuples.fromProduct(p).asInstanceOf[m.MirroredElemTypes]
302374

0 commit comments

Comments
 (0)