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 Equality
s, 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
Double
s 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]
}