Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Type parameter T bounded by Ordering[T]

Tags:

scala

implicit

When implementing a sortable data structure, I was thinking of doing something like this:

trait MaxHeap[T <: Ordering[T]] {
    def insert(e: T): Unit
    ...
}

But that wouldn't work for types like MaxHeap[Int]. In standard collection library, the element type T of a collection is not bounded. Instead, an implicit is provided for methods need it to convert T to Ordering[T], e.g.

trait Seq[+A] extends ... {
    // it's Ordering[B], not Ordering[A], but the idea is the same.
    def max[B >: A](implicit cmp: Ordering[B]): A
}

My question is, if there are many methods in my class/trait involving comparison, is there a way to specify class/trait's element type to be comparable so that I don't need to declare implicits for those methods?

like image 571
cfchou Avatar asked Jul 11 '13 15:07

cfchou


People also ask

How to declare a type parameter?

To declare a bounded type parameter, list the type parameter's name, followed by the extends keyword, followed by its upper bound, which in this example is Number . Note that, in this context, extends is used in a general sense to mean either "extends" (as in classes) or "implements" (as in interfaces).

What is a bound parameter?

A parameter binding is a piece of information that is transmitted from the origin to the destination of a flow. A parameter binding has a name and a value, which is obtained at its origin component. A flow may have a multiple parameter binding, passing a set of values instead of a single one.

What is T type in Scala?

trait Buffer: type T val element: T. Here we have defined an abstract type T . It is used to describe the type of element . We can extend this trait in an abstract class, adding an upper-type-bound to T to make it more specific.


1 Answers

You could declare an implicit parameter that defines the ordering, and then you can use it in all your methods:

class MaxHeap[T](implicit cmp: Ordering[_ >: T]) ...

If it's a trait, it can't take a parameter, but you can declare it as an implicit value:

trait Heap[T] {
  implicit protected val cmp: Ordering[_ >: T];
  // ... use cmp in your methods ...
}

and then each class that uses it can take an implicit parameter that overrides it:

class MaxHeap[T](implicit override protected val cmp: Ordering[_ >: T])
  extends Heap[T]
{
  // ...
}

Update: For some technical reasons Ordering isn't contravariant. This is why I used Ordering[_ >: T], because it allows greater flexibility. You can use an ordering that is defined for a superclass of T. You can of course use just cmp: Ordering[T], but then you can't do things like

new MaxHeap[java.lang.Integer]()(new Ordering[java.lang.Number] {
    // ...
  });

Also, the whole idea of Ordering is that you don't have to impose any constraints on T. This is more flexible and among other things allows to have different comparisons for the same class.

like image 107
Petr Avatar answered Sep 30 '22 11:09

Petr