kmh
kmh

Reputation: 1586

scala polymorphic type signatures with Ordering and ClassTag

I need help understanding this type signature:

def func[A : Ordering : ClassTag](a: A) = ???

I've come up with dummy examples using the type signature above... and also the type signature I'm more familiar with, that I believe is the close to the same thing based on my dummy example, but I can also come up with a toy example where they are clearly not the same.

These two seem similar:

import scala.reflect.ClassTag

// type signature in question
def func1[A : Ordering : ClassTag](elems: A*) = 
  Array[A](elems: _*).sorted

// my typical type signature
def func2[A <% Ordered[A]](elems: A*)(implicit c: ClassTag[A]) = 
  Array[A](elems: _*).sorted

Example use:

class BB(val i: Int) extends Ordered[BB] {
  def compare(that: BB): Int = that.i - i
  override def toString = s"BB(${i})"
}

func1(new BB(33), new BB(100), new BB(-1))
func2(new BB(33), new BB(100), new BB(-1))

Output for each is:

Array[BB] = Array(BB(100), BB(33), BB(-1))

The one edge case I can come up with where they differ... indicating one is not simply syntactic sugar for the other... is the following, where the function has an implicit order for a class that differs from the class's natural sort order.

This example (below) works fine, and implicit val ordering overrides class BB's natural sort order as I (kind of) expected.

def func3[A <% Ordered[A] : ClassTag](elems: A*) = {
  // opposite order defined in class BB
  implicit val ordering: Ordering[A] = 
    Ordering.by{ case bb: BB => bb.i }  

  Array[A](elems: _*).sorted
}

This version (below) give me an error...

def func3[A : Ordering : ClassTag](elems: A*) = {
  // opposite order defined in class BB
  implicit val ordering: Ordering[A] = 
    Ordering.by{ case bb: BB => bb.i }  

  Array[A](elems: _*).sorted
}

error: ambiguous implicit values: both value evidence$1 of type Ordering[A] and value ordering of type Ordering[A] match expected type scala.math.Ordering[A]

So based on this... I'm guessing : Ordering sort of converts Ordered[BB] into an implicit val ordering... or something like that? Are there deeper differences that my toy examples fail to reveal?

Thanks in advance.

Upvotes: 1

Views: 392

Answers (1)

Mikel San Vicente
Mikel San Vicente

Reputation: 3863

A : after a type parameter is syntactic sugar for the declaration of implicit parameters. In this case this means that

def func1[A: Ordering: ClassTag](elems: A*) = Array[A](elems: _*).sorted

is the same as

def func1[A](elems: A*)(implicit ordering: Ordering[A], classTag: ClassTag[A]) = Array[A](elems: _*).sorted

On the other hand func2 is declaring a view bound (<%) from A to Ordered. Knowing this the compiler can summon a Ordering[Ordered] that is passed to the sorted method

The reason why the latest version of fun3 is not compiling is because you are providing 2 implicit Ordering[A] in the scope: the one declared as an implicit parameter of fun3 and the implicit val ordering. The compiler doesn't know which one to choose and it complains about it, you should remove one of them to fix it.

Anyway, it is not a good idea to introduce code about specific types in the implementation of these functions. That pattern matching in the creation of the val ordering will fail for any type that is not BB.

If your goal is to define a specific Ordering[BB] you can do that in the companion object of BB and then load it in the implicit scope of the caller function like this

class BB(val i: Int) {
  override def toString: String = s"BB(${i})"
}

object BB {
  implicit val ordering = Ordering.by[BB, Int](_.i)
  val reverseOrdering = Ordering.by[BB, Int](-_.i)
}

Then when you try to order an BB it will pick the implicit ordering by default, but you could always overwrite it by doing

implicit val ord = BB.reverseOrdering
Seq[BB]().sorted

Upvotes: 1

Related Questions