Рассмотрим этот код:
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 перед фазой стирания, но расширенного типа все еще нет.
Компилятор не считает, что TypeOr
и Tuple2
эквивалентны. Должны ли они быть?
Мне особенно интересно узнать, что компилятор считает этими типами больше, чем просто решение этого конкретного случая. Я попытался распечатать фазы компилятора, но он не печатает расширенный тип ни на одной фазе.
@coubeatczech Попробуйте добавить scalacOptions in Compile ++= Seq("-Xprint-types", "-Xprint:typer")
в build.sbt.
Или вы можете ввести выражение в REPL, и оно напечатает тип и значение этого выражения. Или вы можете использовать scala-reflect. import scala.reflect.runtime.universe._
def printType[A: TypeTag](a: A) = println(typeOf[A])
Или println(typeOf[A].dealias)
.
Во-первых, когда вы пишете, что имплицитный 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
если вы определяете def a: Unit = result
- это явно неправильный тип, как вы заставляете компилятор использовать расширенный тип, который он ожидает?
@coubeatczech Что вы подразумеваете под «развернуть тип»? На самом деле вы можете проверить, что def a: Unit = result
компилируется, так что это правильный тип. В Scala значение любого типа можно преобразовать в тип Unit
.
Я этого не знал, спасибо. Допустим, это будет def a: Int = result
. Под расширенным типом я подразумеваю, что type T = String
показывает String
, а не T
- разрешение имени типа до тех пор, пока больше нечего разрешать. В моем примере #T
относится к какому-то типу, и я хочу его разрешить.
@coubeatczech Попробуйте import scala.reflect.runtime.universe._
def printType[A: TypeTag](a: A) = println(typeOf[A].dealias)
printType(result)
я получу: src/main/scala/returnerror/Error.scala:49: No TypeTag available for ev.T
Я постараюсь добавить полный пример к вопросу
@coubeatczech Это означает, что T
- это не псевдоним типа, это просто абстрактный тип.
Нравится trait TypeOr[A, B] { type T }
.
Вы также можете предоставить код для
returnerror.TypeOr
?