question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Problem with type checking GADTs

See original GitHub issue

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?

Issue Analytics

  • State:closed
  • Created 6 years ago
  • Comments:18 (12 by maintainers)

github_iconTop GitHub Comments

1reaction
smartercommented, Feb 14, 2018

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.

1reaction
Blaisorbladecommented, Feb 5, 2018

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 like class FunnyApp extends App2[...] {type X1 = Int} works in Scala2 but wouldn’t work as intended if App2 had type member X1.

Read more comments on GitHub >

github_iconTop 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 >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found