Skip to content

GADT matching doesn't brings subtyping relation into scope #9833

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
IndiscriminateCoding opened this issue Sep 20, 2020 · 4 comments · Fixed by #12847
Closed

GADT matching doesn't brings subtyping relation into scope #9833

IndiscriminateCoding opened this issue Sep 20, 2020 · 4 comments · Fixed by #12847

Comments

@IndiscriminateCoding
Copy link

IndiscriminateCoding commented Sep 20, 2020

Minimized code

object Main extends App:
  enum Extends[A, B]:
    case Ev[B, A <: B]() extends (A Extends B)

    def cast(a: A): B = this match {
      case Extends.Ev() => a
    }

Output

Found:    (a : A)
Required: B

Expectation

I expect A <: B to be visible inside Extends.Ev() case body

@abgruszecki
Copy link
Contributor

This is an interesting issue.

First off, GADTs currently cannot constrain type parameters of classes: #4323 (though this might actually be changing in the future, there's a person on this right now!)

Even if we modify cast to be a standalone function, the following error is emitted:

scala>   enum Extends[A, B]:                                                                                                                                                                                                                                                          
     |     case Ev[B, A <: B]() extends (A Extends B)
     | 
     |   def cast[A, B](a: A, ev: A Extends B): B = ev match {
     |     case Extends.Ev() => a
     |   }
     | 
5 |    case Extends.Ev() => a
  |         ^
  |         Type argument A does not conform to upper bound B

I think this error is not necessary. I will need to take a closer look.

Bottom line is, this works: https://github.com/lampepfl/dotty/blob/master/tests/neg/gadt-approximation-interaction.scala#L1

  enum SUB[-A, +B]:
    case Refl[S]() extends SUB[S, S]

  def foo[T](t: T, ev: T SUB Int) =
    ev match { case SUB.Refl() =>
      t + 2
    }

@IndiscriminateCoding
Copy link
Author

I have another case where I expect GADTs to bring contraint to scope:

enum Ev[F <: AnyKind]:
  case HK() extends Ev[List]

def f[F <: AnyKind](ev: Ev[F]): Unit = ev match {
  case Ev.HK() =>
    type T = F[Int] // Error: F does not take type parameters
    ()
}

In this example, Ev.HK is intended to be an evidence that F is of kind F[_] (but it doesn't work).
Looks like currently GADTs are only capable of bringing equality contraints to a scope (not subtyping or kind contraints).

@bishabosha
Copy link
Member

@IndiscriminateCoding in this example you will have to extract F out using a type pattern:

enum Ev[F <: AnyKind]:                                                                                           
  case HK() extends Ev[List]

def f[F <: AnyKind](ev: Ev[F]): Unit = ev match {
  case Ev.HK(): Ev[f]  =>
    val m: f[Int] = List(1,2,3)
    ()
}

@IndiscriminateCoding
Copy link
Author

@bishabosha Interesting!
I didn't know that it is possible to introduce local types in such way - is it new (undocumented?) dotty feature?

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

Successfully merging a pull request may close this issue.

4 participants