Contextual abstractions

We are trying to implement sort function.

def sort(xs: List[Int]): List[Int] = {
  ...
  if (x < y)
  ...
}

And then, we want to make it general. But the problem is, how to implement the comparasion < ?

The question is using curryingto pass a lessThan function.

def sort(xs: List[T])(lessThan: (T, T) => Boolean): List[T] = {
  ...
  if lessThan(x, y)
  ...
}

Ordering

In fact, we can use Ordering in Scala standard library to do the same thing.

def sort(xs: List[T])(ord: Ordering[T]): List[T] = {
  ...
  if ord.lt(x, y)
  ...
}

To use it, we can do as followed.

sort(ints)(Ordering.Int)
sort(strings)(Ordering.String)

Ordering.Int and Ordering.String are implemented as followed:

trait Ordering[T] {
  def compare(a1: T, a2: T): Int
}

object Ordering {
  implicit val Int: Ordering[Int] =
    new Ordering[Int] {
      def compare(x: Int, y: Int) =
        if (x < y) -1 else if (x > y) 1 else 0
    }
  implicit val String: Ordering[String] =
    new Ordering[String] {
      def compare(s: String, t: String) = s.compareTo(t)
  }
}

Implicit Parameters

It's cumbersome to write Ordering.Int because Int always uses it and never uses other orderings.

Therefore, Scala has implicit parameters.

def sort[T](xs: List[T])(implicit ord: Ordering[T]): List[T] = ...

So than, we can simply call sort(ints). The compiler will figure out which ordering to use.

// The compiler will parse the call in following steps
// 1
sort(ints)
// 2
sort[Int](ints)
// 3
sort[Int](ints)(Ordering.Int)

Context Bounds

We use context bounds to simplify implicit parameters.

def sort[T: Ordering](xs: List[T]): List[T] = ...

It's the same as the previous definition. It tells us the general type has ordering.

Implicit Query

At any point in a program, one can query an implicit value of a given type by calling the implicitly operation:

implicitly[Ordering[Int]]
// equal to
implicitly[Ordering[Int]](Ordering.Int)

Note that implicitly is not a special keyword, it is defined as a library operation:

def implicitly[T](implicit value: T): T = value

Conclusion

We've introduced a way to do type-directed programming,with the help of a language mechanism that infers values from types.

The idea is that there has to be a unique (or at least most specific) instance that matches the queried type for it to be used by the compiler.

Instance is searched in the enclosing lexical scope (imports, parameters, inherited members, as well as normal definitions), and then if that doesn't give a match in the companion objects that are associated with the queried type.