scala implicit (5)
Ordering[T]
Let’s learn the structure of a type class using Ordering[T] as an example.
Ordering is used to compare objects. All the methods are defined based on compare(x: T, y: T) method. I simplified the code and put it below.
trait Ordering[T] {
def compare(x: T, y: T): Int
/** other methods **/
class OrderingOps(lhs: T) {
def <(rhs: T) = lt(lhs, rhs)
/** other operators **/
}
// implicit converter
implicit def mkOrderingOps(t: T): OrderingOps = new OrderingOps(t)
}
object Ordering {
trait ByteOrdering extends Ordering[Byte] {
def compare(x: Byte, y: Byte) = x.toInt - y.toInt
}
// implicit instance
implicit object Byte extends ByteOrdering
/** other implicit objects **/
// organize other implicit methods in inner scope
trait ExtraImplicits {
/** other methods **/
implicit def infixOrderingOps[T](x: T)(implicit ord: Ordering[T]): Ordering[T]#Ops = new ord.Ops(x)
}
object Implicits extends ExtraImplicits{}
}
Key points:
- use inner class to add infix operators or add syntax to the wrapped type T.
- put all common implicit instances into companion objects. So they are in the implicit scope of type Ordering[T].
- organize implicit methods in trait and put it in the nested level of companion object. So those implicit methods need to be explicit imported when using.
case class Student(name: String, score: Double)
// implicit Ordering[Double] instance is in scope
// Ordering[Double] is actually Ordering.apply[Double](implicit Double instance)
val orderStudent: Ordering[Student] = Ordering.by[Student, Double](student => student.score)
import orderStudent.mkOrderingOps
//or
// import Ordering.Implicits._
val s1 = Student("a", 90)
val s2 = Student("a", 80)
// implicit convert s1 to orderStudent.OrderingOps
println(s1 < s2)