Mike Slinn

Sorting and Ordered Collections

— Draft —

Published 2013-03-14. Last modified 2019-07-12.
Time to read: 7 minutes.

We continue our exploration of parametric types with the topics of sorting and ordered collections.

The sample code for this lecture can be found in courseNotes/src/main/scala/collections/CollectionSorting.scala.

The definitions for ThingOrdered and ThingOrdering and their instances are provided in courseNotes/src/main/scala/collections/package.scala.

Ad-Hoc Sorting

We discussed Function1 in the Functions are First Class lecture of the Introduction to Scala course, and we showed how higher-order functions can be written that accept Function1s in the Higher-Order Functions lecture of this course. As a reminder, a Function1 receives one parameter and returns a value. For example, a Function1[A, B] accepts an instance of type A and returns an instance of type B; the shorthand notation is (A) => B.

It is also useful to work with higher-order functions that accept FunctionNs which receive more than one parameter when working with collections. A common use case is to make a sorted copy of a collection according to an ad-hoc criterion by passing in an instance of a Function2 which compares two elements of the collection at a time as it walks through the collection. The type of this comparison function is Function2[A, A, B], which is written in shorthand as (A, A) => B. The shorthand indicates that a Tuple2[A, A] is actually passed as the argument list into the Function2, and an instance of B is returned. The comparison function must accept two parameters of the same type, and return true if the first argument precedes the second argument or false otherwise. Here is an example of passing an anonymous Function2[Int, Int, Boolean] into the sortWith higher-order function.

Scala REPL
scala> List(3, 7, 5, 2).sortWith((x, y) => x < y)
res1: List[Int] = List(2, 3, 5, 7)

The sortWith higher order function iterates through the collection, comparing pairs of elements, and sorts the intermediate results internally. sortWith is provided by the SeqOps trait, which is mixed into all Scala collection types. It returns a new sorted collection consisting of the elements of the original collection, ordered according to the comparison function passed into it. sortWith provides a stable sort, which means that elements that are equal as determined by the comparison function appear in the same order in the sorted sequence as they appeared in the original collection.

When we discussed the shorthand for writing FunctionNs, we did not mention what happens if multiple parameters are supplied by a higher-order function to a function value. Multiple underscores can be used in the shorthand, and they are bound in order. For example, we can use shorthand to perform the same sort, as written above, like this.

Scala REPL
scala> List(3, 7, 5, 2).sortWith(_ < _)
res2: List[Int] = List(2, 3, 5, 7)

Exercise – Sorting a List Using a Higher-Order Function

Use the REPL to sort List(3, -7, 5, -2) according to the absolute value of each of the elements.

Hint

You can find the absolute value of a number using math.abs.

Solution

Scala REPL
scala> List(3, -7, 5, -2).sortWith(math.abs(_)<math.abs(_))
res0: List[Int] = List(-2, 3, 5, -7) 

To run this solution, type:

Shell
$ sbt "runMain solutions.AbsSort"

Ordered Supports One Natural Ordering for a Collection Type

Value classes (discussed in the Implicit Values lecture) cannot override equals or hashCode.

As we just saw, you can sort a collection upon demand, however you will often want to define a natural ordering for your collection. Scala provides the abstract Ordered trait for classes that have a default natural ordering. It is easy to apply this trait: merely mix in the Ordered trait into any class that needs to be sorted, and define the methods that the Ordered trait declares.

Note that the name of the class to be sorted is a type parameter for the abstract trait Ordered. This is why the name of the class, ThingOrdered, appears twice in the class definition in the following code example. Also note that Ordered requires the compare method be implemented.

class ThingOrdered(val i: Int, val s: String) extends Ordered[ThingOrdered] {
  def compare(that: ThingOrdered) =  {
    val primaryKey = this.i - that.i
    if (primaryKey!=0) primaryKey else this.s compare that.s
  }

  override def equals(other: Any) = {
    val that = other.asInstanceOf[ThingOrdered]
    this.i == that.i && this.s==that.s
  }

  override def hashCode = super.hashCode

  override def toString = s"ThingOrdered($i, $s)"
}

The Ordered trait does not provide a default implementation of equality, so if you define compare I strongly suggest that you also redefine equals, which is actually invoked by the == method. Whenever you define equals you should also define hashCode as shown above.

