使用Ordered trait扩展泛型类型会使sbt编译器发出“类型错误的隐式扩展”错误

问题描述 投票:1回答:1

我有一个实现Scala的Ordered特征的特性:

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对象对Value对象的集合进行排序。这应该由Ordered trait保证。我为此写了一些简单的测试:

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

我已经挖过类似的问题,看完之后:

我知道编译器对如何选择适当的函数进行比较感到困惑。我知道我可以使用sortBy(obj => obj.fitness)绕过这个但是有没有办法使用更简洁的sorted方法?

scala implicit scala-generics
1个回答
2
投票

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

由于Java兼容性,Ordering[T]扩展java.util.Comparator[T],这在T中是不变的,所以Ordering[T]也必须在T中不变。看到这个问题:SI-7179

这意味着Scala无法为Ordering[T]生成T的实例,Ordered是实现val collection = List(biggerValue, lesserValue)的类的子类。


在您的代码中,您有List[Value[Int]],其类型为ValueOrdered没有自己的Orderingcollection,因此Scala无法对此collection进行排序。

要解决此问题,您可以将List[ValueTrait[Int]]指定为val collection = List[ValueTrait[Int]](biggerValue, lesserValue) 类型:

Ordering

或者为Value[T]定义一个明确的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)))

用法:

qazxswpoi
© www.soinside.com 2019 - 2024. All rights reserved.