Open scabug opened 12 years ago
Imported From: https://issues.scala-lang.org/browse/SI-5195?orig=1 Reporter: @pchiusano
@pchiusano said: This might be caused by the same thing as #127. That issue seems to be a bit different - that class is not really a GADT since has no type parameters - but the behavior is similar in that a type parameter is not being properly refined by a pattern match, so maybe it's the same thing.
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: has anyone checked Dotty?
@SethTisue said: @pchiusano I notice the "affects version" field here is blank. curious if this regressed in the new pattern matcher, or if it never worked.
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. https://gist.github.com/snoble/0385b32e8699843e0b2034b749f66734#gistcomment-2318457
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.