Classes that implement the Ordered trait can be sorted many ways, including.

  1. The sorted method, available to all collection types because they all are subclasses of SeqOps. sorted uses the natural ordering of the collection.
  2. The methods of the scala.util.Sorting singleton object (quickSort and stableSort).
  3. The standard comparison operators >, <, >= and <=.
The Ordered trait does not provide a default implementation of equality

Let’s define some ThingOrdered instances and compare them:

Scala REPL
scala> val thingOrdered1 = new ThingOrdered(1, "x")
thingOrdered1: ThingOrdered = ThingOrdered(1, x)
scala>
val thingOrdered2 = new ThingOrdered(1, "y") thingOrdered2: ThingOrdered = ThingOrdered(1, y)
scala>
val thingOrdered3 = new ThingOrdered(33, "b") thingOrdered3: ThingOrdered = ThingOrdered(33, b)
scala>
val thingOrdered4 = new ThingOrdered(4, "m") thingOrdered4: ThingOrdered = ThingOrdered(4, m)
scala>
val thingOrdered5 = new ThingOrdered(4, "n") thingOrdered5: ThingOrdered = ThingOrdered(4, n)

Now let’s compare the ThingOrdered instances.

Scala REPL
scala> thingOrdered1 > thingOrdered2
res3: Boolean = false
scala>
thingOrdered1 < thingOrdered2 res4: Boolean = true
scala>
thingOrdered1 == new ThingOrdered(1, "z") res5: Boolean = false
scala>
thingOrdered1 == thingOrdered2 res6: Boolean = false
scala>
thingOrdered1 <= thingOrdered2 res7: Boolean = true
scala>
thingOrdered1 >= thingOrdered2 res8: Boolean = false

We can sort an Array of ThingOrdered using sorted, which returns a new sorted collection without modifying the original collection. The Sorted method is provided for all types of collections.

Scala REPL
scala> val thingsOrdered = Array(thingOrdered1, thingOrdered2, thingOrdered3, thingOrdered4, thingOrdered5)
thingsOrdered: Array[collections.ThingOrdered] = Array(ThingOrdered(1, x), ThingOrdered(1, y), ThingOrdered(33, b), ThingOrdered(4, m), ThingOrdered(4, n))]
scala>
thingsOrdered.sorted res9: Array[collections.ThingOrdered] = Array(ThingOrdered(1, x), ThingOrdered(1, y), ThingOrdered(4, m), ThingOrdered(4, n), ThingOrdered(33, b))

Contrast this with Sorting.quickSort, which modifies the Array passed to it; remember that Array is a mutable data type.

Scala REPL
scala> scala.util.Sorting.quickSort(thingsOrdered)
scala> thingsOrdered res10: Array[ThingOrdered] = Array(ThingOrdered(1, x), ThingOrdered(1, y), ThingOrdered(4, m), ThingOrdered(4, n), ThingOrdered(33, b))

Even if a natural ordering is defined for a collection of ThingOrdered using Ordered it is still possible to invoke sortWith to perform an ad-hoc sort. As noted above this does not modify the original collection. Here we see how to use sortWith to sort according to two criteria, as implemented by two anonymous Function2[Int, String, Boolean] instances which perform the comparisons.

Scala REPL
scala> thingsOrdered.sortWith((x, y) => x.i > y.i)
res11: Array(ThingOrdered(33, b), ThingOrdered(4, m), ThingOrdered(4, n), ThingOrdered(1, x), ThingOrdered(1, y))
scala>
thingOrdereds.sortWith((x, y) => x.s > y.s) res12: Array(ThingOrdered(1, y), ThingOrdered(1, x), ThingOrdered(4, n), ThingOrdered(4, m), ThingOrdered(33, b))

You can run this code example by typing:

Shell
$ sbt "runMain collections.CollectionOrdered"

Output is as shown above.

Scala provides an implicit conversion that converts any Ordering instance into an Ordered value

Ordering is Often Preferred to Ordered

Scala provides the Ordering trait for objects that need multiple sorting strategies. It is often better to use Ordering instead of Ordered because Ordered must be implemented by the type to compare, while with Ordering you can define this ordering elsewhere.

To define the natural ordering, which is the default Ordering instance for your type, you just define an implicit ordering value in the companion object. If you want a different ordering in a certain context, define an implicit or explicit Ordering instance there. We will see an example of this in a moment.

Scala code
class ThingOrdering(val i: Int, val s: String) {
  override def toString = s"ThingOrdering($i, $s)"
}
object ThingOrdering { implicit val defaultThingOrdering: Ordering[ThingOrdering] = Ordering.by { thing: ThingOrdering => (thing.i, thing.s) } }

