Skip to content

Unsoundness due to GADT bounds ignoring type (un)injectivity #5658

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

Closed
abgruszecki opened this issue Dec 23, 2018 · 2 comments
Closed

Unsoundness due to GADT bounds ignoring type (un)injectivity #5658

abgruszecki opened this issue Dec 23, 2018 · 2 comments
Assignees

Comments

@abgruszecki
Copy link
Contributor

abgruszecki commented Dec 23, 2018

Current treatment of type constructors by constraint inference is blatantly unsound, as we simply ignore the problem of constructor injectivity. As an example, following test causes a runtime exception:

object Test {
  sealed trait EQ[A, B]
  final case class Refl[T]() extends EQ[T, T]

  def absurd[F[_], X, Y](eq: EQ[F[X], F[Y]], x: X): Y = eq match {
    case Refl() => x
  }

  var ex: Exception = _
  try {
    type Unsoundness[X] = Int
    val s: String = absurd[Unsoundness, Int, String](Refl(), 0)
  } catch {
    case e: ClassCastException => ex = e
  }

  def main(args: Array[String]) =
    assert(ex != null)
}

(https://github.com/dotty-staging/dotty/blob/d1fa5d73c4194448e1ab0d4d4e39a764b6e1f651/tests/run/gadt-injectivity-unsoundness.scala)

This was intentionally extracted out of #5611.

@abgruszecki abgruszecki self-assigned this Dec 23, 2018
@abgruszecki abgruszecki changed the title Pattern matching does not consider injectivity of HK types Unsoundness due to GADT bounds ignoring type (un)injectivity Jan 2, 2019
@Blaisorblade
Copy link
Contributor

https://www.microsoft.com/en-us/research/uploads/prod/2019/03/ho-haskell-5c8bb4918a4de.pdf is relevant.

From Sec. 3's intro:

[...] we distinguish the arrow of type constructors (matchable) from that of type families (unmatchable) and use two different symbols: (→), i.e. Haskell’s existing function arrow for the former, and (↠) for the latter. […] matchability corresponds to functions that are both generative and injective.

Works: ∀ (f :: ⋆ → ⋆). (f a ∼ f b) ⇒ a ~ b, because f must be an actual data type (like a class).
Fails: ∀ (f :: ⋆ ↠ ⋆). (f a ∼ f b) ⇒ a ~ b, because f might be any constant function, even a constant one, like Unsoundness above.

@abgruszecki
Copy link
Contributor Author

Was fixed in #6398.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants