org.scalactic

Equality

trait Equality[A] extends Equivalence[A]

Defines a custom way to determine equality for a type when compared with another value of type Any.

Equality enables you to define alternate notions of equality for types that can be used with ScalaUtil's === and !== syntax and ScalaTest's matcher syntax.

For example, say you have a case class that includes a Double value:

scala> case class Person(name: String, age: Double)
defined class Person

Imagine you are calculating the age values in such as way that occasionally tests are failing because of rounding differences that you actually don't care about. For example, you expect an age of 29.0, but you're sometimes seeing 29.0001:

scala> import org.scalactic._
import org.scalactic._

scala> import TripleEquals._
import TripleEquals._

scala> Person("Joe", 29.0001) === Person("Joe", 29.0)
res0: Boolean = false

The === operator looks for an implicit Equality[L], where L is the left-hand type: in this case, Person. Because you didn't specifically provide an implicit Equality[Person], === will fall back on default equality, which will call Person's equals method. That equals method, provided by the Scala compiler because Person is a case class, will declare these two objects unequal because 29.001 does not exactly equal 29.0.

To make the equality check more forgiving, you could define an implicit Equality[Person] that compares the age Doubles with a tolerance, like this:

scala> import Tolerance._
import Tolerance._

scala> implicit val personEq =
     |   new Equality[Person] {
     |     def areEqual(a: Person, b: Any): Boolean =
     |       b match {
     |         case p: Person => a.name == p.name && a.age === p.age +- 0.0002
     |         case _ => false
     |       }
     |   }
personEq: org.scalactic.Equality[Person] = $anon$1@2b29f6e7

Now the === operator will use your more forgiving Equality[Person] for the equality check instead of default equality:

scala> Person("Joe", 29.0001) === Person("Joe", 29.0)
res1: Boolean = true

Default equality

Scalactic defines a default Equality[T] for all types T whose areEqual method works by first calling .deep on any passed array, then calling == on the left-hand object, passing in the right-hand object. You can obtain a default equality via the default method of the Equality companion object, or from the defaultEquality method defined in TripleEqualsSupport.

About equality and equivalence

The Equality trait represents the Java Platform's native notion of equality, as expressed in the signature and contract of the equals method of java.lang.Object. Essentially, trait Equality enables you to write alternate equals method implementations for a type outside its defining class.

In an equals method, the left-hand type is known to be the type of this, but the right-hand type is Any. As a result, you would normally perform a runtime type test to determine whether the right-hand object is of an appropriate type for equality, and if so, compare it structurally for equality with the left-hand (this) object. An an illustration, here's a possible equals implementation for the Person case class shown in the earlier example:

override def equals(other: Any): Boolean =
  other match {
    case p: Person => name = p.name && age = p.age
    case _ => false
  }

The areEquals method of Equality[T] is similar. The left-hand type is known to be T, but the right-hand type is Any, so normally you'd need to do a runtime type test in your areEqual implementation. Here's the areEqual method implementation from the earlier Equality[Person] example:

def areEqual(a: Person, b: Any): Boolean =
  b match {
    case p: Person => a.name == p.name && a.age === p.age +- 0.0002
    case _ => false
  }

Equality is used by TripleEquals, which enforces no type constraint between the left and right values, and the equal, be, and contain syntax of ScalaTest Matchers.

By contrast, TypeCheckedTripleEquals and ConversionCheckedTripleEquals use an Equivalence. Equivalence differs from Equality in that both the left and right values are of the same type. Equivalence works for TypeCheckedTripleEquals because the type constraint enforces that the left type is a subtype or supertype of (or the same type as) the right type, and it widens the subtype to the supertype. So ultimately, both left and right sides are of the supertype type. Similarly, Equivalence works for ConversionCheckedTripleEquals because the type constraint enforces that an implicit conversion exists from either the left type to the right type, or the right type to the left type, and it always converts one type to the other using the implicit conversion. (If both types are the same type, the identity implicit conversion from Predef is used.) Because of the conversion, both left and right sides are ultimately of the converted-to type. Here's an example of how writing an Equivalence's areEquivalent method might look:

def areEquivalent(a: Person, b: Person): Boolean =
     a.name == b.name && a.age === b.age +- 0.0002

Scalactic provides both Equality and Equivalence because the Any in Equality can sometimes make things painful. For example, in trait TolerantNumerics, a single generic factory method can produce Equivalences for any Numeric type, but because of the Any, a separate factory method must be defined to produce an Equality for each Numeric type.

If you just want to customize the notion of equality for === used in Boolean expressions, you can work with Equivalences instead of Equalitys. If you do chose to write the more general Equalitys, they can be used wherever an Equivalence is required, because Equality extends Equivalence, defining a final implementation of areEquivalent that invokes areEqual.

Note: The Equality type class was inspired in part by the Equal type class of the scalaz project.

A

the type whose equality is being customized

Source
Equality.scala
Linear Supertypes
Equivalence[A], AnyRef, Any
Known Subclasses
Ordering
  1. Alphabetic
  2. By inheritance
Inherited
  1. Equality
  2. Equivalence
  3. AnyRef
  4. Any
  1. Hide All
  2. Show all
Learn more about member selection
Visibility
  1. Public
  2. All

Abstract Value Members

  1. abstract def areEqual(a: A, b: Any): Boolean

    Indicates whether the objects passed as a and b are equal.

    Indicates whether the objects passed as a and b are equal.

    a

    a left-hand value being compared with another (right-hand-side one) for equality (e.g., a == b)

    b

    a right-hand value being compared with another (left-hand-side one) for equality (e.g., a == b)

    returns

    true if the passed objects are "equal," as defined by this Equality instance

Concrete Value Members

  1. final def !=(arg0: Any): Boolean

    Definition Classes
    AnyRef → Any
  2. final def ##(): Int

    Definition Classes
    AnyRef → Any
  3. final def ==(arg0: Any): Boolean

    Definition Classes
    AnyRef → Any
  4. final def areEquivalent(a: A, b: A): Boolean

    A final implementation of the areEquivalent method of Equivalence that just passes a and b to areEqual and returns the result.

    A final implementation of the areEquivalent method of Equivalence that just passes a and b to areEqual and returns the result.

    This method enables any Equality to be used where an Equivalence is needed, such as the implicit enabling methods of TypeCheckedTripleEquals and ConversionCheckedTripleEquals.

    a

    a left-hand value being compared with another, right-hand, value for equality (e.g., a == b)

    b

    a right-hand value being compared with another, left-hand, value for equality (e.g., a == b)

    returns

    true if the passed objects are "equal," as defined by the areEqual method of this Equality instance

    Definition Classes
    EqualityEquivalence
  5. final def asInstanceOf[T0]: T0

    Definition Classes
    Any
  6. def clone(): AnyRef

    Attributes
    protected[java.lang]
    Definition Classes
    AnyRef
    Annotations
    @throws( ... )
  7. final def eq(arg0: AnyRef): Boolean

    Definition Classes
    AnyRef
  8. def equals(arg0: Any): Boolean

    Definition Classes
    AnyRef → Any
  9. def finalize(): Unit

    Attributes
    protected[java.lang]
    Definition Classes
    AnyRef
    Annotations
    @throws( classOf[java.lang.Throwable] )
  10. final def getClass(): Class[_]

    Definition Classes
    AnyRef → Any
  11. def hashCode(): Int

    Definition Classes
    AnyRef → Any
  12. final def isInstanceOf[T0]: Boolean

    Definition Classes
    Any
  13. final def ne(arg0: AnyRef): Boolean

    Definition Classes
    AnyRef
  14. final def notify(): Unit

    Definition Classes
    AnyRef
  15. final def notifyAll(): Unit

    Definition Classes
    AnyRef
  16. final def synchronized[T0](arg0: ⇒ T0): T0

    Definition Classes
    AnyRef
  17. def toString(): String

    Definition Classes
    AnyRef → Any
  18. final def wait(): Unit

    Definition Classes
    AnyRef
    Annotations
    @throws( ... )
  19. final def wait(arg0: Long, arg1: Int): Unit

    Definition Classes
    AnyRef
    Annotations
    @throws( ... )
  20. final def wait(arg0: Long): Unit

    Definition Classes
    AnyRef
    Annotations
    @throws( ... )

Inherited from Equivalence[A]

Inherited from AnyRef

Inherited from Any

Ungrouped