0

I'm working exercises from the book Learning Scala and one question asks:

A popular use for implicit parameters is for a default setting that works most of the time but may be overridden in special cases. Assume you are writing a sorting function that takes lines of text, and the lines may start with a right-aligned number. If you want to sort using the numbers, which may be prefixed by spaces, how would you encode this ability in an implicit parameter? How would you allow users to override this behavior and ignore the numbers for sorting?

My solution:

def sortLines(l: List[String])(implicit o: Ordering[String]) = l.sorted

Unit test using Scala Test:

"Ch10" should "order lines starting with right-aligned numbers" in {
    val l = List(" 2 a", " 1 b", " 3 c")

    implicit val orderingByNumber: Ordering[String] = Ordering.by(_.trim.split("\\s")(0).toInt)
    val orderingIgnoringNumber: Ordering[String] = Ordering.by(_.trim.split("\\s")(1))

    Ch10.sortLines(l) should contain inOrder(" 1 b", " 2 a", " 3 c")

    Ch10.sortLines(l)(orderingIgnoringNumber) should contain inOrder(" 2 a", " 1 b", " 3 c")
}

Problem is the test fails with a java.lang.NumberFormatException: For input string: "b". Why?

Abhijit Sarkar
  • 21,927
  • 20
  • 110
  • 219

1 Answers1

2

Ordering.by has the following signature:

def by[T, S](f: T => S)(implicit ord: Ordering[S]): Ordering[T]

It takes an implicit Ordering for the function result type, which in your case is String. And the implicit Ordering[String] defined at that point is orderingByNumber. So what happens is that orderingIgnoringNumber cuts the second words from the strings, and tries to order them using orderingByNumber, which in turn tries to convert them to Int, and throws an exception.

You can try this implementation for orderingIgnoringNumber:

val orderingIgnoringNumber: Ordering[String] = 
  Ordering.by((_: String).trim.split("\\s")(1))(Ordering.String)

This explicitly uses the original Ordering[String].

Kolmar
  • 14,086
  • 1
  • 22
  • 25
  • Couple questions: 1) Why did you need to explicitly specify `(_: String)`? 2) What's the implicit `Ordering[String]` used for `orderingByNumber`? – Abhijit Sarkar Feb 02 '16 at 06:10
  • 1
    @AbhijitSarkar 1) No idea. Without it the type checking fails for some reason. 2) The function passed to it produces an `Int`, so it doesn't need an `Ordering[String]`. And the `Ordering[Int]` it uses is the default one provided by Scala. – Kolmar Feb 02 '16 at 06:20