结果类型是泛型类型上的类型投影的隐式转换

Posted

技术标签:

【中文标题】结果类型是泛型类型上的类型投影的隐式转换【英文标题】:Implicit conversion where there resulting type is a type projection on a generic type 【发布时间】:2021-10-27 02:32:42 【问题描述】:

我有一些基本上可以归结为这个的 scala 2.13 代码

import scala.language.implicitConversions

trait Base 
  type V
  def v: V


case class Derived(v: Int) extends Base 
  type V = Int



object TestImplicitConversion 
  implicit def getV[T <: Base](a: T): T#V = a.v

  val a: Int = Derived(5)


这里我希望编译器使用隐式转换getVDerived 转换为Int,但是代码无法编译。手动添加对getV 的调用将使代码编译。有人可以帮我理解为什么在标准中解释了转换。

我发现进行这种转换的方法是添加第二个通用参数和一个约束

implicit def getV2[T <: Base, S](a: T)(implicit constraint: T#V =:= S): S = constraint(a.v)

在这个版本中,编译器使用转换并且代码会编译。

编辑:

@user 使用细化类型提供的替代解决方案确实看起来是一种更好的方法。但它并没有真正提供为什么它的原始实现不起作用的答案。所以我仍然有兴趣了解为什么编译器在显式调用将使代码编译时不使用隐式 def。

【问题讨论】:

只是一个提示,尽量避免使用通用类型投影,它是unsound,这里有一些examples。 Scala 3 丢弃一般类型的投影。最好利用依赖路径的类型,例如:implicit def getV[T &lt;: Base](a: T): a.V = a.v @gianlucaaguzzi 将类型投影T#V 替换为路径相关类型a.V 没有帮助。斯卡拉 2.13.6。 @DmytroMitin 对我来说似乎是work。 @user 你删除了implicit。当然,通过显式调用它可以工作。 @user 如果删除implicit,则无需将类型投影替换为路径相关类型。显式调用甚至可以使用类型投影。 【参考方案1】:

正如 gianluca aguzzi 在 cmets 中提到的那样,类型投影是不可靠的,应该避免。此外,T 不是具体类型,因此无论如何您都不能在其上使用投影。您可以只接受Base#V 作为类型参数,并为a 的类型使用细化类型:

implicit def get[T](a: Base  type V = T ): T = a.v

因此,您可以避免投射和类型投影。

Scastie

【讨论】:

有一种假设,只要不与下界/交集类型混合,类型投影就会保持正确github.com/lampepfl/dotty-feature-requests/issues/14lptk.github.io/programming/2019/09/13/type-projection.html 我不确定 OP 的实际需求,但最初的问题是关于隐式转换。 @DmytroMitin 啊,这很有趣!让我们希望这最终在 Scala 3 中实现。 @DmytroMitin 我删除了依赖于路径的类型部分并编写了仅 Scala 2 的答案(使用使用 Scala 2 而不是 Scala 3 的 Scastie),我将添加一个 Scala 3稍后。 我认为这里提供的这种方法比原始问题中使用的方法更好。但我仍然想了解为什么原始版本不起作用。因为在 scala 2.13 中显式添加调用将按预期编译和工作。

以上是关于结果类型是泛型类型上的类型投影的隐式转换的主要内容,如果未能解决你的问题,请参考以下文章

泛型类型的隐式转换?

25.scala的隐式转换

不可不会的scala隐式转换

Scala语法基础之隐式转换

JavaScript的数据类型的隐式转换

C的隐式类型转换