Created
November 8, 2009 08:02
-
-
Save retronym/229163 to your computer and use it in GitHub Desktop.
Demo of generalised type constraints in Scala 2.8
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// scala 2.7 simple type constraint. This can only constrain a type parameter of this function. | |
// Below, in ListW.sumint28, we can't use this approach because we want to constrain T, | |
// a type param of the enclosing trait. | |
def sumint27A[T <: Int](l: List[T]) : Int = l.reduceLeft((a: Int, b: Int) => a + b) | |
trait IntLike[X] extends (X => Int) | |
object IntLike { | |
implicit val intIntLike: IntLike[Int] = new IntLike[Int] { def apply(x: Int) = identity(x) } | |
} | |
trait ListW[T] { | |
val l: List[T] | |
// T can be constrained within the context of this function by requiring an implicit parameter | |
// to certify that it is IntLike. | |
def sumint27B(implicit ev: IntLike[T]) : Int = l.map(ev).reduceLeft(_ + _) | |
// In scala 2.8, we can require an implicit parameter of type T <:< Int (that is, <:<[T, Int]) | |
// The compiler will provide this if T conforms to Int. The parameter `ev` can be used to | |
// safely cast a value T to Int. | |
def sumint28(implicit ev: T <:< Int) : Int = l.map(ev).reduceLeft(_ + _) | |
} | |
implicit def ListW[T](list: List[T]): ListW[T] = new ListW[T] { | |
val l = list | |
} | |
val intList = List(1, 2, 3) | |
val stringList = List("1", "2", "3") | |
sumint27A(intList) | |
{ | |
import IntLike._ | |
intList.sumint27B | |
} | |
intList.sumint28 | |
/* Compiler errors: | |
sumint27A(stringList) // type arguments [java.lang.String] do not conform to method sumint27's type parameter bounds [T <: Int] | |
stringList.sumint27 // compile error: could not find implicit value for parameter ev: IntLike[Any] | |
stringList.sumint28 // compile error: could not find implicit value for parameter ev: <:<[java.lang.String,Int] | |
*/ |
It comes in handy when pimping generic types, e.g:
class Tuple2W[A, B](t2: (A, B)) { def toSeq[C](implicit ac: A <:< C, bc: B <:Here we aren't relying on any properties of the types, just finding the Least Upper Bound
C
.This can also be used at times when a methods with a regular type bound fails type inference. No hard and fast rules, just can try it as a method of last resort if inference isn't playing nicely.
def foo[X, Y <: FooX = ...
def foo[X, Y <: Foo[X](implicit yfx: Y <:< Foo[X]) = ...
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Just out of curiosity, when would you use this sort of subclassing constraint rather than a type class?