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.

Nonunified types in GADT pattern match

See original GitHub issue

Compiler version

3.1.3

Minimized code

enum PingMessage[Response]:
  case Ping(from: String) extends PingMessage[String]

val pongBehavior: [O] => (Unit, PingMessage[O]) => (Unit, O) = [O] =>
  (state: Unit, msg: PingMessage[O]) =>
    msg match
      case PingMessage.Ping(from) => ((), s"Pong from $from")

Scastie: https://scastie.scala-lang.org/omCKwdwSQ2uGyiYSPRbOLQ

Output

Found:    [O] => (Unit, PingMessage[O]) => (Unit, String)
Required: [O] => (Unit, PingMessage[O²]) => (Unit, O²)

where:    O  is a type variable
          O² is a type variable

Expectation

Compiles successfully

Issue Analytics

  • State:closed
  • Created a year ago
  • Comments:7 (3 by maintainers)

github_iconTop GitHub Comments

1reaction
dwijnandcommented, Jul 1, 2022

Again: keep an eye out for variance. If you switch to a covariant Option/Some instead of sticking to something invariant like PingMessage/Ping was, the behaviour is going to be different.

1reaction
SethTisuecommented, Jul 1, 2022

Dale and I have been looking at this further.

Our current hypothesis is that the root cause here is that when a polymorphic function literal is desugared, the result type of the apply method is always left blank and it’s up to type inference to fill it in. Desugar.scala line 1734 creates this DefDef:

DefDef(nme.apply, applyTParams :: applyVParams :: Nil, TypeTree(), res)

where the TypeTree() is the missing result type of the apply.

Let’s return to Dale’s minimization. To keep this straight in your head, it’s helpful not to reuse the same type parameter name. So the minimization becomes:

val poly: [O1] => Option[O1] => O1 =    // line 1
  [O2] =>                               // line 2
    (opt: Option[O2]) => ...            // line 3

line 1 is where we say what the expected type of the polymorphic function is, and that expected type includes the information that the body’s result type is O1.

But when lines 2 and 3 are desugared, the desugaring includes def apply(...) = , whereas what we want is def apply(...): O2 = ..., since if we don’t put O2 there, inference puts String there instead, which is wrong.

So we’d need to take the expected type [O1] => Option[O1] => O1, extract the third O1, replace all of the type parameters according to their indices so that O1 becomes O2, and then fill that in.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Haskell pattern matching on GADTs with Data Kinds
I have found that I really like combining GADTs with Data Kinds, as it gives me further type safety than before (for most...
Read more >
GADT pattern matching exhaustiveness - OCaml Discuss
I'm guessing that the type unification must have some module bounds. It can infer it correctly when all types are in the same...
Read more >
6.4.8. Generalised Algebraic Data Types (GADTs) - Haskell.org
The key point about GADTs is that pattern matching causes type refinement. For example, in the right hand side of the equation.
Read more >
Extensions to Type Classes and Pattern Match Checking
Generalised Algebraic Data Types (GADTs), pattern guards, en view patterns is het redeneren over pattern matching veel complexer geworden. Hoewel.
Read more >
Complete and Decidable Type Inference for GADTs - Microsoft
Generalized Algebraic Data Types (GADTs) pose a particularly ... The key difficulty is that a GADT pattern match brings local type constraints into...
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