16

I would like to call 'contains' on my Iterables :-)

Eugene Yokota
  • 94,654
  • 45
  • 215
  • 319
adam77
  • 2,797
  • 3
  • 21
  • 29

2 Answers2

27

The reason Iterable does not have a contains method is because the way it is defined can have direct consequences on variance. Basically, there are two type signatures that make sense for it:

def contains(v: Any): Boolean
def contains(v: A): Boolean

The second definition has increased type safety. However, A, which is the type parameter of collection, appears in a contra-variant position, which forces the collection to be invariant. It could be defined like this:

def contains[B >: A](v: B): Boolean

but that wouldn't offer any improvement over the first signature, using Any.

As a consequence of this, you'll see that immutable.Seq is co-variant and uses the first signature, while immutable.Set is invariant and uses the second signature.

Daniel C. Sobral
  • 295,120
  • 86
  • 501
  • 681
  • Note: `contains` **is** implemented using the signature `contains[A1 >: A](elem: A1)` in `SeqLike` (at least in Scala 2.11.8). I do not think this is the same as using `Any` - it places some constraints on the `B` type - you can pass `Any`, but you cannot pass a type which is known to be unrelated. – Suma Nov 25 '16 at 10:19
  • @Suma Sure you can. Go ahead and try. If you pass an unrelated type, `A1` will be inferred to be the common supertype. And because everything is descendant from `Any`, then all types have a common supertype with each other. – Daniel C. Sobral Nov 30 '16 at 21:56
  • You are right. Is there any reason why is the signature in the library as it is, and not with `Any`, as you write, then? – Suma Dec 01 '16 at 07:14
5

I don't know why contains is not defined on Iterable or TraversableOnce, but you could easily define it yourself:

class TraversableWithContains[A](underlying: TraversableOnce[A]) {
  def contains(v: Any): Boolean =
    underlying.exists(_ == v)
}
implicit def addContains[A](i: Iterable[A]) = new TraversableWithContains(i)

and use it as if it were defined on Iterable:

val iterable: Iterable[Int] = 1 to 4
assert(iterable.contains(3))
assert(!iterable.contains(5))
mkneissl
  • 4,902
  • 2
  • 26
  • 28