This page is no longer maintained — Please continue to the home page at www.scala-lang.org

How to make == symmetrical?

4 replies
Bryne McCullough
Joined: 2010-05-07,
User offline. Last seen 42 years 45 weeks ago.

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!

extempore
Joined: 2008-12-17,
User offline. Last seen 35 weeks 3 days ago.
Re: How to make == symmetrical?

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);
}

H-star Development
Joined: 2010-04-14,
User offline. Last seen 2 years 26 weeks ago.
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);
> }
>
>

Bryne McCullough
Joined: 2010-05-07,
User offline. Last seen 42 years 45 weeks ago.
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:
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

Tony Morris 2
Joined: 2009-03-20,
User offline. Last seen 42 years 45 weeks ago.
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...

Copyright © 2012 École Polytechnique Fédérale de Lausanne (EPFL), Lausanne, Switzerland