Как назначение переменной TupleN значения *: тип работает в Scala 3?

#scala #scala-3 #hlist #dotty #singleton-type

Вопрос:

Из дерева иерархии типов в scala

  • пусть a: Tuple2[Int,Int] , я знаю Tuple2[Int,Int] , что простирается от Product2[Int,Int] ;
  • пусть b: 1 *: 2 *: EmptyTuple имеет тип Tuple (уточненный как Int *: Int *: EmptyTuple )

Они разных типов и не имеют никакого отношения к родителям. Единственное , что у них обоих есть Product , — это то, что они оба исходят из Product .

Но я могу назначить a b и наоборот, почему?

Ответ №1:

В Scala 3 Tuple1 Tuple22 типы … синтетически модифицируются компилятором для расширения *: типов. То Tuple2[A, B] есть модифицируется для расширения A *: B *: EmptyTuple (что расширяет Tuple ).

Таким образом, вы можете назначить a Tuple2[Int, Int] к a Int *: Int *: EmptyTuple . Аналогично, возможно обратное, потому что a A *: ... EmptyTuple будет рассматриваться как a TupleN , когда это возможно (

Комментарии:

1. Нет никаких причин, кроме магии компилятора

Ответ №2:

Это разные типы, но это не значит, что они не связаны. 1 *: 2 *: EmptyTuple является подтипом Tuple2[Int,Int] , потому что одноэлементные литералы 1 и 2 являются подтипами Int , а параметры типа Tuple2 являются ковариантными.

Вы можете расширить экземпляр 1 *: 2 *: EmptyTuple до Tuple2[Int,Int] , но не наоборот.

Пожалуйста, обратите внимание, что Tuple2[A, B] и A *: B *: EmptyTuple эквивалентны. 1 *: 2 *: EmptyTuple предполагается, что выражение уровня типа основано на синтаксисе вопроса, но если бы это было выражение уровня значения, то его тип был бы Int *: Int *: EmptyTuple эквивалентен Tuple2[Int,Int] . В этом случае экземпляры обоих типов будут «назначаться» друг другу.

Код для справки:

 @main def main() =
  type T1 = Tuple2[Int, Int]
  type T2 = 1 *: 2 *: EmptyTuple

  val t1a: T1 = (1, 2) //compiles
  val t2a: T2 = (1, 2) //compiles
  val t1b: T1 = (2, 1) //compiles
//  val t2b: T2 = (2, 1) // does not compile

//  t1a: T2 //does not compile
  t2a: T1 //compiles

  summon[1 <:< Int] // compiles
//  summon[1 =:= Int] // does not compile
  summon[T2 <:< T1] //compiles
//  summon[T1 <:< T2] // does not compile
//  summon[T1 =:= T2] // does not compile

  //these are the same types, not exclusively subtypes of one another
  summon[Tuple2[Int, Int] =:= Int *: Int *: EmptyTuple]
  summon[Tuple2[Int, Int] <:< Int *: Int *: EmptyTuple]
  summon[Int *: Int *: EmptyTuple <:< Tuple2[Int, Int]]
 

Комментарии:

1. Я не думаю , что @LinLee имеет в виду одноэлементный тип 1 *: 2 *: EmptyTuple , я думаю, что он просто имеет в виду Int *: Int *: EmptyTuple .

2. @MichaelZajac да, на основе результата, который, скорее всего, и произошел, но если бы вы сравнивали a:Tuple2[Int,Int] с b:1 *: 2 *: EmptyTuple этим, это было бы сравнение между типами, и в обоих случаях значения были бы одинаковыми