【问题标题】:Scala compiler expand typesScala 编译器扩展类型
【发布时间】:2019-08-31 17:24:06
【问题描述】:

考虑这段代码:

  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,但扩展类型仍然不存在。

【问题讨论】:

  • 能否提供returnerror.TypeOr的代码?
  • 编译器不相信TypeOrTuple2 是等价的。他们应该是吗?
  • 我特别感兴趣的是,编译器认为这些类型不仅仅是解决这个特殊情况。我尝试打印编译器阶段,但它不会在任何阶段打印扩展类型。
  • @coubeatczech 尝试将 scalacOptions in Compile ++= Seq("-Xprint-types", "-Xprint:typer") 添加到 build.sbt。

标签: scala types implicit type-level-computation


【解决方案1】:

首先,当您编写隐式 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
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-06-15
  • 1970-01-01
  • 2017-05-03
相关资源
最近更新 更多