Reputation: 1586
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
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