5

I have this segment of Scala code which defines an ordering and applies it to a TreeSet. This part compiles fine.

val acctOrdering = new Ordering[Account] {
  def compare(acc1: Account, acc2: Account) {

    // code to compare based on various criteria

  }
}

private var accountSet = new TreeSet[Account]()(acctOrdering)

Elsewhere in the code, I want to get the first element in the set (and later on get subsequent ones if the first one doesn't produce what I want, although that usually won't be necessary), based on the order I previously specified. I thought the following would work, but it didn't compile:

val firstAccount = accountSet.min

The error is "could not find implicit value for parameter cmp: Ordering[Account]"

However, if I specify the ordering object again when asking for the minimum, it compiles:

val firstAccount = accountSet.min(acctOrdering)

I thought it would have automatically used the ordering I gave at construction time, and incrementally sorting as I added to the set, so I wouldn't have to specify the ordering again when calling min.

What am I doing wrong? Do I need to explicitly define an implicit function somewhere?

Gigatron
  • 1,995
  • 6
  • 20
  • 27

1 Answers1

11

What is happening is that you are assuming min depends on the ordering of the set, but it doesn't. Specifically, min and max are generic methods available on pretty much any collection, and they take an implicit Ordering parameter.

However, if you try firstKey and lastKey, which are SortedSet-specific methods, they'll work without having to pass any implicit.

Edit

One question you might have posed is to how do you make sure your Account type can be ordered by any method expecting an Ordering. You can do that by putting an implicit definition inside Account's object companion, like this:

object Account {
  implicit val ord = new Ordering[Account] {
    def compare(ac1: Account, acc2: Account): Int = {
      // code to compare based on various criteria
    }
  }
}

Once you do that, you won't need to pass the ordering explicitly.

Daniel C. Sobral
  • 295,120
  • 86
  • 501
  • 681
  • Thanks. Changing `min` to `firstKey` gets it to compile. There two versions of min, one without a parameter and another with an implicit. Just for my understanding of it, how would the method `def min : A` be used? – Gigatron Mar 27 '11 at 22:24
  • OK, now I think I get it ... it appears the parameterless `min` is never usable on TreeSet, because calling it without a parameter will always revert to the version of min that expects an implicit parameter. – Gigatron Mar 27 '11 at 23:34
  • @Gigatron There's only one `min` definition. The other definition you see on ScalaDoc is marked as "_use case_". That means this is not a real signature, but a "fake" signature that represents what happens most of the time. In this case, calling `min` without any parameter will work most of the time. – Daniel C. Sobral Mar 28 '11 at 14:18
  • @Gigatron However, I see now that I could have been more helpful in my answer, so see the edit for more information. – Daniel C. Sobral Mar 28 '11 at 14:20
  • Thanks for the clarification, especially about "use case". In this situation the particular ordering is for a very specific purpose that isn't useful to the rest of the application, so I won't put the ordering in Account itself. – Gigatron Mar 28 '11 at 18:06
  • what's the return type of compare()? In Java compareTo returns a boolean. – Adrian Jun 06 '13 at 14:14
  • @Adrian The same as in Java: an `Int`, not a `Boolean` as you claimed. Note that `Ordering` is equivalent to Java's `Comparator`, not `Comparable`, and `Comparable`'s `compare` method _also_ returns an `Int`. And both JavaDoc and ScalaDoc document the exact meaning of the `Int` returned. – Daniel C. Sobral Jun 07 '13 at 13:48
  • @DanielC.Sobral ah you're right! It's an int.. I must've been really tired. My confusion was bc I didn't see a return type on the compare method; I assumed it was Unit... which didn't make sense. – Adrian Jun 07 '13 at 13:56
  • 1
    @Adrian Well, leaving the return type out on a public method is bad form, so I fixed that now. – Daniel C. Sobral Jun 07 '13 at 14:30