Skip to content

Problem with type checking GADTs #3666

Closed
@odersky

Description

@odersky

Here'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?

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions