// // References // - http://stackoverflow.com/questions/3427345/what-do-and-mean-in-scala-2-8-and-where-are-they-documented // - http://stackoverflow.com/questions/2603003/operator-in-scala // - https://gist.github.com/retronym/229163 // - http://debasishg.blogspot.com/2010/08/using-generalized-type-constraints-how.html#sthash.GKfUGq9p.dpuf // - http://www.scala-lang.org/old/node/4041.html // - https://groups.google.com/forum/#!searchin/scala-user/generic$20type$20constraint/scala-user/mgx9-TUapQo/tiN6x818dKsJ // // Scala 2.8 introduces generalized type constraints which 3 variants: // // - A =:= B means A must be exactly B // (equalTo) // - A <:< B means A must be a subtype of B, i.e. A must conform to B // (analogous to the simple type constraint <:) // (conformsTo) // - A <%< B means A must be viewable as B, possibly via implicit conversion // (analogous to the simple type constraint <%) // (visibleAs) // // Unlike <: or >:, the generalized type constraints are not operators. They // are classes, and instances of which are implicitly provided by the compiler // itself to enforce conformance to the type constraints. // // // Type Constraints from Predef.scala: // used, for example, in the encoding of generalized constraints // we need a new type constructor `<:<` and evidence `conforms`, as // reusing `Function2` and `identity` leads to ambiguities (any2stringadd is inferred) // to constrain any abstract type T that's in scope in a method's argument list (not just the method's own type parameters) // simply add an implicit argument of type `T <:< U`, where U is the required upper bound (for lower-bounds, use: `U <: T`) // in part contributed by Jason Zaugg // sealed abstract class <:<[-From, +To] extends (From => To) implicit def conforms[A]: A <:< A = new (A <:< A) { def apply(x: A) = x } // not in the <:< companion object because it is also intended to subsume identity (which is no longer implicit) sealed abstract class =:=[From, To] extends (From => To) object =:= { implicit def tpEquals[A]: A =:= A = new (A =:= A) { def apply(x: A) = x } } sealed abstract class <%<[-From, +To] extends (From => To) object <%< { implicit def conformsOrViewsAs[A <% B, B]: A <%< B = new (A <%< B) { def apply(x: A) = x } } // // Examples // case class Foo[A](a: A) { // 'A' can be substituted with any type // getStringLength can only be used if this is a Foo[String] def length(implicit evidence: A =:= String) = a.length } scala> Foo(1234).length :10: error: Cannot prove that Int =:= String. Foo(1234).length ^ scala> Foo("asdf").length res1: Int = 4 // // orNull from Option.scala // /** * Returns the option's value if it is nonempty, * or `null` if it is empty. * Although the use of null is discouraged, code written to use * $option must often interface with code that expects and returns nulls. * @example {{{ * val initalText: Option[String] = getInitialText * val textField = new JComponent(initalText.orNull,20) * }}} */ @inline final def orNull[A1 >: A](implicit ev: Null <:< A1): A1 = this getOrElse null scala> Some("string").orNull res3: String = string scala> Some(1231).orNull :8: error: Cannot prove that Null <:< Int. Some(1231).orNull // // joinsRight and joinsLeft from Either.scala // /** * Joins an `Either` through `Right`. * * This method requires that the right side of this Either is itself an * Either type. That is, this must be some type like: {{{ * Either[A, Either[A, C]] * }}} (which respects the type parameter bounds, shown below.) * * If this instance is a Right[Either[A, C]] then the contained Either[A, C] * will be returned, otherwise this value will be returned unmodified. * * @example {{{ * Right[String, Either[String, Int]](Right(12)).joinRight // Result: Right(12) * Right[String, Either[String, Int]](Left("flower")).joinRight // Result: Left("flower") * Left[String, Either[String, Int]]("flower").joinRight // Result: Left("flower") * }}} * * This method, and `joinLeft`, are analogous to `Option#flatten` */ def joinRight[A1 >: A, B1 >: B, C](implicit ev: B1 <:< Either[A1, C]): Either[A1, C] = this match { case Left(a) => Left(a) case Right(b) => b } /** * Joins an `Either` through `Left`. * * This method requires that the left side of this Either is itself an * Either type. That is, this must be some type like: {{{ * Either[Either[C, B], B] * }}} (which respects the type parameter bounds, shown below.) * * If this instance is a Left[Either[C, B]] then the contained Either[C, B] * will be returned, otherwise this value will be returned unmodified. * * {{{ * Left[Either[Int, String], String](Right("flower")).joinLeft // Result: Right("flower") * Left[Either[Int, String], String](Left(12)).joinLeft // Result: Left(12) * Right[Either[Int, String], String]("daisy").joinLeft // Result: Right("daisy") * }}} * * This method, and `joinRight`, are analogous to `Option#flatten` */ def joinLeft[A1 >: A, B1 >: B, C](implicit ev: A1 <:< Either[C, B1]): Either[C, B1] = this match { case Left(a) => a case Right(b) => Right(b) } // // toMap from TraversableOnce // - toMap only works if Traversable contains 2-tuples // def toMap[T, U](implicit ev: A <:< (T, U)): immutable.Map[T, U] = { val b = immutable.Map.newBuilder[T, U] for (x <- self) b += x b.result } // // Example that illustrates how type constraints can be used to supply // method to a generic class that can only be used under certain // conditions // case class Pair[T](first: T, second: T) { def smaller(implicit ev: T <:< Ordered[T]): T = { if (first < second) first else second } } // // Use type constraint when the type inteference cannot figure out the // types. Here the type inferencer attempts to determinte the types // A and C in a single step. Help it along, by first matching on type // C and then on A // def firstLast[A, C <: Iterable[A]](it: C) = (it.head, it.tail) scala> firstLast(List(1,2,3)) :9: error: inferred type arguments [Nothing,List[Int]] do not conform to method firstLast's type parameter bounds [A,C <: Iterable[A]] firstLast(List(1,2,3)) ^ :9: error: type mismatch; found : List[Int] required: C firstLast(List(1,2,3)) ^ def firstLast[A, C](it: C)(implicit ev: C <:< Iterable[A]) = (it.head, it.tail) scala> firstLast(List(1,2,3)) res2: (Int, Iterable[Int]) = (1,List(2, 3)) // Alternatively, you can get specific about types by using // higher kinded types scala> import scala.language.higherKinds scala> def firstLast[A, C[B] <: Iterable[B]](it: C[A]) = (it.head, it.tail) firstLast: [A, C[B] <: Iterable[B]](it: C[A])(A, Iterable[A]) scala> firstLast(List(1,2,3)) res1: (Int, Iterable[Int]) = (1,List(2, 3)) // This however doesn't give what I intended scala> def firstLast[T, C[_] <: Iterable[_]](it: C[T]) = (it.head, it.tail) firstLast: [T, C[_] <: Iterable[_]](it: C[T])(Any, Repr) scala> firstLast(List(1,2,3)) res0: (Any, Repr) = (1,List(2, 3))