org.scalautils.ConversionCheckedLegacyTripleEquals.scala Maven / Gradle / Ivy
Show all versions of scalatest_2.11.0-M5 Show documentation
/* * 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.scalautils import TripleEqualsSupport._ /** * Provides
TypeCheckedLegacyTripleEquals, it will also compile * type types that would be rejected by===
and!==
operators that returnBoolean
, delegate the equality determination * to anEquality
type class, and require that either the types of the two values compared are in a subtype/supertype * relationship, or that an implicit conversion is available that can convert from one type to the other. * ** *
* Recommended Usage: * Trait ConversionCheckedLegacyTripleEquals
is useful (in test, not production, code) when you need determine equality for a type of object differently than * itsequals
* method—either you can't change theequals
method, or theequals
method is sensible generally, but you're in a special situation where you * need something else—and/or you want a compile-time type check that allows types that are implicitly convertable in either (or both) directions. ** * Note: The purpose of this trait is to maintain compatibility with existing ScalaTest code that uses the original
* *===
operator. After * ScalaTest no longer supports Scala 2.9, the “legacy” triple equals traits will be deprecated and eventually removed. Good error messages will * be obtained for both==
and===
through assert macros. In the transition phase, you can in production code use regular triple equals traits, * whose===
operators returnBoolean
, and in test code use "legacy" triple equals traits, whose===
* operators returnOption[String]
. * ** This trait is the middle ground of the three legacy triple equals traits, in between *
LegacyTripleEquals
, the most lenient, andTypeCheckedLegacyTripleEquals
, the most strict. * IfLegacyTripleEquals
is mixed in or imported, the===
can be used with any two types * and still compile. IfTypeCheckedLegacyTripleEquals
is mixed in or imported, however, only types in * a subtype or supertype relationship with each other (including when both types are exactly the same) will compile. *ConversionCheckedLegacyTripleEquals
is slightly more accomodating, because in addition to compiling any * use of===
that will compile underTypeCheckedLegacyTripleEquals
, so long as an implicit * conversion (in either direction) from one type to another is available. * * ** For example, under
* *TypeCheckedLegacyTripleEquals
, the following use of===
will not compile, * becauseInt
andLong
are not in a subtype/supertype relationship. (I.e.,Int
* is not a subtype or supertype ofLong
): ** scala> import org.scalautils._ * import org.scalautils._ * * scala> import TypeCheckedLegacyTripleEquals._ * import TypeCheckedLegacyTripleEquals._ * * scala> 1 === 1L * <console>:14: error: types Int and Long do not adhere to the equality constraint selected for * the === and !== operators; they must either be in a subtype/supertype relationship, or, if * ConversionCheckedLegacyTripleEquals is in force, implicitly convertible in one direction or the other; * the missing implicit parameter is of type org.scalautils.Constraint[Int,Long] * 1 === 1L * ^ ** ** Trait
* *TypeCheckedLegacyTripleEquals
rejects typesInt
andLong
because they are not directly related via * subtyping. However, an implicit widening conversion fromInt
to Long does exist (imported implicitly from *scala.Predef
), soConversionCheckedLegacyTripleEquals
* will allow it: ** scala> import ConversionCheckedLegacyTripleEquals._ * import ConversionCheckedLegacyTripleEquals._ * * scala> 1 === 1L * res1: Option[String = None ** ** The implicit conversion can go in either direction: from the left type to the right type, or vice versa. In the above expression the * implicit conversion goes from left to right (the
* *Int
on the left to theLong
on the right). It also works * the other way: ** scala> 1L === 1 * res2: Option[String] = None ** ** This trait will override or hide implicit methods defined by its sibling traits, *
* *LegacyTripleEquals
orTypeCheckedLegacyTripleEquals
, * and can therefore be used to temporarily turn on or off conversion checking in a limited scope. * Because the methods inConversionCheckedLegacyTripleEquals
(and its siblings) * override all the methods defined in supertypeTripleEqualsSupport
, you can achieve the same * kind of nested tuning of equality constraints whether you mix in traits, import from companion objects, or use some combination of both. ** In short, you should be able to select a primary constraint level via either a mixin or import, then change that in nested scopes * however you want, again either through a mixin or import, without getting any implicit conversion ambiguity. The innermost constraint level in scope * will always be in force. *
* *
* An alternative way to solve an unwanted compiler error caused by an over-zealous type constraint is with a widening type ascription. Here * are some examples: *
* * ** scala> import org.scalautils._ * import org.scalautils._ * * scala> import ConversionCheckedLegacyTripleEquals._ * import ConversionCheckedLegacyTripleEquals._ * * scala> List(1, 2, 3) === Vector(1, 2, 3) * <console>:14: error: types List[Int] and scala.collection.immutable.Vector[Int] do not adhere to the equality constraint selected for the === and !== operators; the missing implicit parameter is of type org.scalautils.Constraint[List[Int],scala.collection.immutable.Vector[Int]] * List(1, 2, 3) === Vector(1, 2, 3) * ^ ** ** Although you could solve the above type error with
* *TraversableEqualityConstraints
, you could also * simply widen the type of one side or the other toAny
. BecauseAny
is a supertype of everything, the * type constraint will be satisfied: ** scala> List(1, 2, 3) === (Vector(1, 2, 3): Any) * res1: Option[String] = None * * scala> (List(1, 2, 3): Any) === Vector(1, 2, 3) * res2: Option[String] = None ** ** You could alternatively widen a type to a more specific common supertype than
* *Any
. For example, sinceList[Int]
and *Vector[Int]
are both subtypes ofSeq[Int]
, so you could widen either type toSeq[Int]
to satisfy * the type checker: ** scala> List(1, 2, 3) === (Vector(1, 2, 3): Seq[Int]) * res3: Option[String] = None * * scala> (List(1, 2, 3): Seq[Int]) === Vector(1, 2, 3) * res4: Option[String] = None ** * @author Bill Venners */ trait ConversionCheckedLegacyTripleEquals extends LowPriorityConversionCheckedConstraint { override def convertToEqualizer[T](left: T): Equalizer[T] = new Equalizer(left) override def convertToLegacyEqualizer[T](left: T): LegacyEqualizer[T] = new LegacyEqualizer(left) override def convertToCheckingEqualizer[T](left: T): CheckingEqualizer[T] = new CheckingEqualizer(left) implicit override def convertToLegacyCheckingEqualizer[T](left: T): LegacyCheckingEqualizer[T] = new LegacyCheckingEqualizer(left) override def unconstrainedEquality[A, B](implicit equalityOfA: Equality[A]): Constraint[A, B] = new EqualityConstraint[A, B](equalityOfA) override def lowPriorityTypeCheckedConstraint[A, B](implicit equivalenceOfB: Equivalence[B], ev: A <:< B): Constraint[A, B] = new AToBEquivalenceConstraint[A, B](equivalenceOfB, ev) override def convertEquivalenceToAToBConstraint[A, B](equivalenceOfB: Equivalence[B])(implicit ev: A <:< B): Constraint[A, B] = new AToBEquivalenceConstraint[A, B](equivalenceOfB, ev) override def typeCheckedConstraint[A, B](implicit equivalenceOfA: Equivalence[A], ev: B <:< A): Constraint[A, B] = new BToAEquivalenceConstraint[A, B](equivalenceOfA, ev) override def convertEquivalenceToBToAConstraint[A, B](equivalenceOfA: Equivalence[A])(implicit ev: B <:< A): Constraint[A, B] = new BToAEquivalenceConstraint[A, B](equivalenceOfA, ev) implicit override def conversionCheckedConstraint[A, B](implicit equivalenceOfA: Equivalence[A], cnv: B => A): Constraint[A, B] = new BToAEquivalenceConstraint[A, B](equivalenceOfA, cnv) implicit override def convertEquivalenceToBToAConversionConstraint[A, B](equivalenceOfA: Equivalence[A])(implicit ev: B => A): Constraint[A, B] = new BToAEquivalenceConstraint[A, B](equivalenceOfA, ev) } /** * Companion object to traitConversionCheckedLegacyTripleEquals
that facilitates the importing ofConversionCheckedLegacyTripleEquals
members as * an alternative to mixing it in. One use case is to importConversionCheckedLegacyTripleEquals
members so you can use * them in the Scala interpreter: * ** $ scala -classpath scalautils.jar * Welcome to Scala version 2.10.0 * Type in expressions to have them evaluated. * Type :help for more information. * * scala> import org.scalautils._ * import org.scalautils._ * * scala> import ConversionCheckedLegacyTripleEquals._ * import ConversionCheckedLegacyTripleEquals._ * * scala> 1 === 1L * res0: Option[String] = None **/ object ConversionCheckedLegacyTripleEquals extends ConversionCheckedLegacyTripleEquals