public
Last active

Tour of Scala Type Bounds

  • Download Gist
type-bounds.scala
Scala
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
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

this is the very definition of "screwy"...

Great summary and reference. It shows that multiple view and context bounds are possible, but this does not work with upper bounds and lower bounds. I believe multiple upper bounds can be specified with with: [A <: B with C], and multiple lower bounds are discussed there: http://stackoverflow.com/questions/6117999/multiple-lower-type-bounds-in-scala

Could you add such examples for completeness's sake?

I'd like to see some example of "abstract type from the enclosing scope".

Me, too! ("I'd like to see some example of "abstract type from the enclosing scope")

trait M[A] {
   def a: A
   def inc(implicit A <:< Int): Int = 1 + a
}

Thanks!
A little bug, though ;-)
Fixed:

trait M[A] {
   def a: A
   def inc(implicit ev: A <:< Int): Int = 1 + a
}```

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.