0

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
Manu Chadha
  • 15,555
  • 19
  • 91
  • 184

1 Answers1

2

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" }
Oleg Pyzhcov
  • 7,323
  • 1
  • 18
  • 30
  • Thanks. Does `implicit view: T => Ordered[T]` means implicit conversion and `implicit val order: Ordering[T]` means implicit value? I am reading this other question to understand the difference between implicit value and implicit conversion - https://stackoverflow.com/questions/8524878/implicit-conversion-vs-type-class. – Manu Chadha Dec 05 '17 at 06:38
  • @ManuChadha `implicit view T => Ordered[T]` means both "implicit value, which is a function" and "implicit conversion". This is a bit confusing, and IIRC it will be changed in Dotty/Scala 3. `implicit order: Ordering[T]` here means only "implicit value" – Oleg Pyzhcov Dec 05 '17 at 06:44