- About Scala
- Documentation
- Code Examples
- Software
- Scala Developers
How to make == symmetrical?
Mon, 2010-05-17, 22:09
Hi there,
I am trying to create a class which may be compared for equality with a built-in class, but can't figure out how to get == to behave symmetrically.
As a simple example, consider MyString class, which I wan't to be able to compare with built-in Strings symmetrically (that is, myStr == str always evaluates the same as str == myStr):
class MyString(val strVal: String) {
override def equals(that: Any) = that match {
case that: MyString => strVal.equals(that.strVal)
case that: String => strVal.equals(that)
case _ => false
}
}
val myStr = new MyString("foobar")
val str = "foobar"
myStr == str // true
str == myStr // false (should be true)
At first I considered implicits, but don't see how that would help here. Somehow this works for the BigInt class:
scala> BigInt(123) == 123
res22: Boolean = true
scala> 123 == BigInt(123)
res23: Boolean = true
Any pointers on how I can make this work?
Thanks!
Mon, 2010-05-17, 22:57
#2
Re: How to make == symmetrical?
you can't do it with ==, but you can do it by using and implicit def
which converts your object to something offering a method which does
that you want.
i'd call it ===. as long as you don't want to override == in every case,
that will do it.
Paul Phillips schrieb:
> On Mon, May 17, 2010 at 02:08:57PM -0700, Bryne McCullough wrote:
>
>> I am trying to create a class which may be compared for equality with
>> a built-in class, but can't figure out how to get == to behave
>> symmetrically.
>>
>
> It won't and you can't.
>
>
>> At first I considered implicits, but don't see how that would help here.
>>
>
> Right, implicits can't help you if the expression typechecks and ==
> always typechecks.
>
>
>> Somehow this works for the BigInt class:
>>
>> scala> 123 == BigInt(123)
>> res23: Boolean = true
>>
>
> We cheated.
>
> /** A marker class for Number types introduced by Scala
> * @author Martin Odersky, Paul Phillips
> * @version 2.8
> * @since 2.8
> */
> public abstract class ScalaNumber extends java.lang.Number {
> protected abstract boolean isWhole();
> public abstract Object underlying();
> }
>
> // boxesruntime
>
> public static boolean equalsNumNum(java.lang.Number xn, java.lang.Number yn) {
> int xcode = eqTypeCode(xn);
> int ycode = eqTypeCode(yn);
> switch (ycode > xcode ? ycode : xcode) {
> case INT:
> return xn.intValue() == yn.intValue();
> case LONG:
> return xn.longValue() == yn.longValue();
> case FLOAT:
> return xn.floatValue() == yn.floatValue();
> case DOUBLE:
> return xn.doubleValue() == yn.doubleValue();
> default:
> // ---> here comes the cheating part
> if ((yn instanceof ScalaNumber) && !(xn instanceof ScalaNumber))
> return yn.equals(xn);
> }
> return xn.equals(yn);
> }
>
>
Mon, 2010-05-17, 23:47
#3
Re: How to make == symmetrical?
Thanks Paul, that was very helpful!
Too bad symmetrical == isn't generally possible in this case.
But I guess it would work if one's class happened to be something that made sense as a subclass of ScalaNumber. For example, a ratio class:
class MyRatio(val numer: Int, val denom: Int) extends scala.math.ScalaNumber {
override def equals(that: Any) = that match {
case that: MyRatio => numer.equals(that.numer) && denom.equals(that.denom)
case that: Int => numer.equals(that) && denom == 1
case _ => false
}
// abstracts inherited from java.lang.Number
def doubleValue = numer.doubleValue / denom.doubleValue
def floatValue = doubleValue.floatValue
def longValue = doubleValue.longValue
def intValue = doubleValue.intValue
// abstracts inherited from scala.math.ScalaNumber
def isWhole = false
def underlying = this
}
val num1 = new MyRatio(123, 1)
val num2 = 123
num1 == num2 // true
num2 == num1 // true
On Mon, May 17, 2010 at 2:22 PM, Paul Phillips <paulp [at] improving [dot] org> wrote:
Too bad symmetrical == isn't generally possible in this case.
But I guess it would work if one's class happened to be something that made sense as a subclass of ScalaNumber. For example, a ratio class:
class MyRatio(val numer: Int, val denom: Int) extends scala.math.ScalaNumber {
override def equals(that: Any) = that match {
case that: MyRatio => numer.equals(that.numer) && denom.equals(that.denom)
case that: Int => numer.equals(that) && denom == 1
case _ => false
}
// abstracts inherited from java.lang.Number
def doubleValue = numer.doubleValue / denom.doubleValue
def floatValue = doubleValue.floatValue
def longValue = doubleValue.longValue
def intValue = doubleValue.intValue
// abstracts inherited from scala.math.ScalaNumber
def isWhole = false
def underlying = this
}
val num1 = new MyRatio(123, 1)
val num2 = 123
num1 == num2 // true
num2 == num1 // true
On Mon, May 17, 2010 at 2:22 PM, Paul Phillips <paulp [at] improving [dot] org> wrote:
On Mon, May 17, 2010 at 02:08:57PM -0700, Bryne McCullough wrote:
> I am trying to create a class which may be compared for equality with
> a built-in class, but can't figure out how to get == to behave
> symmetrically.
It won't and you can't.
> At first I considered implicits, but don't see how that would help here.
Right, implicits can't help you if the expression typechecks and ==
always typechecks.
> Somehow this works for the BigInt class:
>
> scala> 123 == BigInt(123)
> res23: Boolean = true
We cheated.
/** A marker class for Number types introduced by Scala
* @author Martin Odersky, Paul Phillips
* @version 2.8
* @since 2.8
*/
public abstract class ScalaNumber extends java.lang.Number {
protected abstract boolean isWhole();
public abstract Object underlying();
}
// boxesruntime
public static boolean equalsNumNum(java.lang.Number xn, java.lang.Number yn) {
int xcode = eqTypeCode(xn);
int ycode = eqTypeCode(yn);
switch (ycode > xcode ? ycode : xcode) {
case INT:
return xn.intValue() == yn.intValue();
case LONG:
return xn.longValue() == yn.longValue();
case FLOAT:
return xn.floatValue() == yn.floatValue();
case DOUBLE:
return xn.doubleValue() == yn.doubleValue();
default:
// ---> here comes the cheating part
if ((yn instanceof ScalaNumber) && !(xn instanceof ScalaNumber))
return yn.equals(xn);
}
return xn.equals(yn);
}
--
Paul Phillips | Before a man speaks it is always safe to assume
Imperfectionist | that he is a fool. After he speaks, it is seldom
Empiricist | necessary to assume it.
ha! spill, pupil | -- H. L. Mencken
Tue, 2010-05-18, 00:17
#4
Re: How to make == symmetrical?
HamsterofDeath wrote:
> you can't do it with ==, but you can do it by using and implicit def
> which converts your object to something offering a method which does
> that you want.
> i'd call it ===. as long as you don't want to override == in every case,
> that will do it.
>
>
http://code.google.com/p/scalaz/source/browse/continuous/2010-02-15%2B64...
http://code.google.com/p/scalaz/source/browse/continuous/2010-02-15%2B64...
On Mon, May 17, 2010 at 02:08:57PM -0700, Bryne McCullough wrote:
> I am trying to create a class which may be compared for equality with
> a built-in class, but can't figure out how to get == to behave
> symmetrically.
It won't and you can't.
> At first I considered implicits, but don't see how that would help here.
Right, implicits can't help you if the expression typechecks and ==
always typechecks.
> Somehow this works for the BigInt class:
>
> scala> 123 == BigInt(123)
> res23: Boolean = true
We cheated.
/** A marker class for Number types introduced by Scala
* @author Martin Odersky, Paul Phillips
* @version 2.8
* @since 2.8
*/
public abstract class ScalaNumber extends java.lang.Number {
protected abstract boolean isWhole();
public abstract Object underlying();
}
// boxesruntime
public static boolean equalsNumNum(java.lang.Number xn, java.lang.Number yn) {
int xcode = eqTypeCode(xn);
int ycode = eqTypeCode(yn);
switch (ycode > xcode ? ycode : xcode) {
case INT:
return xn.intValue() == yn.intValue();
case LONG:
return xn.longValue() == yn.longValue();
case FLOAT:
return xn.floatValue() == yn.floatValue();
case DOUBLE:
return xn.doubleValue() == yn.doubleValue();
default:
// ---> here comes the cheating part
if ((yn instanceof ScalaNumber) && !(xn instanceof ScalaNumber))
return yn.equals(xn);
}
return xn.equals(yn);
}