“没有为U定义的隐式排序”的错误,其中已经扩展了Ordered的特征

时间:2013-02-24 22:34:58

标签: scala traits

创建名为test.WsTemp的ScalaIDE工作表后,我编写了下面的代码,并在特征Enum中收到了一行错误:

  1. 从特征scala.math.Ordering[U]
  2. 中排序的方法开始,对类型LowPriorityOrderingImplicits进行隐式扩展
  3. 没有为Ordering
  4. 定义隐式U
  5. 方法排序的参数不足:(implicit ord: scala.math.Ordering[U])List[U],未指定的值参数ord
  6. 为什么这不起作用,因为它显而易见Val扩展Ordered[Val]

    object WsTemp {
      trait Val extends Ordered[Val] {
        val id: Int
        val name: String
        final def compare(that: Val) = this.id - that.id
        override def toString: String = name
      }
    
      trait Enum[U <: Val] {
        protected def init: Set[U]
        val all = init
        val allOrdered = all.toList.sorted       // <-- Receiving error here
        val byId = all.map(x => (x.id, x)).toMap
        val byName = all.map(x => (x.name, x)).toMap
    
        def apply(id: Int) = byId.get(id)
        def apply(name: String) = byName.get(name)
      }
    
      sealed class Value(val id: Int, val name: String) extends Val
      object Suit extends Enum[Value] {
        override def init: Set[Value] = //Set()
          Set(
              new Value(0, "Spade")
            , new Value(1, "Club")
            , new Value(2, "Diamond")
            , new Value(3, "Heart")
          )
        val Spade = Suit.byId(0)
        val Club = Suit.byId(1)
        val Diamond = Suit.byId(2)
        val Heart = Suit.byId(3)
      }
    
      val all = Suit.all
      val allOrdered = Suit.allOrdered
      val byId = Suit.byId
      val byName = Suit.byName
      val spade = Suit.Spade
    }
    

2 个答案:

答案 0 :(得分:4)

OrderingOrdered都是不变的,因此UVal的子类型(由关系U <: Val表示)并不意味着Ordering[Val]可以用作Ordering[U]。 因此,即使Val扩展Ordered[Val]这一事实意味着您隐式获得Ordering[Val],这对Ordering[U]也没有任何意义。

因此,编译器抱怨它无法找到Ordering[U]类型的隐式值(调用sorted所需的)。

使用小代码段很容易说明prolbem,该代码段模仿OrderedOrdering的情况:

object WsTemp {
  trait MyOrdered[T]

  trait MyOrdering[T]
  object MyOrdering {
    implicit def toOrdering[A <% MyOrdered[A]]: MyOrdering[A] = new MyOrdering[A]{}
  }

  trait Val extends MyOrdered[Val]

  def test[T](implicit ord: MyOrdering[T]) {}

  trait Enum[U <: Val] {
    def callTest() { test[U] }
  }
}

产生以下错误:

<console>:20: error: could not find implicit value for parameter ord: WsTemp.MyOrdering[U]
           def callTest() { test[U] }

但是如果你使MyOrderedMyOrdering协变,那么编译很好:

trait MyOrdered[+T]      
trait MyOrdering[+T]
...

显然,您无法更改scala的OrderingOrdered以使其不变。


现在,解决问题的一种方法是安排代码,以便Val不会扩展Ordered[Val],而是扩展Ordered[X],其中X是实际类型您想拥有Ordering(此处为X = U)。这可以通过 F-bounded多态来实现:

trait Val[Self<:Val[Self]] extends Ordered[Self] {
  //...
}

trait Enum[U <: Val[U]] {
  //...
}

sealed class Value(val id: Int, val name: String) extends Val[Value]
//...

U现在是Val[U]的子类型,它是Ordered[U]的子类型(与之前的Ordered[Val]子类型相反) ,所以你现在隐含地得到一个Ordering[U],它(隐式)传递给sorted。问题解决了。

答案 1 :(得分:1)

正如RégisJean-Gilles所说,“Ordering and Ordered都是不变的,所以U是Val的子类型(由U&lt;:Val表示)并不意味着Ordering [Val]可以用作Ordering [U]。所以即使Val扩展Ordered [Val]意味着你隐式得到一个Ordering [Val],这对Ordering [U]来说没有任何意义。“

解决此问题的一种非常简单的方法是指定要用于排序方法的类型。替换:

  val allOrdered = all.toList.sorted 

使用:

  val allOrdered = all.toList.sorted[Val]