As you can see, Ordering.by receives a Function1[ThingOrdering, Ordering[ThingOrdering]].

Now let’s define some ThingOrdering instances and put them into a collection called thingOrderings.

Scala REPL
scala> val thingOrdering1 = new ThingOrdering(1, "x")
thingOrdering1: ThingOrdering = ThingOrdering(1, x)
scala>
val thingOrdering2 = new ThingOrdering(1, "y") thingOrdering2: ThingOrdering = ThingOrdering(1, y)
scala>
val thingOrdering3 = new ThingOrdering(33, "b") thingOrdering3: ThingOrdering = ThingOrdering(33, b)
scala>
val thingOrdering4 = new ThingOrdering(4, "m") thingOrdering4: ThingOrdering = ThingOrdering(4, m)
scala>
val thingOrdering5 = new ThingOrdering(4, "n") thingOrdering5: ThingOrdering = ThingOrdering(4, n)
scala>
val thingOrderings = Array(thingOrdering1, thingOrdering2, thingOrdering3, thingOrdering4, thingOrdering5) thingOrderings: Array[ThingOrdering] = Array(ThingOrdering(1, x), ThingOrdering(1, y), ThingOrdering(33, b), ThingOrdering(4, m), ThingOrdering(4, n))

We can use the implicit Ordering defined in the companion object to sort the collection of ThingOrdering according to its natural order using the ordered method.

Scala REPL
scala> thingOrderings.sorted
res13: Array[ThingOrdering] = Array(ThingOrdering(1, x), ThingOrdering(1, y), ThingOrdering(4, m), ThingOrdering(4, n), ThingOrdering(33, b)) 

To show how Ordering allows additional sort orderings beyond the natural sort ordering, let’s define two sort schemes.

Scala REPL
scala> val orderByI = Ordering.by { thing: ThingOrdering => thing.i }
orderByI: scala.math.Ordering[ThingOrdering] = scala.math.Ordering$$anon$9@9083a57
scala>
val orderByS = Ordering.by { thing: ThingOrdering => thing.s } orderByS: scala.math.Ordering[ThingOrdering] = scala.math.Ordering$$anon$9@27daad50

Now we can sort the thingOrderings collection by each scheme.

Scala REPL
scala> thingOrderings.sorted(orderByI)
res14: Array(ThingOrdering(1, x), ThingOrdering(1, y), ThingOrdering(4, m), ThingOrdering(4, n), ThingOrdering(33, b))
scala>
thingOrderings.sorted(orderByS) res15: Array[ThingOrdering] = Array(ThingOrdering(33, b), ThingOrdering(4, m), ThingOrdering(4, n), ThingOrdering(1, x), ThingOrdering(1, y))

You can run this code example by typing:

Shell
$ sbt "runMain collections.CollectionOrdering"

Output is as shown above.

Orderings Are Sensitive to Initialization Issues

This is not a problem for Scala 3, only for Scala 2.

Orderings defined in the constructor of a console App cannot be imported into another context because App implements some unique initialization logic. There is no compiler error or warning. Here is a typical runtime error message, and I highlighted the portions of the stack trace that give a clue as to what the problem actually is.

Error
Exception in thread "main" java.lang.ClassCastException: collections.ThingOrdering cannot be cast to java.lang.Comparable
    at java.util.ComparableTimSort.countRunAndMakeAscending(ComparableTimSort.java:316)
    at java.util.ComparableTimSort.sort(ComparableTimSort.java:184)
    at java.util.Arrays.sort(Arrays.java:1246)
    at java.util.Arrays.sort(Arrays.java:1433)
    at scala.collection.SeqLike$class.sorted(SeqLike.scala:618)
    at scala.collection.mutable.ArrayOps$ofRef.sorted(ArrayOps.scala:186)
    at collections.CollectionOrdering$.delayedEndpoint$collections$CollectionOrdering$1(CollectionSorting.scala:26)
    at collections.CollectionOrdering$delayedInit$body.apply(CollectionSorting.scala:21)
    at scala.Function0$class.apply$mcV$sp(Function0.scala:40)
    at scala.runtime.AbstractFunction0.apply$mcV$sp(AbstractFunction0.scala:12)
    at scala.App$$anonfun$main$1.apply(App.scala:76)
    at scala.App$$anonfun$main$1.apply(App.scala:76)
    at scala.collection.immutable.List.foreach(List.scala:381)
    at scala.collection.generic.TraversableForwarder$class.foreach(TraversableForwarder.scala:35)
    at scala.App$class.main(App.scala:76)
    at collections.CollectionOrdering$.main(CollectionSorting.scala:21)
    at collections.CollectionOrdering.main(CollectionSorting.scala)

