为什么类型相等性检查隐式失败?
Why type equality check with implicitly fails?
我想在运行时对类型级别进行一些计算。所以我为它们定义了包装器 类 和隐式定义。但是我不明白为什么类型信息在计算过程中会丢失
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]
编译器不接受e
、l
、g
:
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
这是怎么回事,为什么编译器拒绝承认 X
和 y.Out
是相同的类型。是否可以改写示例以便编译?
implicitly
"forgets" 类型信息,根据其定义(+ 推断类型 % 重命名):
def implicitly[A](implicit a: A): A = a
请注意,它 returns 类型为 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: 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@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
子类等。但是,宏解决方案不是很复杂并且适用于所有情况,这是一个公平的交易。
我想在运行时对类型级别进行一些计算。所以我为它们定义了包装器 类 和隐式定义。但是我不明白为什么类型信息在计算过程中会丢失
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]
编译器不接受e
、l
、g
:
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
这是怎么回事,为什么编译器拒绝承认 X
和 y.Out
是相同的类型。是否可以改写示例以便编译?
implicitly
"forgets" 类型信息,根据其定义(+ 推断类型 % 重命名):
def implicitly[A](implicit a: A): A = a
请注意,它 returns 类型为 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: 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@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
子类等。但是,宏解决方案不是很复杂并且适用于所有情况,这是一个公平的交易。