0

我正在学习 Scala 以便将其用于项目。

我想要更深入了解的一件事是类型系统,因为它是我以前在其他项目中从未使用过的东西。

假设我已经设置了以下代码:

// priority implicits
sealed trait Stringifier[T] {
  def stringify(lst: List[T]): String
}

trait Int_Stringifier {
  implicit object IntStringifier extends  Stringifier[Int] {
    def stringify(lst: List[Int]): String = lst.toString()
  }
}

object Double_Stringifier extends Int_Stringifier {
  implicit object DoubleStringifier extends Stringifier[Double] {
    def stringify(lst: List[Double]): String = lst.toString()
  }
}

import Double_Stringifier._

object Example extends App {

  trait Animal[T0] {
    def incrementAge(): Animal[T0]
  }

  case class Food[T0: Stringifier]() {
    def getCalories  = 100
  }

  case class Dog[T0: Stringifier]
  (age: Int = 0, food: Food[T0] = Food()) extends Animal[String] {
    def incrementAge(): Dog[T0] = this.copy(age = age + 1)
  }
}

所以在这个例子中,有一个类型错误:

ambiguous implicit values:
[error]  both object DoubleStringifier in object Double_Stringifier of type Double_Stringifier.DoubleStringifier.type
[error]  and value evidence$2 of type Stringifier[T0]
[error]  match expected type Stringifier[T0]
[error]   (age: Int = 0, food: Food[T0] = Food()) extends Animal[String] 

好,可以。但是如果我删除上下文绑定,这段代码就会编译。即,如果我将 '''Dog''' 的代码更改为:

case class Dog[T0]
  (age: Int = 0, food: Food[T0] = Food()) extends Animal[String] {
    def incrementAge(): Dog[T0] = this.copy(age = age + 1)
  }

现在我假设这也不会编译,因为这种类型更通用,更模棱两可,但确实如此。

这里发生了什么?我知道当我将上下文绑定时,编译器不知道它是 double 还是 int。但是为什么还要编译一个更通用的类型呢?当然,如果没有上下文绑定,我可能有一个 Dog[String] 等,这也会使编译器感到困惑。

从这个答案:“上下文绑定描述了一个隐式值,而不是视图绑定的隐式转换。它用于声明对于某些类型 A,有一个 B[A] 类型的隐式值可用”

4

2 回答 2

2

现在我假设这也不会编译,因为这种类型更通用,更模棱两可,但确实如此。

模棱两可在隐含之间。两个都

Double_Stringifier.DoubleStringifier

和匿名证据Dog[T0: Stringifier](因为class Dog[T0: Stringifier](...)被取消class Dog[T0](...)(implicit ev: Stringifier[T0]))是候选人。

Int_Stringifier#IntStringifier无关紧要,因为它的优先级较低)。

现在您删除了上下文绑定,并且仅保留了一个隐式参数候选Food(),因此没有歧义。我看不出更通用的类型是如何相关的。更通用并不意味着更模棱两可。要么你在隐含之间有歧义。


实际上,如果您删除导入但保持上下文绑定,则默认值中看不到匿名证据。因此,它会引起歧义,但不计算何时独自一人:)

斯卡拉 2.13.2、2.13.3。

于 2020-07-16T10:14:39.267 回答
2

在我看来(如果我错了,我希望@DmytroMitin 能纠正我),理解这一点的关键是为food参数提供的默认值,这使得class Dog两个定义站点都需要隐式可用调用站点以及调用站点,要求隐式必须在编译时范围内。

代码中的import较早部分提供了Food()调用站点所需的隐式,但构造函数需要来自其调用站点Dog的隐式放置在 中。ev从而产生歧义。

于 2020-07-16T10:48:44.750 回答