All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.scalautils.ConversionCheckedLegacyTripleEquals.scala Maven / Gradle / Ivy

The newest version!
/*
 * 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 === and !== operators that return Boolean, delegate the equality determination
 * to an Equality 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 * its equals * method—either you can't change the equals method, or the equals 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 return Boolean, and in test code use "legacy" triple equals traits, whose === * operators return Option[String]. * *

* *

* This trait is the middle ground of the three legacy triple equals traits, in between * LegacyTripleEquals, the most lenient, and TypeCheckedLegacyTripleEquals, the most strict. * If LegacyTripleEquals is mixed in or imported, the === can be used with any two types * and still compile. If TypeCheckedLegacyTripleEquals 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 under TypeCheckedLegacyTripleEquals, it will also compile * type types that would be rejected by TypeCheckedLegacyTripleEquals, 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, * because Int and Long are not in a subtype/supertype relationship. (I.e., Int * is not a subtype or supertype of Long): *

* *
 * 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 types Int and Long because they are not directly related via * subtyping. However, an implicit widening conversion from Int to Long does exist (imported implicitly from * scala.Predef), so ConversionCheckedLegacyTripleEquals * 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 the Long 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 or TypeCheckedLegacyTripleEquals, * and can therefore be used to temporarily turn on or off conversion checking in a limited scope. * Because the methods in ConversionCheckedLegacyTripleEquals (and its siblings) * override all the methods defined in supertype TripleEqualsSupport, 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 to Any. Because Any 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, since List[Int] and * Vector[Int] are both subtypes of Seq[Int], so you could widen either type to Seq[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 trait ConversionCheckedLegacyTripleEquals that facilitates the importing of ConversionCheckedLegacyTripleEquals members as * an alternative to mixing it in. One use case is to import ConversionCheckedLegacyTripleEquals 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




© 2015 - 2025 Weber Informatics LLC | Privacy Policy