简体   繁体   English

扩展具有有序特征的泛型类型使sbt编译器发出``将隐式扩展用于类型''错误

[英]Extending generic type with Ordered trait makes sbt compiler issue 'diverging implicit expansion for type' error

I've got a trait implementing an Ordered trait of Scala: 我有一个实现Scala的有序特征的特征:

package stackQuestions

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

and a subclass: 和一个子类:

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
  }
}

Basically, when an object of Value is created with provided function, the value is calculated. 基本上,使用提供的函数创建Value对象时,将计算该值。 What I want to achieve is to sort a collection of Value objects based on their value. 我要实现的是基于Value对象的值对它们进行排序。 This should be guaranteed by Ordered trait. 这应该通过有序特征来保证。 I've written some simple tests for this: 我为此编写了一些简单的测试:

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)
    }
  }
}

However, when sbt test -Xlog-implicits I've get error messages: 但是,当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

I've dug for similar problems and after reading: 我已经阅读了类似的问题并阅读了以下内容:

I get to know that the compiler is confused about how to choose the proper function for comparison. 我知道编译器对于如何选择适当的函数进行比较感到困惑。 I know that I can circumvent this using sortBy(obj => obj.fitness) but is there any way to use less verbose sorted method? 我知道我可以使用sortBy(obj => obj.fitness)规避此问题,但是有什么方法可以使用较少的冗长sorted方法?

Scala uses Ordering[T] trait for methods sorted , min and max of a collection of type T . Scala使用Ordering[T]特征对类型T的集合进行sortedminmax的方法。 It can generate instances of Ordering[T] automatically for T 's that extend Ordered[T] . 它可以产生的实例Ordering[T]用于自动T延伸的Ordered[T]

Because of Java compatibility Ordering[T] extends java.util.Comparator[T] , which is invariant in T , so Ordering[T] has to be invariant in T as well. 由于Java的兼容性Ordering[T]延伸java.util.Comparator[T]这是在不变的T ,所以Ordering[T]必须处于不变T为好。 See this issue: SI-7179 . 看到这个问题: SI-7179

This means that Scala can't generate instances of Ordering[T] for T 's that are subclasses of classes that implement Ordered . 这意味着Scala无法为实现了Ordered的类的子类的T生成Ordering[T]实例。


In your code you have val collection = List(biggerValue, lesserValue) , which has type List[Value[Int]] . 在您的代码中,您具有val collection = List(biggerValue, lesserValue) ,其类型为List[Value[Int]] Value doesn't have its own Ordered or Ordering , so Scala can't sort this collection . Value没有自己的OrderedOrdering ,因此Scala无法对该collection排序。

To fix you can specify collection to have type List[ValueTrait[Int]] : 要解决此问题,您可以将collection指定为List[ValueTrait[Int]]

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

Or define an explicit Ordering for Value[T] : 或为Value[T]定义明确的Ordering

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

You can also consider using a different design in this problem, if it suits your other requirements: 如果它符合您的其他要求,那么您也可以考虑在此问题中使用其他设计:

In your code all instances of ValueTrait[TYPE] have a value of type Double , and the distinctions in subclass and TYPE don't seem to be important at runtime. 在您的代码中, ValueTrait[TYPE]所有实例都具有类型Double的值,并且子类和TYPE的区别在运行时似乎并不重要。 So you can just define a case class Value(value: Double) and have different factory methods to create Value 's from different kinds of arguments. 因此,您可以只定义一个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)
} 

And the usage: 以及用法:

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)))

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM