class A class A2 extends A class B trait M[X] // // Upper Type Bound // def upperTypeBound[AA <: A](x: AA): A = x // equivalently, require a implicit parameter of <:<[AA, A]. // This technique must be used if A is an abstract type from the // enclosing scope. def upperTypeBound2[AA](x: AA)(implicit ev: AA <:< A): A = x // compiles to ev(x) // // Lower Type Bound // def lowerTypeBound[AA >: A](x: AA, pred: (AA => Boolean)): Boolean = pred(x) // equivalently, require a implicit parameter of <:<[A, AA]. // This technique must be used if A is an abstract type from the // enclosing scope. def lowerTypeBound2[AA >: A](x: AA, pred: (AA => Boolean))(implicit ev: A <:< AA): Boolean = pred(x) // // View Bound // def viewBound[AA <% A](x: AA): A = x //compiles to: def viewBound$[AA](x: AA)(implicit ev1$ : AA => A) = ev1$(x) // // Context Bound // def contextBound[X: M](x: X) = x; //compiles to: def contextBound$[X](x: X)(implicit ev1$ : M[X]) = x // In combination: def combo[AA <: A >: B <% String <% Int : M : Ordering] = 0 // compiles to: def combo[AA >: B <: A](implicit evidence$1: (AA) => String, evidence$2: (AA) => Int, evidence$3: M[AA], evidence$4: Ordering[AA] ): Int = 0 // // Usage // { { upperTypeBound(new A) upperTypeBound(new A2) } { val predA = (a: A) => true val predAny = (a: Any) => true lowerTypeBound(new A, predA) lowerTypeBound(new {}, predAny) } { implicit def String2A(s: String): A = new A viewBound("") } { implicit def MofA: M[A] = new M[A] {} contextBound(new A) } } implicit def int2str(i: Int): String = i.toString def f1[T <% String](t: T) = 0 f1(1) // This could also be expressed with a Context Bound, // with the help of a type alias representing functions // from type `F` to type `T`. trait To[T] { type From[F] = F => T } def f2[T : To[String]#From](t: T) = 0 f2(1) // A context bound must be used with a type constructor of kind `* => *`. // However the type constructor `Function1` is of kind `(*, *) => *`. // The use of the type alias partially applies second type parameter // with the type `String`, yielding a type constructor of the correct kind // for use as a context bound. // There is a proposal to allow you to directly express partially applied types // in Scala, without the use of the type alias inside a trait. You could then write: // // def f3[T : [X](X => String)](t: T) = 0