Problem with type checking GADTs
See original GitHub issueHere’s an interpreter for simply typed lambda calculus:
sealed trait Exp[T]
case class Num(n: Int) extends Exp[Int]
case class Plus(e1: Exp[Int], e2: Exp[Int]) extends Exp[Int]
case class Var[T](name: String) extends Exp[T]
case class Lambda[T, U](x: Var[T], e: Exp[U]) extends Exp[T => U]
case class App[T, U](f: Exp[T => U], e: Exp[T]) extends Exp[U]
abstract class Env { outer =>
def apply[T](x: Var[T]): T
def + [T](xe: (Var[T], T)) = new Env {
def apply[T](x: Var[T]): T =
if (x == xe._1) xe._2.asInstanceOf[T]
else outer(x)
}
}
object Env {
val empty = new Env {
def apply[T](x: Var[T]): T = ???
}
}
object Test {
val exp = App(Lambda(Var[Int]("x"), Plus(Var[Int]("x"), Num(1))), Var[Int]("2"))
def eval[T](e: Exp[T])(env: Env): T = e match {
case Num(n) => n
case Plus(e1, e2) => eval(e1)(env) + eval(e2)(env)
case v: Var[T] => env(v)
case Lambda(x: Var[s], e) => ((y: s) => eval(e)(env + (x -> y)))
case App(f, e) => eval(f)(env)(eval(e)(env))
}
eval(exp)(Env.empty)
}
When we compile that, we get two mysterious warnings:
-- Warning: STL.scala:32:19 ----------------------------------------------------
32 | case Lambda(x: Var[s], e) => ((y: s) => eval(e)(env + (x -> y)))
| ^^^^^^^^^^^^^^^^^^^^
| There is no best instantiation of pattern type Lambda[Any, Any]
| that makes it a subtype of selector type Exp[T].
| Non-variant type variable U cannot be uniquely instantiated.
| (This would be an error under strict mode)
-- Warning: STL.scala:33:16 ----------------------------------------------------
33 | case App(f, e) => eval(f)(env)(eval(e)(env))
| ^^^^^^^^^
| There is no best instantiation of pattern type App[Any, T]
| that makes it a subtype of selector type Exp[T].
| Non-variant type variable T' cannot be uniquely instantiated.
|
| where: T is a type in method eval
| T' is a type variable
|
| (This would be an error under strict mode)
two warnings found
If we look under the hood we find that these stem from the fact that we somehow miss the right subtype constraints for the Lambda
and App
cases. I suspect something fundamental is wrong here. I don’t have the time to look into it now, but maybe someone who is up-to-speed with GADT matching can?
Issue Analytics
- State:
- Created 6 years ago
- Comments:18 (12 by maintainers)
Top Results From Across the Web
Complete and Decidable Type Inference for GADTs - Microsoft
Generalized Algebraic Data Types (GADTs) pose a particularly tough problem for type ... The type checking problem for GADTs is decidable (CH03; SP07)....
Read more >Fundeps and GADTs: When is type checking decidable?
I believe GADT type checking is always decidable; it's inference which is undecidable, as it requires higher order unification.
Read more >Type Inference for GADTs, OutsideIn and Anti-unification
paper presents a type inference algorithm for GADTs that ex- tends OutsideIn algorithm using anti-unification to capture the relationship between the types ...
Read more >Simple Unification-based Type Inference for GADTs
Goal: Add GADTs to Haskell. • Problem: GADT type inference is hard. • Requirements: – Simple, declarative specification. – Easy to implement in...
Read more >Sound and Complete Bidirectional Typechecking for Higher ...
These two features lead to difficulties in typechecking for GADTs. Universal, existentials, and type inference. Practical typed functional languages must ...
Read more >Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start FreeTop Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Top GitHub Comments
I believe this issue has been fixed by https://github.com/lampepfl/dotty/pull/3918, but I’ll leave it open until someone makes a PR to turn the example code into positive and negative testcases.
I remember people proposing
case class App[type X1, type X2, Y]...
to achieve that effect. Not sure on the plans on it.But adding such type members by default instead of requiring
type
there would be a breaking change relative to Scala2: something likeclass FunnyApp extends App2[...] {type X1 = Int}
works in Scala2 but wouldn’t work as intended ifApp2
had type memberX1
.