Skip to content

Instantly share code, notes, and snippets.

@bvenners
Created January 17, 2014 08:03
Show Gist options
  • Save bvenners/8469906 to your computer and use it in GitHub Desktop.
Save bvenners/8469906 to your computer and use it in GitHub Desktop.
Type checked contains method for GenSeq
scala> List(1, 2, 3) contains 1
res5: Boolean = true
scala> List(1, 2, 3) contains "1"
res6: Boolean = false
scala> import scala.collection.GenSeq
import scala.collection.GenSeq
scala> implicit class Containz[A](xs: GenSeq[A]) {
| def containz[B <: A](ele: B): Boolean = xs.exists(_ == ele)
| }
defined class Containz
scala> List(1, 2, 3) containz 1
res7: Boolean = true
scala> List(1, 2, 3) containz "1"
<console>:13: error: inferred type arguments [String] do not conform to method containz's type parameter bounds [B <: Int]
List(1, 2, 3) containz "1"
^
<console>:13: error: type mismatch;
found : String("1")
required: B
List(1, 2, 3) containz "1"
^
@bvenners
Copy link
Author

Oops, deleted my comment by mistake while trying to get left square brackets to show up. Dang. It was something like:

I eventually realized that the reason my implicit conversion worked in my initial example is that I'd gotten rid of covariance. List[Int] is covariant, but Containz[Int] is invariant. When I tried the same B <: A trick on an actually covariant type, I hit the problem:

scala> // This does not compile

scala> class MySeq1[+A] {
     |   def contains[B <: A](ele: B): Boolean = true
     | }
<console>:8: error: covariant type A occurs in contravariant position in type  <: A of type B
         def containz[B <: A](ele: B): Boolean = true
                      ^

scala>

scala> // This compiles, but of course will slam to Any

scala> class MySeq2[+A] {
     |   def containz[B >: A](ele: B): Boolean = true
     | }
defined class MySeq2

scala>

scala>

scala> class GoForIt\[A, B]
defined class GoForIt

scala>

scala> implicit def evidence[A, B](implicit ev: B <:< A): GoForIt[A, B] = new GoForIt[A, B]
evidence: [A, B](implicit ev: <:<[B,A])GoForIt [A,B]

scala>

scala> // This does not compile

scala> class MySeq3[+A] {
     |   def containz[B](ele: B)(implicit ev: GoForIt\A, B]): Boolean = true
     | }
<console>:10: error: covariant type A occurs in invariant position in type GoForIt\[A,B] of value ev
         def containz[B](ele: B)(implicit ev: GoForIt[A, B]): Boolean = true
                                          ^

So it seems the best way to have a type safe contains method on covariant Seq's in Scala is via an implicit conversion.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment