Scala - value < is not a member of AnyVal

Viewed 3465

Here is my code. I got a compile error at second case clause:

Error:(92, 26) value < is not a member of AnyVal case x if x._2 < 2 => "price under 2"

def tupleMatch: Unit = {
  val donut = Tuple2("Donut", 2.5)
  val plain = Tuple2("Plain", 1.0)
  val tuples = List(donut, plain)
  tuples.foreach { tuple => 
    val toPrint = tuple match {
      case ("Donut", price) => s"price of Donut is ${donut._2}"
      case (_, price) if price < 2 => "price under 2"
      case _ => "other"
    }
    println(toPrint)

  }
}

After I change

val plain = Tuple2("Plain", 1)

to

val plain = Tuple2("Plain", 1.0)

it finally works. It makes me confusing, I want to know why?

3 Answers

That is because your tuples array is formed by two different types: Tuple2[String, Int] and Tuple2[String, Double]. These types are inferred by the compiler and the interfered type of tuples array is then Tuple2[String, AnyVal]. When you put a Double representation the compiler is capable of create the Tuple2[String, Double].

Looking at the REPL:

scala> val donut = Tuple2("Donut", 2.5)
donut: (String, Double) = (Donut,2.5)

scala> val plain = Tuple2("Plain", 1.0)
plain: (String, Double) = (Plain,1.0)

scala> val tuples = List(donut, plain)
tuples: List[(String, Double)] = List((Donut,2.5), (Plain,1.0))

Because the two Tuples have the same type (String, Double), the List is of type List[(String, Double)]. < is a valid function for Double.

When you define plain as Tuple2("Plain", 1), the second part of the two Tuples are of different types (Int vs Double) so the resulting List is List[(String, AnyVal)]:

scala> val donut = Tuple2("Donut", 2.5)
donut: (String, Double) = (Donut,2.5)

scala> val plain = Tuple2("Plain", 1)
plain: (String, Int) = (Plain,1)

scala> val tuples = List(donut, plain)
tuples: List[(String, AnyVal)] = List((Donut,2.5), (Plain,1))

... and < is not a valid function for AnyVal.

It because of we cannot perform < (less than) operation on AnyVal type.

scala> val donut = Tuple2("Donut", 2.5)
donut: (String, Double) = (Donut,2.5)

scala> val plain = Tuple2("Plain", 1)  
plain: (String, Int) = (Plain,1)

As you can see above. When you define Tuple2("Donut", 2.5), 2.5 is inferred to Double and in Tuple2("Plain", 1), 1 is inferred as Int since you have not provided type explicitly.

Now, when you create list of these tuples, because of type inference list type will be (String, AnyVal).

scala> val tuples = List(donut, plain)
tuples: List[(String, AnyVal)] = List((Donut,2.5), (Plain,1))

Now, when you are performing case x if x._2 < 2 you are actually comparing AnyVal type to number i.e. 2, which is not possible because compiler thinks that x._2 is AnyVal(not number) and won't compile. Therefore, you are getting exception at compile time.

<console>:22: error: value < is not a member of AnyVal
                 case x if (x._2 < 2) => "price under 2"

In your second case, i.e. when you defined tuple as Tuple2("Plain", 1.0), its number type is also inferred as Double.

scala> val donut = Tuple2("Donut", 2.5)
donut: (String, Double) = (Donut,2.5)

scala> val plain = Tuple2("Plain", 1.0)
plain: (String, Double) = (Plain,1.0)

And, when you create list of tuples, since both have same type i.e. (String, Double), list will have type List[(String, Double)].

scala> val tuples = List(donut, plain)
tuples: List[(String, Double)] = List((Donut,2.5), (Plain,1.0))

And x._2 < 2 will compile correctly since you are invoking < in double (number).

Related