【问题标题】:Implicit conversion classes for type-aliased Function types fail to compile in Scala类型别名函数类型的隐式转换类无法在 Scala 中编译
【发布时间】:2015-08-09 15:32:40
【问题描述】:

在 scala 中,以下代码可以正确编译:

class a {}
class b {}

object Main {

  implicit class Conv[f, t](val v: f ⇒ t) extends AnyVal {
    def conv = v
  }

  def main(args: Array[String]) {
    val m = (a: a) ⇒ new b
    m.conv
  }
}

但由于某种原因,以下编译失败:

class a {}
class b {}

object Main {
  type V[f, t] = f ⇒ t

  implicit class Conv[f, t](val v: V[f, t]) extends AnyVal {
    def conv = v
  }

  def main(args: Array[String]) {
    val m = (a: a) ⇒ new b
    m.conv
  }
}

带有以下信息:

value conv is not a member of a => b
    m.conv

为什么会这样?

编辑:是的,即使使用

仍然有错误
  val m: V[a,b] = new V[a,b] { def apply(a: a) = new b }

【问题讨论】:

  • 嗯,你可以通过在类型别名中声明函数参数的方差来解决这个问题:type V[-f, t] = f ⇒ t
  • 好吧,你没有从(a => b)Conv[a,b] 的隐式转换,所以我首先认为注释类型应该有助于val m: V[a, b] = (a: a) ⇒ new b。但事实证明,将错误更改为value conv is not a member of V[a,b] | m.conv 仍然无法编译(尽管 IntelliJ Idea 找到了隐式转换)。

标签: scala implicit-conversion


【解决方案1】:

在您的第一个示例中,val v: f => t 被推断为类型签名[-A, +B],因为它是一个参数的函数的简写。 Function1 具有类型签名 Function1[-A, +B]。所以,在A 参数中是逆变的,在B 参数中是协变的。

然后代码后面的 lambda 函数 (a: a) => new b 将其类型推断为从 a 到 b 的函数。因此,类型签名是相同的,并且隐式解析有效。

在您的第二个示例中,键入 V[f, t] = f => t 和从中创建的参数:val v: V[f, t],将它们的类型明确指定为 V[f, t]。函数f => t 仍将是[-A, +B],但您明确限制您的类型在类型签名中保持不变,因此类型V 在两个类型参数中都是不变的。

稍后,当您声明:val m = (a: a) => new b 时, this 的类型签名仍然是 [-A, +B],如第一个示例所示。隐式解析不起作用,因为 val 在其第一个类型参数中是逆变的,但类型 V 在其第一个类型参数中是不变的。

将 V 的类型签名更改为 V[-f, +t]V[-f, t] 可解决此问题,并且隐式解析再次起作用。

这确实提出了一个问题,即为什么第二类型参数的协方差对于隐式解析来说不是问题,而第一类型参数的逆变是。我到处玩,做了一些研究。我遇到了一些有趣的链接,这表明隐式解析肯定存在一些限制/问题,特别是在逆变方面。

我必须向了解 Scala 编译器内部结构和解析机制的人咨询更多细节,但这似乎违反了逆变上下文中隐式解析的一些限制。

对于您的第三个示例,我认为您的意思是:

class a {}
class b {}

object Main {
  type V[f, t] = f => t

  implicit class Conv[f, t](val v: V[f, t]) extends AnyVal {
    def conv = v
  }

  def main(args: Array[String]) {
    val m: V[a,b] = new V[a,b] { def apply(a: a) = new b }
    m.conv // does not compile
  }
}

这是一个有趣的问题,我认为这是一个稍微不同的原因。类型别名在变化时可以更改的内容受到限制,但允许更严格的变化。我不能确定这里发生了什么,但这里有一个interesting Stack Overflow question related to type aliases and variance

考虑到隐式解析的复杂性,再加上类型声明差异与隐含 Function1 差异的额外因素,我怀疑编译器无法解决该特定场景中的任何问题。

改为:

implicit class Conv(val v: V[_, _]) extends AnyVal {
  def conv = v
}

意味着它适用于所有场景,因为您基本上是在对编译器说,出于 Conv 隐式类的目的,您不需要关心 V 上类型参数的变化。

例如:以下也有效

class a {}
class b {}

object Main {
  type V[f, t] = f ⇒ t

  implicit class Conv(val v: V[_, _]) extends AnyVal {
    def conv = v
  }

  def main(args: Array[String]) {
    val m = (a: a) ⇒ new b
    m.conv
  }
}

【讨论】:

  • 谢谢,这是一个很好的答案,我一直怀疑它与方差有关。所有这些事实都没有回答我的编辑评论,即为什么显式创建 V[f,t] 对象(val m: V[a,b] = new V[a,b] { def apply(a: a) = new b })仍然无法隐式转换为Conv。我怀疑这是一个错误并且没有真正的答案,但以防万一我会再等一会儿。还是我错过了什么?
猜你喜欢
  • 1970-01-01
  • 2021-01-06
  • 1970-01-01
  • 1970-01-01
  • 2021-11-06
  • 1970-01-01
  • 2016-01-11
  • 1970-01-01
  • 2020-01-04
相关资源
最近更新 更多