Scala компилятор расширяет типы - PullRequest
0 голосов
/ 10 апреля 2019

Рассмотрим этот код:

  trait TypeOr[E, F] {
    type T
  }

  implicit def noneq2[E, F](implicit ev: E =!= F): TypeOr[E, F] = new TypeOr[E, F] {
    type T = (E, F)
  }

  sealed trait Error[+E, +A]

  case class Err[E, A](e: Error[E, A]) {
    def combine[B, F](f: A => Error[F, B])(implicit ev: TypeOr[E, F]): Error[ev.T, B] = ???
  }
  val result = Err(null.asInstanceOf[Error[Int, Int]]).combine(_ => null.asInstanceOf[Error[String, String]])

Пока все хорошо. Из приведенных выше определений я пришел к выводу, что расширенный тип результата следующий:

  val itsType: Error[(Int, String), String] = result

Но, очевидно, это не так, поскольку компилятор отвечает:

 found   : returnerror.Comb.Error[returnerror.Comb.TypeOr[Int,String]#T,String]
 required: returnerror.Comb.Error[(Int, String),String]
  val itsType: Error[(Int, String), String] = result

Можно ли узнать упрощенный - расширенный тип выражения? Я не могу получить эту информацию от компилятора, я пытался напечатать AST перед фазой стирания, но расширенный тип все еще отсутствует.

1 Ответ

0 голосов
/ 10 апреля 2019

Во-первых, когда вы пишете, что неявное noneq2 имеет тип TypeOr[E, F], вы теряете уточнение типа https://typelevel.org/blog/2015/07/19/forget-refinement-aux.html. Правильно

implicit def noneq2[E, F](implicit ev: E =:!= F) = new TypeOr[E, F] {
  type T = (E, F)
}

или лучше с явным типом

implicit def noneq2[E, F](implicit ev: E =:!= F): TypeOr[E, F] { type T = (E, F) }  = new TypeOr[E, F] {
  type T = (E, F)
}

Именно поэтому обычно вводится Aux

object TypeOr {
  type Aux[E, F, T0] = TypeOr[E, F] { type T = T0 }

  implicit def noneq2[E, F](implicit ev: E =:!= F): Aux[E, F, (E, F)] = new TypeOr[E, F] {
    type T = (E, F)
  }
}

Во-вторых, автоматически выводимый тип result, т.е. Error[TypeOr[Int, String]#T, String] (проекция типа TypeOr[Int,String]#T - это супертип (y.T forSome { val y: TypeOr[Int, String] }) и, более того, x.T), слишком грубый https://typelevel.org/blog/2015/07/23/type-projection.html

Лучше написать зависимый от пути тип для result.

Но

val x = implicitly[TypeOr[Int, String]]
val result: Error[x.T, String] =
  Err(null.asInstanceOf[Error[Int, Int]]).combine(_ => null.asInstanceOf[Error[String, String]])

не компилируется.

Дело в том, что implicitly может повредить уточнения типа https://typelevel.org/blog/2014/01/18/implicitly_existential.html

Вот почему существует макрос shapeless.the.

val x = the[TypeOr[Int, String]]
val result: Error[x.T, String] = Err(null.asInstanceOf[Error[Int, Int]]).combine(_ => null.asInstanceOf[Error[String, String]])
val itsType: Error[(Int, String), String] = result

В качестве альтернативы можно указать пользовательский материализатор

object TypeOr {
  //...
  def apply[E, F](implicit typeOr: TypeOr[E, F]): Aux[E, F, typeOr.T] = typeOr
}

val x = TypeOr[Int, String]
val result: Error[x.T, String] =
  Err(null.asInstanceOf[Error[Int, Int]]).combine(_ => null.asInstanceOf[Error[String, String]])
val itsType: Error[(Int, String), String] = result
...