Scala Programming Language
  1. Scala Programming Language
  2. SI-5549

Match cases are not type constrained for simulated union type

    Details

    • Type: Bug Bug
    • Status: Closed
    • Priority: Major Major
    • Resolution: Not a Bug
    • Affects Version/s: None
    • Fix Version/s: None
    • Component/s: None
    • Labels:
      None

      Description

      See bug #2 in the context of this mailing list post:

      http://groups.google.com/group/shapeless-dev/browse_thread/thread/1f26830027f3517

      "2. It doesn't exclude the `D[¬[Double]]` case from the match."

      The union type is important. Ceylon has it, and afaics it is necessary for supporting heterogeneous collections type-safely without resorting to the (probably unbearable for novices, and more "Scala is difficult to read" accusations) complexity of the experimental metascala.

      P.S. There is a duplicate syntax-highlighted version buried nearer to the bottom of the following stackoverflow answer:

      http://stackoverflow.com/questions/3508077/does-scala-have-type-disjunction-union-types/7460312#7460312

        Activity

        Hide
        Paul Phillips added a comment -

        If I've understood you correctly - and I should have just closed this ticket, you left way too much for me to sort out just to figure out what you are reporting - you want this to statically exclude the third case:

          class D[-A] (v: A) { }
          def size(t: D[D[Int] with D[String]]) = t match {
            case x: D[D[Int]]    => 1
            case x: D[D[String]] => 2
            case x: D[D[Double]] => 3
            case _               => 4
          }
        

        That would be a reasonable thing to want, but you seem not to realize that it would not help you at all. The only case which will ever match there is the first one because of erasure. The compiler limits the parameters to D[D[Int]]s and D[D[String]]s, but you still won't be able to distinguish them by matching on their types. (You can distinguish them by matching on the value.) It doesn't bother trying to exclude D[D[Double]] because it can't tell one D[X] from another D[X] and it knows it.

        You're supposed to get unchecked warnings in a match like that - everyone ignores them, but at least I can say "if you'd read the unchecked warnings..." But actually it seems to be failing to warn with a parameterized type in the erasure zone, so I will open a ticket about that.

        Show
        Paul Phillips added a comment - If I've understood you correctly - and I should have just closed this ticket, you left way too much for me to sort out just to figure out what you are reporting - you want this to statically exclude the third case: class D[-A] (v: A) { } def size(t: D[D[Int] with D[String]]) = t match { case x: D[D[Int]] => 1 case x: D[D[String]] => 2 case x: D[D[Double]] => 3 case _ => 4 } That would be a reasonable thing to want, but you seem not to realize that it would not help you at all. The only case which will ever match there is the first one because of erasure. The compiler limits the parameters to D[D [Int] ]s and D[D [String] ]s, but you still won't be able to distinguish them by matching on their types. (You can distinguish them by matching on the value.) It doesn't bother trying to exclude D[D [Double] ] because it can't tell one D [X] from another D [X] and it knows it. You're supposed to get unchecked warnings in a match like that - everyone ignores them, but at least I can say "if you'd read the unchecked warnings..." But actually it seems to be failing to warn with a parameterized type in the erasure zone, so I will open a ticket about that.
        Hide
        Paul Phillips added a comment -

        Oh, and the OTHER reason it can't do that is that D is contravariant. In the size method above, you can call it with any subtype of D[D[Int] with D[String]], such as D[Any]. The pattern matcher does not deal in the same sorts of guarantees as the compiler.

        Show
        Paul Phillips added a comment - Oh, and the OTHER reason it can't do that is that D is contravariant. In the size method above, you can call it with any subtype of D[D [Int] with D [String] ], such as D [Any] . The pattern matcher does not deal in the same sorts of guarantees as the compiler.
        Hide
        Shelby Moore III added a comment - - edited

        I now see how many bugs you few core guys are handling, thus apologies for not making my code example concise. Will do in the future.

        The unchecked warning is appearing with my prior code, so no need to open a separate bug report.

        Thanks for catching the type hole that D[Any] was an allowable input to size. I fixed that below.

        scala> size(new D[Any](5 : Any))
        error: type mismatch;
         found   : D[Any]
         required: DD[D[Int] with D[String]]
               size(new D[Any](5 : Any))
                    ^
        

        Also, I was able to eliminate the unchecked warnings as follows.

        scala> class D[-A] (v: A) {
          def get = (v : Any)
        }
        defined class D
        
        scala> class DD[-A >: D[Any]] (v: A) {
          def get = (v : Any)
        }
        defined class DD
        
        scala> implicit def neg[A](x: A) = new DD[D[A]]( new D[A](x) )
        neg: [A](x: A)DD[D[A]]
        
        scala> def size(t: DD[D[Int] with D[String]]) = t.get match { case x: D[_] =>  
          x.get match {
            case x: Int => 1
            case x: String => 2
            case x: Double => 3
          }
        }
        size: (t: DD[D[Int] with D[String]])Int
        

        Why can't the compiler reason that the Double case above is unreachable?

        Afaik, Scala will prevent any call of size with a D[D[Double]], but at the JVM such can't be prevented due to type erasure. But what if I don't care about interoption at the JVM level of linkage? I want to assume that all callers will respect my higher-level API, where size only inputs D[D[Int] with D[String]].

        Is the current policy to make my desired assumption, or if not could there be a compiler flag to do so?

        Show
        Shelby Moore III added a comment - - edited I now see how many bugs you few core guys are handling, thus apologies for not making my code example concise. Will do in the future. The unchecked warning is appearing with my prior code, so no need to open a separate bug report. Thanks for catching the type hole that D [Any] was an allowable input to size. I fixed that below. scala> size(new D[Any](5 : Any)) error: type mismatch; found : D[Any] required: DD[D[Int] with D[String]] size(new D[Any](5 : Any)) ^ Also, I was able to eliminate the unchecked warnings as follows. scala> class D[-A] (v: A) { def get = (v : Any) } defined class D scala> class DD[-A >: D[Any]] (v: A) { def get = (v : Any) } defined class DD scala> implicit def neg[A](x: A) = new DD[D[A]]( new D[A](x) ) neg: [A](x: A)DD[D[A]] scala> def size(t: DD[D[Int] with D[String]]) = t.get match { case x: D[_] => x.get match { case x: Int => 1 case x: String => 2 case x: Double => 3 } } size: (t: DD[D[Int] with D[String]])Int Why can't the compiler reason that the Double case above is unreachable? Afaik, Scala will prevent any call of size with a D[D [Double] ], but at the JVM such can't be prevented due to type erasure. But what if I don't care about interoption at the JVM level of linkage? I want to assume that all callers will respect my higher-level API, where size only inputs D[D [Int] with D [String] ]. Is the current policy to make my desired assumption, or if not could there be a compiler flag to do so?
        Hide
        Shelby Moore III added a comment - - edited

        The following test example, shows that the compiler is enforcing unreachable code errors, so my prior comments about needing a compiler flag is not necessary.

        scala> def text(t : D[Int]) = t match {
          case x: D[Int] => 1
          case x: D[String] => 2
        }
        error: unreachable code
                 case x: D[String] => 2
                                      ^
        

        I realized why the compiler can't reason that the Double case is unreachable. There is no way to extract the parameter of D[Int] with D[String] from DD[-A], without losing the contravariance that gives the semantics of the union of Int and String. The extraction of the field v from DD and D is a two step process calling get twice, and afaics there is no way to express such in Scala to maintain the invariants of the combination (the type combining DD and D) across those two deconstruction steps.

        Show
        Shelby Moore III added a comment - - edited The following test example, shows that the compiler is enforcing unreachable code errors, so my prior comments about needing a compiler flag is not necessary. scala> def text(t : D[Int]) = t match { case x: D[Int] => 1 case x: D[String] => 2 } error: unreachable code case x: D[String] => 2 ^ I realized why the compiler can't reason that the Double case is unreachable. There is no way to extract the parameter of D [Int] with D [String] from DD [-A] , without losing the contravariance that gives the semantics of the union of Int and String. The extraction of the field v from DD and D is a two step process calling get twice, and afaics there is no way to express such in Scala to maintain the invariants of the combination (the type combining DD and D) across those two deconstruction steps.
        Hide
        Shelby Moore III added a comment -

        Please change to "Not A Bug" instead of current "Won't Fix". See my comments for what I learned since you closed it.

        Show
        Shelby Moore III added a comment - Please change to "Not A Bug" instead of current "Won't Fix". See my comments for what I learned since you closed it.
        Hide
        Shelby Moore III added a comment -

        This was not a bug. It is an example that probably the current type system can't fully express union types.

        Show
        Shelby Moore III added a comment - This was not a bug. It is an example that probably the current type system can't fully express union types.

          People

          • Assignee:
            Paul Phillips
            Reporter:
            Shelby Moore III
          • Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved:

              Development