Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Match cases are not type constrained for simulated union type #5549

Closed
scabug opened this issue Mar 5, 2012 · 7 comments
Closed

Match cases are not type constrained for simulated union type #5549

scabug opened this issue Mar 5, 2012 · 7 comments
Assignees

Comments

@scabug
Copy link

scabug commented Mar 5, 2012

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

@scabug
Copy link
Author

scabug commented Mar 5, 2012

Imported From: https://issues.scala-lang.org/browse/SI-5549?orig=1
Reporter: Shelby Moore III (shelby)

@scabug
Copy link
Author

scabug commented May 4, 2012

@paulp said:
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.

@scabug
Copy link
Author

scabug commented May 4, 2012

@paulp said:
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.

@scabug
Copy link
Author

scabug commented May 4, 2012

Shelby Moore III (shelby) said (edited on Nov 7, 2012 3:46:52 PM UTC):
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?

@scabug
Copy link
Author

scabug commented May 4, 2012

Shelby Moore III (shelby) said (edited on Nov 7, 2012 3:47:15 PM UTC):
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.

@scabug
Copy link
Author

scabug commented May 4, 2012

Shelby Moore III (shelby) said:
Please change to "Not A Bug" instead of current "Won't Fix". See my comments for what I learned since you closed it.

@scabug
Copy link
Author

scabug commented May 4, 2012

Shelby Moore III (shelby) said:
This was not a bug. It is an example that probably the current type system can't fully express union types.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants