Note that there are some explanatory texts on larger screens.

plurals
  1. POSelectively disable subsumption in Scala? (correctly type List.contains)
    primarykey
    data
    text
    <pre class="lang-scala prettyprint-override"><code>List("a").contains(5) </code></pre> <p>Because an <code>Int</code> can never be contained in a list of <code>String</code>, this <strong>should generate an error at compile-time</strong>, but it does not.</p> <p>It wastefully and silently tests every <code>String</code> contained in the list for equality to <code>5</code>, which can never be true (<code>"5"</code> never equals <code>5</code> in Scala).</p> <p>This has been named "<a href="https://groups.google.com/group/scala-debate/browse_thread/thread/d9ec90236b77cbe7?fwc=1" rel="nofollow noreferrer">the 'contains' problem</a>". And <a href="http://lambda-the-ultimate.org/node/4377#comment-67603" rel="nofollow noreferrer">some have implied</a> that if a type system cannot correctly type such semantics, then why go through the extra effort for enforcing types. So I consider it is an important problem to solve.</p> <p>The type parametrization <code>B &gt;: A</code> of <code>List.contains</code> inputs any type that is a supertype of the type <code>A</code> (the type of the elements contained in the list).</p> <pre class="lang-scala prettyprint-override"><code>trait List[+A] { def contains[B &gt;: A](x: B): Boolean } </code></pre> <p>This type parametrization is necessary because the <code>+A</code> declares that the list is <a href="https://stackoverflow.com/questions/56860/what-is-the-liskov-substitution-principle/8279878#8279878">covariant</a> on the type <code>A</code>, thus <code>A</code> can't be used in the contravariant position, i.e. as the type of an input parameter. Covariant lists (which must be immutable) are <a href="https://stackoverflow.com/questions/7266596/why-avoid-subtyping/8352969#8352969">much more powerful for extension</a> than invariant lists (which can be mutable).</p> <p><code>A</code> is a <code>String</code> in the problematic example above, but <code>Int</code> is not a supertype of <code>String</code>, so what happened? The <a href="http://lambda-the-ultimate.org/node/4377#comment-67659" rel="nofollow noreferrer">implicit subsumption</a> in Scala, decided that <code>Any</code> is a mutual supertype of both <code>String</code> and <code>Int</code>.</p> <p>The creator of Scala, Martin Odersky, <a href="http://lambda-the-ultimate.org/node/4377#comment-67646" rel="nofollow noreferrer">suggested</a> that a fix would be to limit the input type <code>B</code> to only those types that have an equals method that <code>Any</code> doesn't have.</p> <pre class="lang-scala prettyprint-override"><code>trait List[+A] { def contains[B &gt;: A : Eq](x: B): Boolean } </code></pre> <p>But that doesn't solve the problem, because two types (where the input type is not supertype of the type of the elements of the list) might have a mutual supertype which is a subtype of <code>Any</code>, i.e. also a subtype of <code>Eq</code>. Thus, it would compile without error and the incorrectly typed semantics would remain.</p> <p>Disabling implicit subsumption every where is <a href="http://lambda-the-ultimate.org/node/4377#comment-67657" rel="nofollow noreferrer">not an ideal solution</a> either, because implicit subsumption is why the following example for subsumption to <code>Any</code> works. And we wouldn't want to be forced to use type casts when the receiving site (e.g. passing as a function argument) has correctly typed semantics for a mutual supertype (that might not even be <code>Any</code>).</p> <pre class="lang-scala prettyprint-override"><code>trait List[+A] { def ::[B &gt;: A](x: B): List[B] } val x : List[Any] = List("a", 5) // see[1] </code></pre> <p><a href="http://www.codecommit.com/blog/scala/diving-into-scala" rel="nofollow noreferrer">&#91;1&#93; List.apply calls the :: operator</a>.</p> <p>So my question is what is the best fix to this problem?</p> <p>My tentative conclusion is that implicit subsumption should be turned off at the definition site where the semantics are otherwise not typed correctly. I will be providing an answer that shows how to turn off implicit subsumption at the method definition site. Are there alternative solutions?</p> <p>Please note this problem is general, and not isolated to lists.</p> <p><strong>UPDATE</strong>: I have <a href="https://issues.scala-lang.org/browse/SI-7857" rel="nofollow noreferrer">filed an improvement request</a> and started a <a href="https://groups.google.com/forum/#!topic/scala-language/B6f-iubR9Mk" rel="nofollow noreferrer">scala discussion thread on this</a>. I have also added comments under Kim Stebel's and Peter Schmitz's answers showing that their answers have erroneous functionality. Thus there is no solution. Also at the aforementioned discussion thread, I explained why I think soc's answer is not correct.</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
 

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