1

I'm trying to make a method that will match on reference equality for any type, including primitives. How best to do this?

eq is only defined on AnyRef. If we try

def refEquals[A <% AnyRef, B <% AnyRef](a: A, b: B) = a eq b 

then on running refEquals(1,2) we find there are implicit methods in Predef including int2IntegerConflict to scupper such conversions.

I tried this:

def refEquals(a: Any, b: Any) = a match { case x: AnyRef => b match { case y: AnyRef => x eq y case _ => false } case x: Any => b match { case y: AnyRef => false case y: Any => x == y } } 

But this doesn't work (refEquals(1.0, 1.0) gives false) for reasons given by Rex Kerr here: Strange pattern matching behaviour with AnyRef

So how do we implement such a method?

edit: should have said "reference equality for reference types, or value equality for primitive types".

edit: here's the method using the idea from Rex's answer, for anyone who needs this and doesn't like typing:

def refEquals(a: Any, b: Any) = a match { case x: Boolean if b.isInstanceOf[Boolean] => x == b case x: Byte if b.isInstanceOf[Byte] => x == b case x: Short if b.isInstanceOf[Short] => x == b case x: Char if b.isInstanceOf[Char] => x == b case x: Int if b.isInstanceOf[Int] => x == b case x: Float if b.isInstanceOf[Float] => x == b case x: Double if b.isInstanceOf[Double] => x == b case x: Long if b.isInstanceOf[Long] => x == b case _ => a.asInstanceOf[AnyRef] eq b.asInstanceOf[AnyRef] } 

2 Answers 2

5

Reference equality is undefined for primitive types because they are not references. The only notion of equality in that case is value equality.

However, if you want your code to work both with primitives and reference types, you can either use '==' and make sure you pass objects that don't redefine 'equals', or define your own equality object and pass it around. You could probably use 'scala.math.Equiv[T]'.

def myRefEquals[A](x: A, y: A)(implicit eq: Equiv[A]) { eq.equiv(x, y) } implicit def anyRefHasRefEquality[A <: AnyRef] = new Equiv[A] { def equiv(x: A, y: A) = x eq y } implicit def anyValHasUserEquality[A <: AnyVal] = new Equiv[A] { def equiv(x: A, y: A) = x == y } println(myRefEquals(Some(1), Some(1))) 

This assumes you want the both objects to have the same type.

Sign up to request clarification or add additional context in comments.

2 Comments

Is there a way to check that two primitives are the same type? Currently this returns true for myRefEquals(1, 1.0).
@LuigiPlinge They are the same type by the time method sees them, since the compiler inserts the implicit conversion from Int to Double. The only way to avoid it (I think) is for the method to take (x: Any, y: Any).
4

You catch all the primitives first, then fall through to eq:

def refEquals(a: Any, b: Any) = a match { case x: Boolean => b match { case y: Boolean => x==y case _ => false } case x: Byte => ... case _ => a.asInstanceOf[AnyRef] eq b.asInstanceOf[AnyRef] } 

2 Comments

Why is this not the default for an Any#eq method?
@ManuelSchmidt - Probably just to avoid accidental computational overhead. eq checks are fast. Telling which of eight primitives you are is considerably slower. It would be nice to have something built in, though.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.