-
Notifications
You must be signed in to change notification settings - Fork 21
Pattern matching does not appropriately refine types as expected for GADTs #5195
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
Comments
Imported From: https://issues.scala-lang.org/browse/SI-5195?orig=1 |
@pchiusano said: In the case I reported, it is an invariant type parameter to a data constructor that is not being refined - in #127, it is just a type parameter to the function. |
@SethTisue said: |
@SethTisue said: |
I was working on type-aligned lists and encountered this bug (or a related bug). @snoble and I put together a small example with various variants, as well as compilation status from Scala 2.11, Scala 2.12, and Dotty (which @SethTisue was interested in). The results are interesting and not so consistent even in Dotty. sealed trait TList[X, Z]
case class TCons[X, Y, Z](head: X => Y, tail: TList[Y,Z]) extends TList[X, Z]
case class TEnd[X,Z](f: X => Z) extends TList[X, Z]
object Evaluators {
def safe1[X,Z](x: X, tList: TList[X, Z]): Z = tList match {
case TCons(head, tail) => safe1(head(x), tail)
case TEnd(f) => f(x)
}
// dotty 0.5 : Warning
// scala 2.11.8: Fine
// scala 2.12.4: Fine
def unsafe1[X,Z](x: X, tList: TList[X, Z]): Z = tList match {
case TCons(head, tail) => safe1("WTF", tail)
case TEnd(f) => f(x)
}
// dotty 0.5 : Warning
// scala 2.11.8: Fine
// scala 2.12.4: Fine
def safe2[X,Z](x: X, tList: TList[X, Z]): Z = tList match {
case tCons: TCons[X, _, Z] => safe2(tCons.head(x), tCons.tail)
case TEnd(f) => f(x)
}
// dotty 0.5 : Error
// scala 2.11.8: Fine
// scala 2.12.4: Fine
def unsafe2[X,Z](x: X, tList: TList[X, Z]): Z = tList match {
case tCons: TCons[X, _, Z] => unsafe2("WTF", tCons.tail)
case TEnd(f) => f(x)
}
// dotty 0.5 : Error
// scala 2.11.8: Error
// scala 2.12.4: Error
def safe3[X,Y,Z](x: X, tList: TList[X, Z]): Z = tList match {
case tCons: TCons[X, Y, Z] => safe3(tCons.head(x), tCons.tail)
case TEnd(f) => f(x)
}
// dotty 0.5 : Fine
// scala 2.11.8: Warning (Erasure)
// scala 2.12.4: Warning (Erasure)
def unsafe3[X,Y,Z](x: X, tList: TList[X, Z]): Z = tList match {
case tCons: TCons[X, Y, Z] => unsafe3("WTF", tCons.tail)
case TEnd(f) => f(x)
}
// dotty 0.5 : Error
// scala 2.11.8: Error
// scala 2.12.4: Error
} |
An important part of GADT support is that pattern matching should recover / refine type information. I posted some examples here: https://gist.github.com/1369239 Here's a smaller snippet:
Note the commented out line - pattern matching on Flip does not refine the existing type parameters. In comparison, this Haskell code compiles as expected. The pattern match on Flip refines the type of the function f, so you can call swap on its result without any cast.
In addition to the type refinement issue, I get spurious "constructor cannot be instantiated to expected type" problems which are probably related to the same underlying problem. The commented out line below does not compile - I've tried a few other variations of it that also don't compile.
It seems like most pattern matches that would result in type refinement of any existing type parameters are considered compile errors ("constructor cannot be instantiated to expected type"). There also seem to be some cases where Scala does the right thing, like the line above {noformat}case Par(f2, g2) => Par(f pipe f2, g pipe g2){noformat}. I don't have a clear model for why it works in some cases but not others.
It would be great if Scala included better GADT support. As a personal anecdote - I was working on a library for describing distributed computations and decided to try encoding it as a GADT. The description of the distributed computation could then be examined by multiple distribution strategies, which could optimize the descriptions via (well-typed) pattern matching before actually doing the distributed execution. I ran into so many problems with getting this to work properly that I eventually gave up and resorted to making everything untyped.
The text was updated successfully, but these errors were encountered: