Created
January 17, 2014 08:03
-
-
Save bvenners/8469906 to your computer and use it in GitHub Desktop.
Type checked contains method for GenSeq
This file contains hidden or 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> 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" | |
| ^ |
Yep, this approach with an implicit class is probably the most convenient way of casting off the variance that cripples type checking for contains.
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
Yeah, but you are cheating Paul. Anyway, mercy apart, your workaround doesn't break Set and Map (arguably the collections on which you want to call
contains), as they are invariant in the key: