Manu Chadha
Manu Chadha

Reputation: 16723

Why I could compare Strings using view bound but not upper bound?

In the following example, I could compare two Strings using view bound but not upper bound even though on REPL, the < method works for String. Is it because String is not a subclass of Ordered[T] but there is an implicit conversion from String to Ordered[String]?

//view bound example. For String, It seems to work because I suppose there is an implicit conversion from String to Ordered[String]
scala> class Person[T <% Ordered[T]](val fn:T, val ln:T){
     | def greater = if(fn > ln) fn else ln
     | }
defined class Person

scala> val p1 = new Person("manu", "chadha")
p1: Person[String] = Person@f95d64d

scala> p1.greater
res0: String = manu

scala> val p2 = new Person("anita", "chadha")
p2: Person[String] = Person@30cafd13

scala> p2.greater
res1: String = chadha

//upper bound example. It doesn't work for String. Is it because String is not a subclass of Ordered[T] but there is an implicit converstion from String to Ordered[Strig]
scala> class Person2[T <: Ordered[T]](val fn:T, val ln:T){
     | def greater = if(fn > ln) fn else ln
     | }
defined class Person2

scala> val p3 = new Person2("manu", "chadha")
<console>:12: error: inferred type arguments [String] do not conform to class Person2's type parameter bounds [T <: Ordered[T]]
       val p3 = new Person2("manu", "chadha")
                ^
<console>:12: error: type mismatch;
 found   : String("manu")
 required: T
       val p3 = new Person2("manu", "chadha")
                            ^
<console>:12: error: type mismatch;
 found   : String("chadha")
 required: T
       val p3 = new Person2("manu", "chadha")
                                    ^

I suppose view bounds are deprecated. So how does the following example work in REPL?

scala> "manu" > "Manu"
res2: Boolean = true

Upvotes: 0

Views: 63

Answers (1)

Oleg Pyzhcov
Oleg Pyzhcov

Reputation: 7353

Exactly. "a" < "b" is desugared as

scala.Predef.augmentString("a") < "b"

Where augmentString("a") is a class StringOps, which is also Ordered[String], where < comes from.


View bounds syntax is deprecated, but the implicit conversions and parameters are not. So the way your code is written in the modern Scala is just using an implicit parameter which is an implicit conversion as well:

class Person[T](val fn:T, val ln:T)(implicit view: T => Ordered[T]){
  def greater = if(fn > ln) fn else ln
}

Although it's generally considered best practice to use typeclasses. Here, we have scala.math.Ordering in standard library:

import Ordering.Implicits._ // brings operators like "<" into the scope

class Person[T](val fn: T, val ln: T)(implicit val order: Ordering[T]) {
  def greater = if (fn > ln) fn else ln
}

Which have the syntactic form of context bounds that is not deprecated:

import Ordering.Implicits._ // brings operators like "<" into the scope

class Person[T: Ordering](val fn: T, val ln: T) {
  def greater = if (fn > ln) fn else ln
}

This is the equivalent of Java's Comparable vs Comparator, except you don't have to pass Comparator instances manually

assert { new Person("manu", "chadha").greater == "manu" }

Upvotes: 2

Related Questions