Differences between (Int, Int) and Int *: Int *: EmptyTuple

I would have assumed that a type like e.g. Int *: Int *: EmptyTuple is the same as (Int, Int) both in theory and practice. However it seems that this is not so.

On a basic level they are interchangable:

scala> val a = (1, 2)
val a: (Int, Int) = (1,2)

scala> val b: (Int *: Int *: EmptyTuple) = a
val b: Int *: Int *: EmptyTuple = (1,2)

scala> val c: (Int, Int) = b
val c: (Int, Int) = (1,2)

scala> summon[(Int, Int) =:= (Int *: Int *: EmptyTuple)]
val res30: (Int, Int) =:= (Int, Int) = generalized constraint

However if we try to summon Mirror instances for them, they do not behave the same:

scala> summon[Mirror.ProductOf[(Int, Int)]]
val res29:  ( ...)

scala> summon[Mirror.ProductOf[Int *: Int *: EmptyTuple]]
-- Error:
1 |summon[Mirror.ProductOf[Int *: Int *: EmptyTuple]]
  |                                                  ^
  |no implicit argument of type deriving.Mirror.ProductOf[Int *: Int *: EmptyTuple] was found for parameter x of method summon in object Predef

So it seems that summoning a mirror for (Int, Int) succeeds, while for Int *: Int * EmptyTuple it fails. This is problematic since many operations on tuples return the latter version (even though it is shown as the former, just to add to the confusion):

scala> val x = (1, 2, 3)
val x: (Int, Int, Int) = (1,2,3)

scala> summon[Mirror.ProductOf[x.type]]
val res31:  ( ...)

scala> val y = x.drop(1)
val y: (Int, Int) = (2,3)

scala> summon[Mirror.ProductOf[y.type]]
-- Error:
1 |summon[Mirror.ProductOf[y.type]]
  |                                ^
  |no implicit argument of type deriving.Mirror.ProductOf[(y : (Int, Int))] was found for parameter x of method summon in object Predef

Another example:

scala> val a = Tuple.fromProductTyped(1 *: 2 *: EmptyTuple)
-- Error:
1 |val a = Tuple.fromProductTyped(1 *: 2 *: EmptyTuple)
  |                                                    ^
  |no implicit argument of type deriving.Mirror.ProductOf[(Int, Int)] was found for parameter m of method fromProductTyped in object Tuple

scala> val b = Tuple.fromProductTyped((1, 2))
val b: (Int, Int) = (1,2)

scala> val c = Tuple.fromProductTyped(b)
-- Error:
1 |val c = Tuple.fromProductTyped(b)
  |                                 ^
  |no implicit argument of type deriving.Mirror.ProductOf[(Int, Int)] was found for parameter m of method fromProductTyped in object Tuple

Is this behaviour intentional?

3 Likes

No, this is not intentional. There’s a recent bug report with some activity: https://github.com/lampepfl/dotty/issues/14127

2 Likes