We will see this initialization issue again with concurrency in later lectures. Please ensure that you either define Orderings in an object that does not extend App, or you define them in a trait or a class.

Defining Orderings

Because I want to reuse orderByI and orderByS for this section, I had to define them in the collections package object. I placed all of the Orderings there for consistency, and to emphasize best practices.

Scala code
val orderByI:        Ordering[ThingOrdering] = Ordering.by { _.i } // cannot import if defined in App
val orderByS:        Ordering[ThingOrdering] = Ordering.by { _.s } // cannot import if defined in App
val orderByIReverse: Ordering[ThingOrdering] = orderByI.reverse
val orderBySReverse: Ordering[ThingOrdering] = orderByS.reverse
val orderBySandI:    Ordering[ThingOrdering] = Ordering.by { x => (x.s, x.i) }
val orderByIandS:    Ordering[ThingOrdering] = Ordering.by { x => (x.i, x.s) }

Notice that I defined four other Orderings.

  • orderByIReverse and orderBySReverse are transformed Orderings such that they sort in the reverse order of orderByI and orderByS.
  • orderBySandI and orderByIandS sort by two keys, expressed as Tuple2s: the former sorts by s as the primary key, and i as the secondary key, and the latter does the opposite.

Sort results for all of these Orderings are.

Scala REPL
scala> thingOrderings.sorted(orderByI)
res16: Array[ThingOrdering] = Array(ThingOrdering(1, x), ThingOrdering(1, y), ThingOrdering(4, m), ThingOrdering(4, n), ThingOrdering(33, b))
scala>
thingOrderings.sorted(orderByIReverse) res17: Array[ThingOrdering] = Array(ThingOrdering(33, b), ThingOrdering(4, m), ThingOrdering(4, n), ThingOrdering(1, x), ThingOrdering(1, y))
scala>
thingOrderings.sorted(orderByS) res18: Array[ThingOrdering] = Array(ThingOrdering(33, b), ThingOrdering(4, m), ThingOrdering(4, n), ThingOrdering(1, x), ThingOrdering(1, y))
scala>
thingOrderings.sorted(orderBySReverse) res19: Array[ThingOrdering] = Array(ThingOrdering(1, y), ThingOrdering(1, x), ThingOrdering(4, n), ThingOrdering(4, m), ThingOrdering(33, b))
scala>
thingOrderings.sorted(orderBySandI) res20: Array[ThingOrdering] = Array(ThingOrdering(33, b), ThingOrdering(4, m), ThingOrdering(4, n), ThingOrdering(1, x), ThingOrdering(1, y))
scala>
thingOrderings.sorted(orderByIandS) res21: Array[ThingOrdering] = Array(ThingOrdering(1, x), ThingOrdering(1, y), ThingOrdering(4, m), ThingOrdering(4, n), ThingOrdering(33, b))
scala>
thingOrderings.sorted(orderBySandI.reverse) res22: Array[ThingOrdering] = Array(ThingOrdering(1, y), ThingOrdering(1, x), ThingOrdering(4, n), ThingOrdering(4, m), ThingOrdering(33, b))
scala>
thingOrderings.sorted(orderByIandS.reverse) res23: Array[ThingOrdering] = Array(ThingOrdering(33, b), ThingOrdering(4, n), ThingOrdering(4, m), ThingOrdering(1, y), ThingOrdering(1, x))

math.Ordering.Implicits

Importing math.Ordering.Implicits._ makes 3 methods implicitly available:

  1. infixOrderingOps – allows you to write infix operators between instances that you are comparing that do not provide those operators. The operator are: <, >, <=, >=, == and !=. This import also defines additional implicit orderings, in particular Orderings of Comparable implementations.
  2. seqOrdering – can cause problems with ordering, import with caution. If you just want to import infixOrderingOps, I suggest you just write:
    Scala code
    import math.Ordering.Implicits.infixOrderingOps
  3. sortedSetOrdering – invokes the same implementation as seqOrdering, but applies the Ordering to a SortedSet instead of a Seq. The same cautionary message applies to sortedSetOrdering.
