【问题标题】:Unexpected implicit resolution based on inference from return type基于返回类型推断的意外隐式解析
【发布时间】:2015-08-15 04:35:30
【问题描述】:

给定一个类型类,其中应根据返回类型执行实例选择:

case class Monoid[A](m0: A) // We only care about the zero here
implicit def s[T] : Monoid[Set[T]] = Monoid(Set.empty[T])
implicit def l[T] : Monoid[List[T]] = Monoid(List.empty[T])
def mzero[A](implicit m: Monoid[A]) : A = m.m0

为什么 Scala (2.11.6) 无法解析正确的实例:

scala> mzero : List[Int]
<console>:24: error: ambiguous implicit values:
 both method s of type [T]=> Monoid[Set[T]]
 and method l of type [T]=> Monoid[List[T]]
 match expected type Monoid[A]
              mzero : List[Int]
              ^

当使用 implicitly 函数时根据返回类型找到一个隐式没有问题(我们在这里将其重新定义为 i 以说明它与 mzero)

def i[A](implicit a : A) : A = a
scala> i : Monoid[List[Int]]
res18: Monoid[List[Int]] = Monoid(List())

错误消息中的Monoid[A],而不是Monoid[List[Int]],令人费解。

我假设许多 scalaz 贡献者都熟悉这个问题,因为它似乎限制了 scala 中类型类的便利性。

编辑:我正在考虑在不放弃类型推断的情况下使其正常工作。否则,我想了解为什么这是不可能的。如果这个限制被记录为 Scala 问题,我找不到它。

【问题讨论】:

  • Scala 在涉及隐式时使用预期返回类型来指导类型参数推断的能力的限制令人难以置信,所以每个人都只写mzero[List[Int]]
  • 作为一些解决方法,您可以通过将mzero 重新定义为def mzero[A](implicit m: Monoid[_&lt;:A]) : A = m.m0 来摆脱困境。正如 Travis Brown 所说,我个人会选择 mzero[List[Int]]
  • 令人印象深刻和令人惊讶。谢谢!有什么解释吗?这似乎是放松类型并获得更少的匹配!
  • 抱歉,不,我无法解释这种行为。只是经验告诉我尝试使Monoid协变,这确实使代码编译。由于您可能无法使其成为协变的(假设 Monoid 的完整定义会在逆变位置出现 A),下一个最好的方法是尝试通过使用存在类型单独在 mzero 上模拟它,给def mzero[A](implicit m: Monoid[_&lt;:A])。我肯定有兴趣亲自解释这种奇怪的情况。

标签: scala type-inference implicit return-type scalaz


【解决方案1】:

1) 如下重写代码后:

case class Monoid[A](m0: A) // We only care about the zero here
implicit def s[T] : Monoid[Set[T]] = Monoid(Set.empty[T])
implicit def l[T] : Monoid[List[T]] = Monoid(List.empty[T])
def mzero[A]()(implicit m: Monoid[A]) : A = m.m0

val zero = mzero[List[Int]]()
val zero2: List[Int] = mzero()

然后就很清楚为什么会这样了。

2) 在您必须将 mzero 设置为 def mzero[A]()(implicit m: Monoid[_ &lt;: A]) : A = m.m0 之后,您启用了额外的类型推断来解析存在类型。编译器从所需的返回类型中获得了实际类型。 如果需要,您可以使用def mzero[A &lt;: B, B]()(implicit m: Monoid[A]) : A = m.m0 进行检查。

3) 当然,所有这些行为只是编译器的微妙之处,我认为这些部分情况并不需要深入理解。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-07-23
    • 2016-07-12
    • 1970-01-01
    • 2018-08-10
    • 1970-01-01
    • 1970-01-01
    • 2018-06-29
    • 1970-01-01
    相关资源
    最近更新 更多