tags:

views:

135

answers:

2

Is it a bug in Scala 2.8.0 ? (the same happens with 2.8.1.RC2)

import junit.framework._
import Assert._

class BugTest extends TestCase {

  def compare(first: Any, second: Any): Int = {
      (first, second) match {
        case (k: Int, o: Int) => k compare o
        //why the next case matches (Float, Int) but does not match (Int, Float) ???
        case (k: Number, o: Number) => k.doubleValue() compare o.doubleValue()
        case _ => throw new Exception("Unsupported compare " + first + "; " + second)
    }
  }

  def testCompare() {
    assertEquals("Both Int", -1, compare(0, 1))
    assertEquals("Both Float", 1, compare(1.0, 0.0))
    assertEquals("Float then Int", 0, compare(10.0, 10))
    assertEquals("Int then Float", 0, compare(10, 10.0))//this fails with an exception
  }
}
+2  A: 

I don't know the answer to the specific question, but here's a different way to define compare:

def compare[A : Numeric, B: Numeric](first: A, second: B): Int =
  implicitly[Numeric[A]].toDouble(first) compare implicitly[Numeric[B]].toDouble(second)

scala> compare(0.0,1.0)
res30: Int = -1

scala> compare(0.0,1)
res31: Int = -1

scala> compare(0,1.0)
res32: Int = -1

scala> compare(0,1)
res33: Int = -1
IttayD
Does this guarantee that compare(1, 1) always returns 0 ?
For Ints, yes. For doubles it gives you the same guarantees as comparing any double value.
IttayD
Thank you for the reply. Unfortunately my usecase is much bigger and I cannot directly apply your solution. (I need to implement Erlang - http://www.erlang.org/doc/reference_manual/expressions.html#id72588)
+3  A: 

I think this is a bug. If you look at the generated bytecode:


public int compare(java.lang.Object, java.lang.Object);
  Code:
   Stack=4, Locals=7, Args_size=3
   0:   aload_1
   1:   astore_3
   2:   aload_2
   3:   astore  4
   5:   aload_3
   6:   instanceof  #8; //class java/lang/Integer
   9:   ifeq    81
   12:  aload_3
   13:  invokestatic    #14; //Method scala/runtime/BoxesRunTime.unboxToInt:(Ljava/lang/Object;)I
   16:  istore  5
   18:  aload   4
   20:  instanceof  #8; //class java/lang/Integer
   23:  ifeq    45
   26:  getstatic   #20; //Field scala/Predef$.MODULE$:Lscala/Predef$;
   29:  iload   5
   31:  invokevirtual   #24; //Method scala/Predef$.intWrapper:(I)Lscala/runtime/RichInt;
   34:  aload   4
   36:  invokestatic    #14; //Method scala/runtime/BoxesRunTime.unboxToInt:(Ljava/lang/Object;)I
   39:  invokevirtual   #29; //Method scala/runtime/RichInt.compare:(I)I
   42:  goto    124
   45:  new #31; //class java/lang/Exception

   //part omitted for brevity..

   81:  aload_3
   82:  instanceof  #54; //class java/lang/Number
   85:  ifeq    161
   88:  aload_3
   89:  checkcast   #54; //class java/lang/Number
   92:  astore  6
   94:  aload   4
   96:  instanceof  #54; //class java/lang/Number
   99:  ifeq    125
   102: getstatic   #20; //Field scala/Predef$.MODULE$:Lscala/Predef$;
   105: aload   6
   107: invokevirtual   #58; //Method java/lang/Number.doubleValue:()D
   110: invokevirtual   #62; //Method scala/Predef$.doubleWrapper:(D)Lscala/runtime/RichDouble;
   113: aload   4
   115: checkcast   #54; //class java/lang/Number
   118: invokevirtual   #58; //Method java/lang/Number.doubleValue:()D

In line 6, the check is done whether the first variable is an instance of java.lang.Integer. If this does not succeed, we continue at line 81, which starts with the java.lang.Number check. If the first variable is an Integer, then we continue with the same check for the second variable. However, if this second check does not succeed, instead of again continuing at line 81 with the Number check, a jump is made to line 45, which throws the Exception. That does not seem to be correct. I quickly browsed through trac but couldn't directly find an issue regarding this, so it may be sensible to create one.

Edit As Extempore pointed out, it is a bug that is already in trac, see his comment below.

Arjan Blokzijl
This is a manifestation of #1697 / #2337. See http://lampsvn.epfl.ch/trac/scala/ticket/2337 .
extempore
Ah, good to know, thanks!
Arjan Blokzijl
Thank you. I have added this as yet another example for the ticket.