Scala REPL
scala> (1, "b") < (1, "a")
<console>:8: error: value < is not a member of (Int, String)
              (1, "b") < (1, "a")
                        ^
scala>
import math.Ordering.Implicits.infixOrderingOps import math.Ordering.Implicits.infixOrderingOps
scala>
(1, "b") < (1, "a") res1: Boolean = false

Total Orderings

New for Scala 2.13 is scala.math.Ordering.Double.TotalOrdering and Ordering.Float.TotalOrdering. I’ve paraphrased the Scaladoc, combining the two descriptions into one.

An ordering for Floats/Doubles which is a fully consistent total ordering, and treats NaN as larger than all other Float/Double values; it behaves the same as java.lang.Float/Double.compare().

Because the behaviour of Floats/Doubles specified by IEEE is not consistent with a total ordering when dealing with NaN, there are two orderings defined for Float/Double: TotalOrdering, which is consistent with a total ordering, and IeeeOrdering, which is consistent as much as possible with IEEE spec and floating point operations defined in scala.math.

These orderings may be preferable for sorting collections.

enum Value Sets

As we learned in the Enumerations lecture of the Introduction to Scala course, Java enums can be used to great benefit by Scala programs.

Here is a Java enum stored in a file called src/main/java/Day.java. It is similar to Java 8’s DayOfWeek enum.

Java code
public enum Day {
    SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY
}

Here is how we can obtain a Set[Day] containing all values of Day.

Scala REPL
scala> val valueSet = Day.values.toSet
valueSet: scala.collection.immutable.Set[Day] =Set(TUESDAY, SATURDAY, WEDNESDAY, MONDAY, FRIDAY, SUNDAY, THURSDAY) 

Java Enum implements Comparable. Ordinarily it is awkward to compare two Java enums.

Scala REPL
scala> Day.MONDAY.compareTo(Day.FRIDAY)
res4: Int = -4
scala>
Day.MONDAY.compareTo(Day.MONDAY) res5: Int = 0
scala>
Day.FRIDAY.compareTo(Day.MONDAY) res6: Int = 4

After importing infixOrderingOps you can compare two Days using infix notation and the comparison operators that infixOrderingOps provides.

Scala REPL
scala> import math.Ordering.Implicits.infixOrderingOps
import scala.math.Ordering.Implicits.infixOrderingOps
scala>
Day.MONDAY < Day.FRIDAY res3: Boolean = true

What’s more, if we import all the values of Day, like this:

Scala REPL
scala> import Day._
import Day._ 

Then we can write:

Scala REPL
scala> MONDAY < FRIDAY
res4: Boolean = true 

We can use this fact and the imported implicit Ordering for Comparable to create a SortedSet of the values. Recall that a collection’s elements are unpacked and supplied as arguments to a method by following the collection name with :_*.

Scala REPL
scala> val values: collection.immutable.SortedSet[Day] = collection.immutable.TreeSet(Day.values:_*)
values: scala.collection.immutable.SortedSet[Day] = TreeSet(SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY) 

PriorityQueue

Now that we have learned about Ordering, we can discuss the PriorityQueue mutable collection type, left over from the Mutable Collections lecture.

Let’s remind ourselves of the contents of thingOrderings, sorted according to the natural Ordering of ThingOrdering.

Scala REPL
scala> thingOrderings.sorted.mkString(", ")
res0: String = ThingOrdering(1, x), ThingOrdering(1, y), ThingOrdering(4, m), ThingOrdering(4, n), ThingOrdering(33, b) 

If we make a PriorityQueue of ThingOrderings, the implicit Ordering in the ThingOrderings companion object is used to determine the order of the items to dequeue. Notice that once pq1 is created, its contents are displayed in the reverse of the natural Ordering. This is because PriorityQueue returns the highest priority item, which is the item that sorts into first position.

Scala REPL
scala> val pq1 = mutable.PriorityQueue(thingOrderings:_*)
pq1: scala.collection.mutable.PriorityQueue[collections.ThingOrdering] = PriorityQueue(ThingOrdering(33, b), ThingOrdering(4, n), ThingOrdering(1, y), ThingOrdering(1, x), ThingOrdering(4, m)) 

Now I’ll just keep pulling items from the PriorityQueue instance by calling pq1.dequeue() until NoSuchElementException is thrown. Items are returned in the reverse of the natural Ordering.

