import scala.language.{ higherKinds, postfixOps } import scala.reflect.runtime.universe._ object Test { object Variances extends Enumeration { val CO, IN, CONTRA = Value } import Variances.{ CO, IN, CONTRA } object SubtypeRelationship extends Enumeration { val NONE, SAME, SUB, SUPER = Value } import SubtypeRelationship.{ NONE, SAME, SUB, SUPER } class VarianceTester[T, U, CC[_]](expected: Variances.Value)( implicit ev1: TypeTag[T], ev2: TypeTag[U], ev3: TypeTag[CC[T]], ev4: TypeTag[CC[U]]) { def elements = List(ev1.tpe <:< ev2.tpe, ev2.tpe <:< ev1.tpe) def containers = List(ev3.tpe <:< ev4.tpe, ev4.tpe <:< ev3.tpe) def isUnrelated = typeCompare[T, U] == NONE def isSame = typeCompare[T, U] == SAME def isSub = typeCompare[T, U] == SUB def isSuper = typeCompare[T, U] == SUPER def showsCovariance = (elements == containers) def showsContravariance = (elements == containers.reverse) def showsInvariance = containers forall (_ == isSame) def allContainerVariances = List(showsCovariance, showsInvariance, showsContravariance) def showsExpectedVariance = if (isUnrelated) allContainerVariances forall (_ == false) else if (isSame) allContainerVariances forall (_ == true) else expected match { case CO => showsCovariance && !showsContravariance && !showsInvariance case IN => showsInvariance && !showsCovariance && !showsContravariance case CONTRA => showsContravariance && !showsCovariance && !showsInvariance } } def showsCovariance[T, U, CC[_]](implicit ev1: TypeTag[T], ev2: TypeTag[U], ev3: TypeTag[CC[T]], ev4: TypeTag[CC[U]]) = new VarianceTester[T, U, CC](CO) showsExpectedVariance def showsInvariance[T, U, CC[_]](implicit ev1: TypeTag[T], ev2: TypeTag[U], ev3: TypeTag[CC[T]], ev4: TypeTag[CC[U]]) = new VarianceTester[T, U, CC](IN) showsExpectedVariance def showsContravariance[T, U, CC[_]](implicit ev1: TypeTag[T], ev2: TypeTag[U], ev3: TypeTag[CC[T]], ev4: TypeTag[CC[U]]) = new VarianceTester[T, U, CC](CONTRA) showsExpectedVariance def typeCompare[T, U](implicit ev1: TypeTag[T], ev2: TypeTag[U]) = (ev1.tpe <:< ev2.tpe, ev2.tpe <:< ev1.tpe) match { case (true, true) => SAME case (true, false) => SUB case (false, true) => SUPER case (false, false) => NONE } def assertAnyRef[T: TypeTag] = List( typeOf[T] <:< typeOf[Any], typeOf[T] <:< typeOf[AnyRef], !(typeOf[T] <:< typeOf[AnyVal]) ) foreach (assert(_, "assertAnyRef")) def assertAnyVal[T: TypeTag] = List( typeOf[T] <:< typeOf[Any], !(typeOf[T] <:< typeOf[AnyRef]), typeOf[T] <:< typeOf[AnyVal] ) foreach (assert(_, "assertAnyVal")) def assertSameType[T: TypeTag, U: TypeTag] = assert(typeCompare[T, U] == SAME, "assertSameType") def assertSuperType[T: TypeTag, U: TypeTag] = assert(typeCompare[T, U] == SUPER, "assertSuperType") def assertSubType[T: TypeTag, U: TypeTag] = assert(typeCompare[T, U] == SUB, "assertSubType") def assertNoRelationship[T: TypeTag, U: TypeTag] = assert(typeCompare[T, U] == NONE, "assertNoRelationship") def testVariancesVia[T: TypeTag, U: TypeTag] = assert( typeCompare[T, U] == SUB && showsCovariance[T, U, List] && showsInvariance[T, U, Set], "testVariancesVia" ) def runAllTests = { assertAnyVal[AnyVal] assertAnyVal[Unit] assertAnyVal[Int] assertAnyVal[Double] assertAnyVal[Boolean] assertAnyVal[Char] assertAnyRef[AnyRef] assertAnyRef[java.lang.Object] assertAnyRef[java.lang.Integer] assertAnyRef[java.lang.Double] assertAnyRef[java.lang.Boolean] assertAnyRef[java.lang.Character] assertAnyRef[String] assertAnyRef[scala.List[String]] assertAnyRef[scala.List[_]] // variance doesn't work yet // testVariancesVia[String, Any] // testVariancesVia[String, AnyRef] assertSubType[List[String], List[Any]] assertSubType[List[String], List[AnyRef]] assertNoRelationship[List[String], List[AnyVal]] assertSubType[List[Int], List[Any]] assertSubType[List[Int], List[AnyVal]] assertNoRelationship[List[Int], List[AnyRef]] // Nothing assertSubType[Nothing, Any] assertSubType[Nothing, AnyVal] assertSubType[Nothing, AnyRef] assertSubType[Nothing, String] assertSubType[Nothing, List[String]] assertSubType[Nothing, Null] assertSameType[Nothing, Nothing] // Null assertSubType[Null, Any] assertNoRelationship[Null, AnyVal] assertSubType[Null, AnyRef] assertSubType[Null, String] assertSubType[Null, List[String]] assertSameType[Null, Null] assertSuperType[Null, Nothing] // Any assertSameType[Any, Any] assertSuperType[Any, AnyVal] assertSuperType[Any, AnyRef] assertSuperType[Any, String] assertSuperType[Any, List[String]] assertSuperType[Any, Null] assertSuperType[Any, Nothing] // Misc unrelated types assertNoRelationship[Unit, AnyRef] assertNoRelationship[Unit, Int] assertNoRelationship[Int, Long] assertNoRelationship[Boolean, String] assertNoRelationship[List[Boolean], List[String]] assertNoRelationship[Set[Boolean], Set[String]] } def main(args: Array[String]): Unit = runAllTests }