Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>The problem is that <code>Seq</code> is covariant in its type parameter. This makes a lot of sense for the majority of its functionality. As an immutable container, it really <em>should</em> be covariant. Unfortunately, this does get in the way when they have to define a method which takes some of the parameterized type. Consider the following example:</p> <pre><code>trait Seq[+A] { def apply(i: Int): A // perfectly valid def contains(v: A): Boolean // does not compile! } </code></pre> <p>The problem is that functions are always contravariant in their parameter types and covariant in their return types. Thus, the <code>apply</code> method can return a value of type <code>A</code> because <code>A</code> is covariant along with the return type for <code>apply</code>. However, <code>contains</code> <em>cannot</em> take a value of type <code>A</code> because its parameter must be contravariant.</p> <p>This problem can be solved in different ways. One option is to simply make <code>A</code> an invariant type parameter. This allows it to be used in both covariant and contravariant contexts. However, this design would mean that <code>Seq[String]</code> would <em>not</em> be a subtype of <code>Seq[Any]</code>. Another option (and the one most often used) is to employ a local type parameter which is bounded below by the covariant type. For example:</p> <pre><code>trait Seq[+A] { def +[B &gt;: A](v: B): Seq[B] } </code></pre> <p>This trick retains the <code>Seq[String] &lt;: Seq[Any]</code> property as well as provides some very intuitive results when writing code which uses heterogeneous containers. For example:</p> <pre><code>val s: Seq[String] = ... s + 1 // will be of type Seq[Any] </code></pre> <p>The results of the <code>+</code> function in this example is a value of type <code>Seq[Any]</code>, because <code>Any</code> is the least upper-bound (LUB) for the types <code>String</code> and <code>Int</code> (in other words, the least-common supertype). If you think about it, this is exactly the behavior we would expect. If you create a sequence with both <code>String</code> and <code>Int</code> components, then its type <em>should</em> be <code>Seq[Any]</code>.</p> <p>Unfortunately, this trick, while applicable to methods like <code>contains</code>, produces some surprising results:</p> <pre><code>trait Seq[+A] { def contains[B &gt;: A](v: B): Boolean // compiles just fine } val s: Seq[String] = ... s contains 1 // compiles! </code></pre> <p>The problem here is that we are calling the <code>contains</code> method passing a value of type <code>Int</code>. Scala sees this and tries to infer a type for <code>B</code> which is a supertype of both <code>Int</code> and <code>A</code>, which in this case is instantiated as <code>String</code>. The LUB for these two types is <code>Any</code> (as shown earlier), and so the local type instantiation for <code>contains</code> will be <code>Any =&gt; Boolean</code>. Thus, the <code>contains</code> method <em>appears</em> to not be type safe.</p> <p>This result isn't an issue for <code>Map</code> or <code>Set</code> because neither of them are covariant in their parameter types:</p> <pre><code>trait Map[K, +V] { def contains(key: K): Boolean // compiles } trait Set[A] { def contains(v: A): Boolean // also compiles } </code></pre> <p>So, long story short, the <code>contains</code> method on covariant container types cannot be restricted to only take values of the component type because of the way that functions types work (contravariant in their parameter types). This isn't really a limitation of Scala or bad implementation, it's a mathematical fact.</p> <p>The consolation prize is that this really isn't an issue in practice. And, as the other answers have mentioned, you can always define your own implicit conversion which adds a "type-safe" <code>contains</code>-like method if you <em>really</em> need the extra check.</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload