Question
In Scala, is there a way to write a match clause which matches an object by it's class, but does not match any extending classes?
Motivation
It may seem pedantic.. it is.
Anyway, I thought of it when I saw the equals function generated by IntelliJ:
class Position(val x: Float, val y: Float) {
def canEqual(other: Any): Boolean = other.isInstanceOf[Position]
override def equals(other: Any): Boolean = other match {
case that: Position =>
(that canEqual this) &&
x == that.x &&
y == that.y
case _ => false
}
// generated hashCode omitted for brevity
}
Here we can see that other is class matched to be a Position, and then canEqual is used to put an upper bound on the match. That is, other could still be, say, a Position3D, which shouldn't equal a Position even if the x and y coordinates are the same.
This makes sense, but I wonder if some form of Scala wizardry would let us explicitly match Position classes and not subclasses.
There's a reason the auto-generated equals method doesn't do what you're suggesting. The canEqual convention is a common and well-documented one, because it allows for handling a broader set of common use cases.
You don't always want to exclude all sub classes when implementing an equals method, only ones that override equals. If, as you described, there was a Position3D class extending Position, and Position3D had an additional field z that was part of the Position3D equals method, then of course that Position3D should not be equal to any normal 2D position.
But what if for some reason a user wants to instantiate an anonymous subclass of Position?
val pos = new Position(1,2){}
or
trait Foo
val pos = new Position(1,2) with Foo
This position should probably be considered equal to new Position(1,2). Disallowing this equality could in some situations be tedious, if the mixed-in functionality is trivial or just for some particular local-scope use case. To require, as the other response suggested, other.getClass == classOf[Position], a client API mixing in a trait or for some reason creating an anonymous class might find they're creating positions that are not even equal to themselves.
Consider this code:
class Position(val x:Int, val y:Int) {
override def equals(other:Any) = other match {
case o:Position if o.getClass==classOf[Position] && o.x==x && o.y==y => true
case _ => false
}
}
trait Foo
val p1 = new Position(1,2) with Foo
val p2 = new Position(1,2) with Foo
p1 == p2
the result is false
see:
http://www.artima.com/lejava/articles/equality.html
Try:
other match {
case that: Position if that.getClass == classOf[Postion] => // or just getClass
(that canEqual this) &&
x == that.x &&
y == that.y
case: that: Postion => false //this case can be removed
case _ => false
}
Related
Consider the following code:
import scala.collection.mutable
case class A(b: Int) {
override def equals(obj: Any): Boolean = {
println("called")
obj match {
case o: A => b == o.b
case _ => false
}
}
}
val set = mutable.Set.empty[A]
val a1 = A(1)
set.add(a1)
println(set.contains(A(1)))
println(set.contains(A(2)))
Why the second call of set.contains doesn't print out "called"? Can someone explain how set identify two objects being equal?
As #LuisMiguelMejíaSuárez mentioned in the comments, A mutable Set is backed by a HashSet. It is well elaborated at What issues should be considered when overriding equals and hashCode in Java?
Whenever a.equals(b), then a.hashCode() must be same as b.hashCode().
This is because hashCode is a prior check, which when fails equals will definitely fail as well.
From What code is generated for an equals/hashCode method of a case class? we can conclude that for case classes hashCode and equals are overridden for case classes, using productPrefix which means the first set of elements in the case class. For example, if we have:
case class c(a: A)(b: B)
The hashCode and equals will be calculated only based on a. In your case, the hasCode method is calculated based on gender, which is different for Person(1) and Person(2). Therefore there is no need to check equals, which suppose to fail as well.
A tip from What issues should be considered when overriding equals and hashCode in Java?
In practice:
If you override one, then you should override the other.
Use the same set of fields that you use to compute equals() to compute hashCode().
From what I have understood, scala treats val definitions as values.
So, any instance of a case class with same parameters should be equal.
But,
case class A(a: Int) {
lazy val k = {
println("k")
1
}
val a1 = A(5)
println(a1.k)
Output:
k
res1: Int = 1
println(a1.k)
Output:
res2: Int = 1
val a2 = A(5)
println(a1.k)
Output:
k
res3: Int = 1
I was expecting that for println(a2.k), it should not print k.
Since this is not the required behavior, how should I implement this so that for all instances of a case class with same parameters, it should only execute a lazy val definition only once. Do I need some memoization technique or Scala can handle this on its own?
I am very new to Scala and functional programming so please excuse me if you find the question trivial.
Assuming you're not overriding equals or doing something ill-advised like making the constructor args vars, it is the case that two case class instantiations with same constructor arguments will be equal. However, this does not mean that two case class instantiations with same constructor arguments will point to the same object in memory:
case class A(a: Int)
A(5) == A(5) // true, same as `A(5).equals(A(5))`
A(5) eq A(5) // false
If you want the constructor to always return the same object in memory, then you'll need to handle this yourself. Maybe use some sort of factory:
case class A private (a: Int) {
lazy val k = {
println("k")
1
}
}
object A {
private[this] val cache = collection.mutable.Map[Int, A]()
def build(a: Int) = {
cache.getOrElseUpdate(a, A(a))
}
}
val x = A.build(5)
x.k // prints k
val y = A.build(5)
y.k // doesn't print anything
x == y // true
x eq y // true
If, instead, you don't care about the constructor returning the same object, but you just care about the re-evaluation of k, you can just cache that part:
case class A(a: Int) {
lazy val k = A.kCache.getOrElseUpdate(a, {
println("k")
1
})
}
object A {
private[A] val kCache = collection.mutable.Map[Int, Int]()
}
A(5).k // prints k
A(5).k // doesn't print anything
The trivial answer is "this is what the language does according to the spec". That's the correct, but not very satisfying answer. It's more interesting why it does this.
It might be clearer that it has to do this with a different example:
case class A[B](b: B) {
lazy val k = {
println(b)
1
}
}
When you're constructing two A's, you can't know whether they are equal, because you haven't defined what it means for them to be equal (or what it means for B's to be equal). And you can't statically intitialize k either, as it depends on the passed in B.
If this has to print twice, it would be entirely intuitive if that would only be the case if k depends on b, but not if it doesn't depend on b.
When you ask
how should I implement this so that for all instances of a case class with same parameters, it should only execute a lazy val definition only once
that's a trickier question than it sounds. You make "the same parameters" sound like something that can be known at compile time without further information. It's not, you can only know it at runtime.
And if you only know that at runtime, that means you have to keep all past uses of the instance A[B] alive. This is a built in memory leak - no wonder Scala has no built-in way to do this.
If you really want this - and think long and hard about the memory leak - construct a Map[B, A[B]], and try to get a cached instance from that map, and if it doesn't exist, construct one and put it in the map.
I believe case classes only consider the arguments to their constructor (not any auxiliary constructor) to be part of their equality concept. Consider when you use a case class in a match statement, unapply only gives you access (by default) to the constructor parameters.
Consider anything in the body of case classes as "extra" or "side effect" stuffs. I consider it a good tactic to make case classes as near-empty as possible and put any custom logic in a companion object. Eg:
case class Foo(a:Int)
object Foo {
def apply(s: String) = Foo(s.toInt)
}
In addition to dhg answer, I should say, I'm not aware of functional language that does full constructor memoizing by default. You should understand that such memoizing means that all constructed instances should stick in memory, which is not always desirable.
Manual caching is not that hard, consider this simple code
import scala.collection.mutable
class Doubler private(a: Int) {
lazy val double = {
println("calculated")
a * 2
}
}
object Doubler{
val cache = mutable.WeakHashMap.empty[Int, Doubler]
def apply(a: Int): Doubler = cache.getOrElseUpdate(a, new Doubler(a))
}
Doubler(1).double //calculated
Doubler(5).double //calculated
Doubler(1).double //most probably not calculated
I want to assert equality in a ScalaTest of case classes which contain an Array. (So the built-in equality matchers for case classes are not applicable.)
Example:
case class Example(array: Array[Double], variable: Integer)
Test stub:
val a = Example(Array(0.1, 0.2), 1)
val b = Example(Array(0.1, 0.2), 1)
a should equal (b)
Fails as expected. So I implement an Equality trait:
implicit val exampleEq =
new Equality[Example] {
def areEqual(left: Example, right: Any): Boolean =
right match {
case other: Example => {
left.array should contain theSameElementsInOrderAs other.array
left.variable should be other.variable
true
}
case _ => false
}
}
Which works. The other option is to implement the Equality trait with == at all places of the "should be" and in case it is false at one place return false, else true. The problem with both is that when running the test I get the error message that both "Example" objects are not equal (if they are not) but I would like to see in which element they differ.
How do I achieve this?
Thank you for your help!
[UPDATE] In practice Example contains multiple arrays and other fields, I changed the code accordingly.
Considered using:
left.array should contain theSameElementsInOrderAs other.array
Reference: Working with "sequences".
Is it ok, to create case classes with floating point fields, like:
case class SomeClass(a:Double, b:Double)
I guess auto generated equal method won't work in this case.
Is overriding equals the best solution?
EDIT:
if overriding equals is the way to go, I would like to avoid hardcoding epsilon ( where epsilon is defined like => |this.a-a|< epsilon). This won't compile:
case class SomeClass(a:Double, b:Double, implicit epsilon:Double)
I am looking for a way to pass epsilon without passing concert value each time
(some "implicit" magic).
I have also follow up more general question, how would you define hashcode for class with only floating point fields?
You are correct. If you are worried about precision, then you will need to override equals:
case class SomeClass(a:Double, b:Double)
SomeClass(2.2 * 3, 1.0) == SomeClass(6.6, 1.0)
// res0: Boolean = false
case class BetterClass(a: Double, b: Double) {
override def equals(obj: Any) = obj match {
case x: BetterClass =>
(this.a - x.a).abs < 0.0001 && (this.b - x.b).abs < 0.0001
case _ => false
}
}
BetterClass(2.2 * 3, 1.0) == BetterClass(6.6, 1.0)
// res1: Boolean = true
Ah, the joy of floating point numbers.
I think it is not a good idea to override equals with a fuzzy comparison. It violates all sorts of things that you usually take for granted with equality. Imagine a, b and c are some case classes with a fuzzy equals. Then it is possible to have a, b, c such that a==b, b==c but a!=c.
Then there is the behavior of hashcode to consider. If you override equals with fuzzy equality and do not override hashcode, you will not be able to use the resulting object in a hashmap or set, because a==b but a.hashcode!=b.hashcode.
The best way to solve the problem is to define an operator like =~= that provides a fuzzy comparison in addition to equals/== which (at least for immutable objects in scala) means that objects are exactly identical so that you can replace one with the other without changing the result of a calculation.
If you also want the ability to configure the precision of the comparison via an implicit, that adds another level of complexity. Here is a more complete example:
// a class to configure the comparison that will be passed to the operator
// as an implicit value
case class CompareSettings(epsilon:Double = 0.1) extends AnyVal
// add an operator =~= to double to do a fuzzy comparions
implicit class DoubleCompareExtensions(val value:Double) extends AnyVal {
def =~=(that:Double)(implicit settings:CompareSettings) : Boolean = {
// this is not a good way to do a fuzzy comparison. You should have both relative
// and absolute precision. But for an example like this it should suffice.
(value - that).abs < settings.epsilon
}
}
case class SomeClass(x:Double, y:Double) {
// we need an implicit argument of type CompareSettings
def =~=(that:SomeClass)(implicit settings:CompareSettings) =
// the implicit argument will be automatically passed on to the operators
this.x =~= that.x && this.y =~= that.y
}
// usage example
val x=1.0
val y=1.01
// this won't work since there is no implicit in scope
x =~= y
// define an implicit of the right type
implicit val compareSettings = CompareSettings(0.2)
// now this will work
x =~= y
// and this as well
SomeClass(1,2) =~= SomeClass(1.1,2)
Note that the implicit is not an argument of the class but of the operation.
I have an immutable Set of a class, Set[MyClass], and I want to use the Set methods intersect and diff, but I want them to test for equality using my custom equals method, rather than default object equality test
I have tried overriding the == operator, but it isn't being used.
Thanks in advance.
Edit:
The intersect method is a concrete value member of GenSetLike
spec: http://www.scala-lang.org/api/current/scala/collection/GenSetLike.html
src: https://lampsvn.epfl.ch/trac/scala/browser/scala/tags/R_2_9_1_final/src//library/scala/collection/GenSetLike.scala#L1
def intersect(that: GenSet[A]): Repr = this filter that
so the intersection is done using the filter method.
Yet another Edit:
filter is defined in TraversableLike
spec: http://www.scala-lang.org/api/current/scala/collection/TraversableLike.html
src: https://lampsvn.epfl.ch/trac/scala/browser/scala/tags/R_2_9_1_final/src//library/scala/collection/TraversableLike.scala#L1
def filter(p: A => Boolean): Repr = {
val b = newBuilder
for (x <- this)
if (p(x)) b += x
b.result
}
What's unclear to me is what it uses when invoked without a predicate, p. That's not an implicit parameter.
equals and hashCode are provided automatically in case class only if you do not define them.
case class MyClass(val name: String) {
override def equals(o: Any) = o match {
case that: MyClass => that.name.equalsIgnoreCase(this.name)
case _ => false
}
override def hashCode = name.toUpperCase.hashCode
}
Set(MyClass("xx"), MyClass("XY"), MyClass("xX"))
res1: scala.collection.immutable.Set[MyClass] = Set(MyClass(xx), MyClass(XY))
If what you want is reference equality, still write equals and hashCode, to prevent automatic generation, and call the version from AnyRef
override def equals(o: Any) = super.equals(o)
override def hashCode = super.hashCode
With that:
Set(MyClass("x"), MyClass("x"))
res2: scala.collection.immutable.Set[MyClass] = Set(MyClass(x), MyClass(x))
You cannot override the ==(o: Any) from AnyRef, which is sealed and always calls equals. If you tried defining a new (overloaded) ==(m: MyClass), it is not the one that Set calls, so it is useless here and quite dangerous in general.
As for the call to filter, the reason it works is that Set[A] is a Function[A, Boolean]. And yes, equals is used, you will see that function implementation (apply) is a synonymous for contains, and most implementations of Set use == in contains (SortedSet uses the Ordering instead). And == calls equals.
Note: the implementation of my first equals is quick and dirty and probably bad if MyClass is to be subclassed . If so, you should at the very least check type equality (this.getClass == that.getClass) or better define a canEqual method (you may read this blog by Daniel Sobral)
You'll need to override .hashCode as well. This is almost always the case when you override .equals, as .hashCode is often used as a cheaper pre-check for .equals; any two objects which are equal must have identical hash codes. I'm guessing you're using objects whose default hashCode does not respect this property with respect to your custom equality, and the Set implementation is making assumptions based on the hash codes (and so never even calling your equality operation).
See the Scala docs for Any.equals and Any.hashCode: http://www.scala-lang.org/api/rc/scala/Any.html
This answer shows a custom mutable Set with user-defined Equality. It could be made immutable by replacing the internal store with a Vector and returning a modified copy of itself upon each operation
"It is not possible to override == directly, as it is defined as a final method in class Any. That is, Scala treats == as if were defined as follows in class Any:
final def == (that: Any): Boolean =
if (null eq this) {null eq that} else {this equals that}
" from Programming In Scala, Second Edition