Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

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

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?

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

Successfully merging a pull request may close this issue.

4 participants