21

Is there a reason why there is no implicit Ordering for Lists in Scala?

val lists = List(List(2, 3, 1), List(2, 1, 3))
lists.sorted

error: could not find implicit value for parameter ord: Ordering[List[Int]]

EDIT

Yes, my question is why there is no built-in Ordering that's already implicitly in scope. To me, it seems obvious that the second list should be "less than" the first list since the items at 0 are equal and the second list has the lower item at 1. I was wondering if maybe it's that there's no good answer when the Lists are two different sizes.

Craig P. Motlin
  • 26,452
  • 17
  • 99
  • 126
  • 2
    Lexicographic ordering (which you describe) is definable for differently sized lists, although you need to make a choice as to whether the shorter lists are greater than or lesser than longer lists. Both ways can be used to create mathematically valid orders, and while the shorterlonger option has uses as well. This might be the reason why there's no implicit ordering provided on Lists in the standard library, but I's still bet that lack of general usefulness is a more likely reason. – Dave Griffith Dec 20 '10 at 20:24

7 Answers7

45

I think it's an oversight. Lexicographic ordering does make sense on Seqs. We should add it to the standard library.

Martin Odersky
  • 20,470
  • 9
  • 51
  • 49
8

Incidentally even before I fixed this you could do this other ways:

scala> List[Iterable[Int]](List(2, 3, 1), List(2, 1, 3)).sorted
res0: List[Iterable[Int]] = List(List(2, 1, 3), List(2, 3, 1))

scala> List(List(2, 3, 1), List(2, 1, 3)).sorted(Ordering[Iterable[Int]])
res1: List[List[Int]] = List(List(2, 1, 3), List(2, 3, 1))

But now it works like you'd hope.

Edit: due to sketchy divergence issues with the requisite implicit I moved it out of the default scope. Having an implicit conversion which acts across a bound like this:

implicit def SeqDerived[CC[X] <: collection.Seq[X], T](implicit ord: Ordering[T]): Ordering[CC[T]]

...is a potential recipe for issues. It'll be available in 2.9, but you have to import it as follows.

scala> val lists = List(List(2, 3, 1), List(2, 1, 3))
lists: List[List[Int]] = List(List(2, 3, 1), List(2, 1, 3))

scala> lists.sorted
<console>:9: error: could not find implicit value for parameter ord: Ordering[List[Int]]
       lists.sorted
             ^

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

scala> lists.sorted
res1: List[List[Int]] = List(List(2, 1, 3), List(2, 3, 1))
psp
  • 12,138
  • 1
  • 41
  • 51
4

What you have is a list of lists, not a list of integers. What you are missing is a criteria for determining whether a list is <= another list, or not.

That's what the error message says: I can't find a way to compare a list to another list, you should provide one explicitly.

If your question was "why don't list have a built-in comparison method against other lists", well, that's just the way it is.

salezica
  • 74,081
  • 25
  • 105
  • 166
4

The only really sensible total order over the class of List[Int] would be lexicographic (i.e, compare the first elements of the list, then the second if they are equal, the third if the seconds are equal, etc.). This isn't provided by the standard library, probably because there aren't that many cases where it's actually needed. It would be easy enough to create an implicit conversion from List[X] to Ordering[List[X]] that would implement that, and then you could simply import that conversion wherever you needed it.

Dave Griffith
  • 20,435
  • 3
  • 55
  • 76
1

You can use sortWith. This doesn't take differently sized lists into account because zip will throw out the difference, but I think it does something like what you're after:

lists.sortWith((a,b) => {
  a.zip(b).filterNot(x => x._1 == x._2) match {
    case Nil => true
    case t => t._1 < t._2
  }
})
Janx
  • 3,285
  • 3
  • 19
  • 24
0

In newer Scala versions (tested with 2.12.5) there's an Ordering for Iterable[A]. Just ascribe the right type to your variable lists:

scala> val lists = List(List(2, 3, 1), List(2, 1, 3))
lists: List[List[Int]] = List(List(2, 3, 1), List(2, 1, 3))

scala> (lists: List[Iterable[Int]]).sorted
res0: List[Iterable[Int]] = List(List(2, 1, 3), List(2, 3, 1))

Or convert the elements to instances of Iterable[] (which is a no-op for instances of List[]):

scala> lists.map(_.toIterable).sorted
res1: List[Iterable[Int]] = List(List(2, 1, 3), List(2, 3, 1))
Feuermurmel
  • 9,490
  • 10
  • 60
  • 90
0

Scala 2.13.3 - invoking sorted method worked for me:

import scala.math.Ordering.Implicits.seqOrdering
val lists = List(List(2, 3, 1), List(2, 1, 3))
print(lists.sorted)   // prints List(List(2, 1, 3), List(2, 3, 1)) 

sorted method is a member of StrictOptimizedSeqOps trait,:

override def sorted[B >: A](implicit ord: Ordering[B]): C
Saurav Sahu
  • 13,038
  • 6
  • 64
  • 79