The teleological answer is that this is because contains is defined in SeqLike , but not in ParSeqLike .
If this does not satisfy your curiosity, you may find that SeqLike contains is defined as follows:
def contains(elem: Any): Boolean = exists (_ == elem)
So for your example you can write
List.range(0,100).par.exists(_ == 2)
ParSeqLike also lacks several other methods, some of which are difficult to implement efficiently (for example, indexOfSlice ), and some for less obvious reasons (for example, combinations - perhaps because it is useful only for small data sets). But if you have a parallel collection, you can also use .seq to return to the linear version and return your methods:
List.range(0,100).par.seq.contains(2)
As for why the library designers left this ... I fully guess, but maybe they wanted to reduce the number of methods for simplicity, and it is almost as easy to use exists .
This also raises the question of why contains is defined on SeqLike and not on the grandfather of all collections, GenTraversableOnce , where do you find exists ? A possible reason is that contains for Map semantically different from the Set and Seq methods. A Map[A,B] is Traversable[(A,B)] , so if contains were defined for Traversable , contains need to take a tuple (A,B) ; however, Map contains accepts only argument A Given this, I think that contains should be defined in GenSeqLike - perhaps this is an oversight that will be fixed.
(At first I thought that parallel sequences do not have contains , because searching where you are going to stop after finding your target in parallel collections is much less efficient than the linear version (various threads make a lot of unnecessary work after the value is found: see this question ), but it may not be right because there exists .)