使用有序特征扩展泛型类型会使 sbt 编译器问题'diverging implicit expansion for type'错误



我有一个特质实现了 Scala 的有序特质:

package stackQuestions
trait ValueTrait[TYPE] extends Ordered[ValueTrait[TYPE]]{
def value: Double
}

和一个子类:

package stackQuestions
class Value[A](list: List[A], function: (A, A) => Double) extends ValueTrait[A] {
private val _value: Double = list.zip(list.tail).map(pair => function(pair._1, pair._2)).sum
override def value: Double = _value
override def compare(that: ValueTrait[A]): Int = {
(this.value - that.value).signum
}
}

基本上,当使用提供的函数创建 Value 对象时,将计算该值。我想要实现的是根据 Value 对象的值对它们的集合进行排序。这应该由有序特征来保证。我为此编写了一些简单的测试:

package stackQuestions
import org.scalatest.FunSpec
class ValueTest extends FunSpec {
def evaluationFunction(arg1: Int, arg2: Int): Double = {
if (arg1 == 1 && arg2 == 2) return 1.0
if (arg1 == 2 && arg2 == 1) return 10.0
0.0
}
val lesserValue = new Value(List(1, 2), evaluationFunction) // value will be: 1.0
val biggerValue = new Value(List(2, 1), evaluationFunction) // value will be: 10.0
describe("When to Value objects are compared") {
it("should compare by calculated value") {
assert(lesserValue < biggerValue)
}
}
describe("When to Value objects are stored in collection") {
it("should be able to get max value, min value, and get sorted") {
val collection = List(biggerValue, lesserValue)
assertResult(expected = lesserValue)(actual = collection.min)
assertResult(expected = biggerValue)(actual = collection.max)
assertResult(expected = List(lesserValue, biggerValue))(actual = collection.sorted)
}
}
}

但是,sbt test -Xlog-implicits时,我收到错误消息:

[info] Compiling 1 Scala source to /project/target/scala-2.11/test-classes ...
[error] /project/src/test/scala/stackQuestions/ValueTest.scala:24:64: diverging implicit expansion for type Ordering[stackQuestions.Value[Int]]
[error] starting with method $conforms in object Predef
[error]       assertResult(expected = lesserValue)(actual = collection.min)
[error]                                                                ^
[error] /project/src/test/scala/stackQuestions/ValueTest.scala:25:64: diverging implicit expansion for type Ordering[stackQuestions.Value[Int]]
[error] starting with method $conforms in object Predef
[error]       assertResult(expected = biggerValue)(actual = collection.max)
[error]                                                                ^
[error] /project/src/test/scala/stackQuestions/ValueTest.scala:27:83: diverging implicit expansion for type scala.math.Ordering[stackQuestions.Value[Int]]
[error] starting with method $conforms in object Predef
[error]       assertResult(expected = List(lesserValue, biggerValue))(actual = collection.sorted)
[error]                                                                                   ^
[error] three errors found
[error] (Test / compileIncremental) Compilation failed
[error] Total time: 1 s, completed 2018-09-01 08:36:18

我已经挖掘了类似的问题,并在阅读后:

  • "发散隐式扩展"scalac 消息是什么意思?
  • 为什么我在尝试对有序类的实例进行排序时出现"发散隐式扩展"错误?
  • 如何解决类型的发散隐式扩展,
  • scala - 使用"sortBy"时混淆"发散隐式扩展"错误,

我知道编译器对如何选择合适的函数进行比较感到困惑。我知道我可以使用sortBy(obj => obj.fitness)来规避这一点,但是有没有办法使用不那么冗长的sorted方法?

Scala使用Ordering[T]特征来sortedminmax类型为T集合的方法。它可以为扩展Ordered[T]T自动生成Ordering[T]实例。

因为Java的兼容性Ordering[T]扩展了java.util.Comparator[T],这在T中是不变的,所以Ordering[T]T也必须是不变的。请参阅此问题:SI-7179。

这意味着 Scala 无法为T生成Ordering[T]的实例,这些实例是实现Ordered的类的子类。


在你的代码中,你有val collection = List(biggerValue, lesserValue),它的类型为List[Value[Int]]Value没有自己的OrderedOrdering,所以Scala无法对这个collection进行排序。

要修复,您可以指定collection类型为List[ValueTrait[Int]]

val collection = List[ValueTrait[Int]](biggerValue, lesserValue)

或者为Value[T]定义一个显式Ordering

object Value {
implicit def ord[T]: Ordering[Value[T]] = 
Ordering.by(t => t: ValueTrait[T])
}

如果适合您的其他要求,您也可以考虑在此问题中使用不同的设计:

在你的代码中,所有ValueTrait[TYPE]实例都有一个类型Double的值,子类和TYPE的区别在运行时似乎并不重要。因此,您可以只定义一个case class Value(value: Double),并使用不同的工厂方法来从不同类型的参数创建Value

case class Value(value: Double) extends Ordered[Value] {
override def compare(that: Value): Int = this.value compareTo that.value
}
object Value {
def fromList[A](list: List[A], function: (A, A) => Double): Value =
Value((list, list.tail).zipped.map(function).sum)
} 

和用法:

scala> val lesserValue = Value.fromList(List(1, 2), evaluationFunction)
lesserValue: Value = Value(1.0)
scala> val biggerValue = Value.fromList(List(2, 1), evaluationFunction)
biggerValue: Value = Value(10.0)
scala> val collection = List(biggerValue, lesserValue)
collection: List[Value] = List(Value(10.0), Value(1.0))
scala> (collection.min, collection.max, collection.sorted)
res1: (Value, Value, List[Value]) = (Value(1.0),Value(10.0),List(Value(1.0), Value(10.0)))

相关内容

最新更新