org.scalactic.Equivalence.scala Maven / Gradle / Ivy
/*
* Copyright 2001-2013 Artima, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.scalactic
/**
* Defines a custom way to determine equality for a type when compared with another value of the same type.
*
*
* Equivalence enables you to define alternate notions of equality for types that can be used
* with ScalaUtil's TypeCheckedTripleEquals and
* ConversionCheckedTripleEquals
* traits. These traits can be used to perform equality comparisons with type constraints enforced at
* compile time using ScalaUtil's === and !== syntax
* and ScalaTest's should === syntax of Matchers trait.
*
*
*
* Because Equality extends Equivalence, you automatically
* define an Equivalence[T] when you define an Equality[T]. Most often you will usually
* want to define custom Equalitys, because they will be more generally useful: they are also
* used by Scalactic's TripleEquals trait and ScalaTest's
* equal, be, and contain matcher syntax. However, if you really want
* just an Equivalence, and writing an Equality is inconvenient, you can write
* an Equivalence directly for a type.
*
*
*
* 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 TypeCheckedTripleEquals._
* import TypeCheckedTripleEquals._
*
* scala> Person("Joe", 29.0001) === Person("Joe", 29.0)
* res0: Boolean = false
*
*
*
* The === operator of TypeCheckedTripleEquals looks for an implicit
* Equivalence[SUPER], where SUPER is either the left-hand or right-hand type, whichever
* one is a supertype of the other. In this case, both sides are Person (which is considered a supertype of
* itself), so the compiler will look for an Equivalence[Person].
* Because you didn't specifically provide an implicit Equivalence[Person], === will fall back on
* default equality, because an Equality[Person] is-an
* Equivalence[Person]. The default Equality[Person] 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 Equivalence[Person] that compares
* the age Doubles with a tolerance, like this:
*
*
*
* scala> import Tolerance._
* import Tolerance._
*
* scala> implicit val personEq =
* | new Equivalence[Person] {
* | def areEquivalent(a: Person, b: Person): Boolean =
* | a.name == b.name && a.age === b.age +- 0.0002
* | }
* personEq: org.scalactic.Equivalence[Person] = $anon$1@7892bd8
*
*
*
* Now the === operator will use your more forgiving Equivalence[Person] for the
* equality check instead of default equality:
*
*
*
* scala> Person("Joe", 29.0001) === Person("Joe", 29.0)
* res1: Boolean = true
*
*
*/
trait Equivalence[T] {
/**
* Indicates whether the objects passed as a and b are equal.
*
*
* Note: this areEquivalent method means essentially the same thing as the areEqual method
* of trait Equality, the difference only being the static type of the
* right-hand value. This method is named areEquivalent instead
* of areEqual so that it can be implemented in terms of areEqual in trait
* Equality (which extends Equivalence).
*
*
* @param a a left-hand-side object being compared with another (right-hand-side one) for equality (e.g., a == b)
* @param b a right-hand-side object being compared with another (left-hand-side one) for equality (e.g., a == b)
* @return true if the passed objects are "equal," as defined by this Equivalence instance
*/
def areEquivalent(a: T, b: T): Boolean
}
/**
* Companion object for trait Equivalence that provides a factory method for producing
* default Equivalence instances.
*/
object Equivalence {
/**
* Provides default Equivalence implementations for the specified type whose
* areEqual method first calls .deep on any Array (on either the left or right side),
* then compares the resulting objects with ==.
*
* @return a default Equivalence[T]
*/
implicit def default[T]: Equivalence[T] = Equality.default[T]
}