为什么类型相等检查隐式失败?

时间:2017-09-09 23:33:47

标签: scala type-level-computation implicits

我想在运行时对类型级别进行一些计算。所以我为它们定义了包装类和隐式定义。但我无法理解为什么在计算过程中丢失了类型信息

sealed trait Solve[In] {
  type Out
}

implicit def idSolve[I] = new Solve[I] {
  override type Out = I
}

type X = Int
val y = implicitly[Solve[X]]

val e = implicitly[X =:= y.Out]
val l = implicitly[X <:< y.Out]
val g = implicitly[y.Out <:< X]

编译器既不接受elg

TypeSolution.scala:15: error: Cannot prove that test.Test.X =:= test.Test.y.Out.
  val e = implicitly[X =:= y.Out]
                    ^
TypeSolution.scala:16: error: Cannot prove that test.Test.X <:< test.Test.y.Out.
  val l = implicitly[X <:< y.Out]
                    ^
TypeSolution.scala:17: error: Cannot prove that test.Test.y.Out <:< test.Test.X.
  val g = implicitly[y.Out <:< X]
                    ^
three errors found

发生了什么以及编译器拒绝承认Xy.Out属于同一类型的原因。是否可以重新编写示例,以便编译?

1 个答案:

答案 0 :(得分:4)

implicitly&#34;忘记&#34;类型信息,根据它的定义(+推断类型%重命名):

def implicitly[A](implicit a: A): A = a

请注意,它会返回A类型的内容,而不是a.type。因此,您的代码如下所示:

val y = implicitly[Solve[Int]]
// ===
val y: Solve[Int] /* per return type of implicitly */ = implicitly[Solve[Int]]

y的类型推断为Solve[Int],而不是Solve[Int] { type Out = Int },因此y.Out未知。

您可以定义没有此限制的自定义implicitly

import Predef.{ implicitly => _, _ } // Begone, failure!

import language.experimental.macros
import reflect.macros.whitebox.Context

def implicitly[T](implicit found: T): T = macro implicitly_impl[T]

def implicitly_impl[T: c.WeakTypeTag](c: Context)(found: c.Tree) = found
// We just return the exact same tree that we got, eliding the implicitly completely and dropping it's type-erasing touch.

作为直接替代品:

scala> :paste
// Entering paste mode (ctrl-D to finish)

import Predef.{ implicitly => _, _ }

import language.experimental.macros
import reflect.macros.whitebox.Context

def implicitly[T](implicit found: T): T = macro implicitly_impl[T]

def implicitly_impl[T: c.WeakTypeTag](c: Context)(found: c.Tree) = found

// Exiting paste mode, now interpreting.

import Predef.{implicitly=>_, _}
import language.experimental.macros
import reflect.macros.whitebox.Context
defined term macro implicitly: [T](implicit found: T)T
implicitly_impl: [T](c: scala.reflect.macros.whitebox.Context)(found: c.Tree)(implicit evidence$1: c.WeakTypeTag[T])c.Tree

scala> :paste
// Entering paste mode (ctrl-D to finish)

sealed trait Solve[In] {
  type Out
}

implicit def idSolve[I] = new Solve[I] {
  override type Out = I
}

type X = Int
val y = implicitly[Solve[X]]

val e = implicitly[X =:= y.Out]
val l = implicitly[X <:< y.Out]
val g = implicitly[y.Out <:< X]

// Exiting paste mode, now interpreting.

defined trait Solve
idSolve: [I]=> Solve[I]{type Out = I}
defined type alias X
y: Solve[X]{type Out = X} = $anon$1@611f28f5
e: y.Out =:= y.Out = <function1>
l: X <:< X = <function1>
g: y.Out <:< y.Out = <function1>

旁注:

def implicitly[A](implicit a: A): a.type = a

不会工作,因为当您使用没有AnyRef上限的单例类型时,Scala不喜欢它。

def implicitly[A <: AnyRef](implicit a: A): a.type = a

在这种情况下适用,但它不允许AnyVal子类等。然而,宏观解决方案并不复杂,适用于所有事情,这是一种公平交易。