7

I need a method to return the first of two ordered values. I've tried:

def first[T <: Ordered[T]](a: T, b: T) = {
  a compare b match {
    case -1 | 0 => a
    case 1      => b
  }
}

but get

scala> first(3,4)
<console>:9: error: inferred type arguments [Int] do not conform to method first's 
type parameter bounds [T <: Ordered[T]]
       first(3,4)
       ^

I guess this is because Int needs to be converted to a RichInt, which is an Ordered[Int] rather than an Ordered[RichInt]. What next?

Luigi Plinge
  • 50,650
  • 20
  • 113
  • 180

3 Answers3

8

You can use type class Ordering and context bound:

def first[T : Ordering](a: T, b: T) = {
  implicitly[Ordering[T]].compare(a, b) match {
    case -1 | 0 => a
    case 1      => b
  }
}

Update

This code can be simplified further if you import scala.math.Ordered._. Companion object of Ordered has orderingToOrdered implicit conversion, so everything that has Ordering would also be treated as Ordered:

import scala.math.Ordered._

def first[T : Ordering](a: T, b: T) = if (a <= b) a else b
tenshi
  • 26,268
  • 8
  • 76
  • 90
8

I think you're looking for a view bound

def first[T <% Ordered[T]](a: T, b: T) = {
  a compare b match {
  case -1 | 0 => a
  case 1      => b
  }
}

Then,

scala> first(3, 2)
res3: Int = 2

Behind the scenes, a view bound will compile into a implicit parameter that converts type T into Ordered[T]. Daniel Sobral's answer has a nice explanation: What are Scala context and view bounds?

Edit. Introduced in Scala 2.8, Ordering may be the preferred way to do comparisons, but I couldn't find any definitive guidance. I guess Ordered has the advantage of compatibility with Java's Comparable, and as others have pointed out, there are implicit conversions between Ordered and Ordering.

Community
  • 1
  • 1
Kipton Barros
  • 21,002
  • 4
  • 67
  • 80
  • About context vs view bounds - in Daniel's answer (that you have linked) he writes: "The context bound with the typeclass pattern is much more likely to be used by your own classes, as they enable separation of concerns, whereas view bounds can be avoided in your own code by good design (it is used mostly to get around someone else's design).". – tenshi Aug 15 '11 at 15:54
  • In this case, the view bound is used to get around Java's design that `Int` (as a primitive) doesn't implement `Ordered`. – Kipton Barros Aug 15 '11 at 19:14
1

You can simply use implicit parameters and using ordering methods to make it clearer:

def first[T](a: T, b: T)( implicit ord: Ordering[T] ) = {
  import ord._
  if( a <= b ) a else b
}

or if you don't care which one is returned in case of equality:

def first[T](a: T, b: T)( implicit ord: Ordering[T] ) = 
  ord.min(a,b)
paradigmatic
  • 40,153
  • 18
  • 88
  • 147