24

Given:

case class Person(name: String)

and trying to do:

scala> List(Person("Tom"), Person("Bob")).sorted

results in a complaint about missing Ordering.

<console>:8: error: could not find implicit value for parameter ord: Ordering[Person]
   List(Person("Tom"), Person("Bob")).sorted

However this:

case class Person(name: String) extends Ordered[Person] {
  def compare(that: Person) = this.name compare that.name }

works fine as expected:

scala> List(Person("Tom"), Person("Bob")).sorted
res12: List[Person] = List(Person(Bob), Person(Tom))

although there's no Ordering or implicits involved.

Question #1: what's going on here? (My money is on something implicit...)

However, given the above and the fact that this:

scala> Person("Tom") > Person("Bob")
res15: Boolean = true

works, and that also this:

scala> List(Some(2), None, Some(1)).sorted

works out of the box:

res13: List[Option[Int]] = List(None, Some(1), Some(2))

I would expect that this:

scala> Some(2) > Some(1)

would also work, however it does not:

<console>:6: error: value > is not a member of Some[Int]
       Some(2) > Some(1)

Question #2: why not, and how can I get it to work?

Knut Arne Vedaa
  • 15,372
  • 11
  • 48
  • 59

6 Answers6

29

If you install the slightly-too-magical-for-default-scope bonus implicits, you can compare options like so:

scala> import scala.math.Ordering.Implicits._
import scala.math.Ordering.Implicits._

scala> def cmpSome[T: Ordering](x: Option[T], y: Option[T]) = x < y
cmpSome: [T](x: Option[T], y: Option[T])(implicit evidence$1: Ordering[T])Boolean

The import gives you an implicit from an Ordering to the class with the infix operations, so that it's enough to have the Ordering without another import.

psp
  • 12,138
  • 1
  • 41
  • 51
  • Better...but still a lot of work, in my maybe not so humble opinion. :) I understand there may be reasons for why it is this way, but from a user's viewpoint it would seem to be logical that you could compare two Options without any fuzz given that you can *sort* a list of them without any fuzz...since you have to compare in other to sort, right? – Knut Arne Vedaa Sep 29 '11 at 20:59
  • 1
    You can sort them because you call a method which takes an implicit ordering. Here you are writing a method which takes an implicit ordering. Somewhere, an ordering must enter the picture, because arbitrary Option[T]s are not comparable. – psp Sep 29 '11 at 21:17
11

Concerning your first question: Ordered[T] extends Comparable[T]. The Ordering companion object provides an implicit Ordering[T] for any value that can be converted into a Comparable[T]:

implicit def ordered[A <% Comparable[A]]: Ordering[A]

There is no implicit conversion A : Ordering => Ordered[A] - that's why Some(1) > Some(2) will not work.

It is questionable if it is a good idea to define such a conversion as you may end up wrapping your objects into Ordered instances and then create an Ordering of that again (and so on...). Even worse: you could create two Ordered instances with different Ordering instances in scope which is of course not what you want.

Moritz
  • 14,144
  • 2
  • 56
  • 55
3

The definition of List's sorted method is:

def sorted [B >: A] (implicit ord: Ordering[B]): List[A]

So yes, implicit things are happening, but many classes in the standard library have implicit objects associated with them without you having to import them first.

The Ordering companion object defines a bunch of implicit orderings. Among these is an OptionOrdering and IntOrdering, which helps explain the ability of a list to call sorted.

To gain the ability to use operators when there is an implicit conversion available, you need to import that object, for example:

def cmpSome(l:Option[Int], r:Option[Int])(implicit ord:Ordering[Option[Int]]) = {
  import ord._
  l < r
}

scala> cmpSome(Some(0), Some(1))
res2: Boolean = true
Dylan
  • 13,645
  • 3
  • 40
  • 67
  • That doesn't really answer the question why implementing Ordered[T] magically brings about an instance of Ordering[T]. And why do you have to create a method in order to do the last comparison? – Knut Arne Vedaa Sep 29 '11 at 17:55
  • I made the method so I could get access to the instance of Ordering. I import the contents of that instance so I can get its implicit conversion to an `Ops`, which defines some comparison operators. Using that same method, I finally figured out exactly where the implicit conversion for an `Ordered` is. http://www.scala-lang.org/api/current/index.html#scala.math.LowPriorityOrderingImplicits – Dylan Sep 29 '11 at 18:35
  • But that seems quite a lot of work just to be able to compare two Options? – Knut Arne Vedaa Sep 29 '11 at 18:55
  • `import ord._` is exactly what I wanted – sid-kap Jul 28 '16 at 19:32
2

To answer your second question, why can't you do this: Some(2) > Some(1)

You can, with an import and working with Option[Int] rather than Some[Int].

@ import scala.math.Ordering.Implicits._ 
import scala.math.Ordering.Implicits._
@ Some(2) > Some(1) // doesn't work
cmd11.sc:1: value > is not a member of Some[Int]
val res11 = Some(2) > Some(1)
                    ^
Compilation Failed
@ (Some(2): Option[Int]) > (Some(1): Option[Int]) // Option[Int] works fine
res11: Boolean = true
@ Option(2) > Option(1) 
res12: Boolean = true
@ (None: Option[Int]) > (Some(1): Option[Int]) 
res13: Boolean = false

In practise your types will probably be of Option[Int] rather than Some[Int] so it won't be so ugly and you won't need the explicit upcasting.

David
  • 1,862
  • 2
  • 22
  • 35
0

I assume you understand why sorted does not work when you do not pass in an Ordering and none is available in scope. As to why the sorted function works when you extend your class from Ordered trait. The answer is that when you extend from Ordered trait, the code type checks as the trait contains function like <,> etc. So there is no need to do implicit conversion and hence no complains about the missing implicit Ordering.

As for your second question, Some(2) > Some(1) will not work because Some does not extend the trait Ordered, neither does there seem to be any implicit function in scope that implicitly converts a Some to something that has the function >

0

Thanks for a detailed question with examples.

My answer is based what I learnt from a great article here: http://like-a-boss.net/2012/07/30/ordering-and-ordered-in-scala.html

All credit to the author here.

Quoting the article:

Coming back to our Box example - the scala library defines an implicit conversion between Ordered[T] and Ordering[T] and vice-versa.

The companion object of Ordered in https://github.com/scala/scala/blob/2.12.x/src/library/scala/math/Ordered.scala provides the required conversion here:

/** Lens from `Ordering[T]` to `Ordered[T]` */ implicit def orderingToOrdered[T](x: T)(implicit ord: Ordering[T]): Ordered[T] = new Ordered[T] { def compare(that: T): Int = ord.compare(x, that) }

However the reverse conversion isn't defined and I am not sure why?

Xolve
  • 22,298
  • 21
  • 77
  • 125