Scala REPL
scala> pq1.dequeue()
res1: collections.ThingOrdering = ThingOrdering(33, b)
scala>
pq1.dequeue() res2: collections.ThingOrdering = ThingOrdering(4, n)
scala>
pq1.dequeue() res3: collections.ThingOrdering = ThingOrdering(4, m)
scala>
pq1.dequeue() res4: collections.ThingOrdering = ThingOrdering(1, y)
scala>
pq1.dequeue() res5: collections.ThingOrdering = ThingOrdering(1, x)
scala>
pq1.dequeue() java.util.NoSuchElementException: no element to remove from heap at scala.collection.mutable.PriorityQueue.dequeue(PriorityQueue.scala:139) ... 43 elided

There are other ways to dequeue all the elements without throwing an Exception, of course, which we will see in a moment.

You can explicitly specify the Ordering when creating the PriorityQueue. This code example first reminds us what the contents of thingOrderings looks like when sorted according to orderByI. We then create pq2 using orderByI instead of the natural ordering of ThingOrdering to prioritize the queue. We then see one way of retrieving all of the elements from the PriorityQueue in the reverse Ordering defined by orderByI. The foreach expression iterates over the collection and prints out each item in order.

Scala REPL
scala> thingOrderings.sorted(orderByI).mkString(", ")
res7: String = ThingOrdering(1, x), ThingOrdering(1, y), ThingOrdering(4, m), ThingOrdering(4, n), ThingOrdering(33, b)
scala>
val pq2 = mutable.PriorityQueue(thingOrderings:_*)(orderByI) pq2: scala.collection.mutable.PriorityQueue[collections.ThingOrdering] = PriorityQueue(ThingOrdering(33, b), ThingOrdering(4, m), ThingOrdering(1, x), ThingOrdering(1, y), ThingOrdering(4, n))
scala>
pq2.dequeueAll.foreach { item => println(s"""pq2.dequeue()=$item""") } pq2.dequeue()=ThingOrdering(33, b) pq2.dequeue()=ThingOrdering(4, n) pq2.dequeue()=ThingOrdering(4, m) pq2.dequeue()=ThingOrdering(1, x) pq2.dequeue()=ThingOrdering(1, y)

This code example uses the orderByS Ordering to define the PriorityQueue’s behavior. All of the items are individually dequeued in a foreach loop.

Scala REPL
scala> thingOrderings.sorted(orderByS).mkString(", ")
ThingOrdering(33, b), ThingOrdering(4, m), ThingOrdering(4, n), ThingOrdering(1, x), ThingOrdering(1, y)
scala>
val pq3 = mutable.PriorityQueue(thingOrderings:_*)(orderByS) pq3: scala.collection.mutable.PriorityQueue[collections.ThingOrdering] = PriorityQueue(ThingOrdering(1, y), ThingOrdering(1, x), ThingOrdering(33, b), ThingOrdering(4, m), ThingOrdering(4, n))
scala>
(0 until pq3.length).foreach { _ => println(s"""pq3.dequeue()=${pq3.dequeue()}""") } pq3.dequeue()=ThingOrdering(1, y) pq3.dequeue()=ThingOrdering(1, x) pq3.dequeue()=ThingOrdering(4, n) pq3.dequeue()=ThingOrdering(4, m) pq3.dequeue()=ThingOrdering(33, b)

Discovering the Default Implicit Ordering with Implicitly (Supplemental)

This only applies to Scala 2, not Scala 3.

As we saw in the lecture on Implicit Conversions, the implicitly keyword returns the implicit value in scope for a given type. Let’s use the default implicit Ordering (defined in the scala package object, so it is available to all programs) to compare two tuples.

Scala REPL
scala> val ordering = implicitly[Ordering[(Int, String)]]
ordering: Ordering[(Int, String)] = scala.math.Ordering$$anon$11@12e6ca10
scala>
ordering.compare( (1, "b"), (1, "a") ) res22: Int = 1
scala>
ordering.compare( (1, "b"), (1, "b") ) res23: Int = 0
scala>
ordering.compare( (1, "b"), (1, "c") ) res24: Int = -1
scala>
implicitly[Ordering[Day]].compare(Day.MONDAY, Day.WEDNESDAY) res25: Int = -2

* indicates a required field.

Please select the following to receive Mike Slinn’s newsletter:

You can unsubscribe at any time by clicking the link in the footer of emails.

Mike Slinn uses Mailchimp as his marketing platform. By clicking below to subscribe, you acknowledge that your information will be transferred to Mailchimp for processing. Learn more about Mailchimp’s privacy practices.