scala-parser-combinators icon indicating copy to clipboard operation
scala-parser-combinators copied to clipboard

Scala 3 union types don't always play nicely with choice

Open FlorianCassayre opened this issue 3 years ago • 0 comments

  • Scala: 3.X
  • scala-parser-combinators: 2.1.1

Consider the following motivating example:

import scala.util.parsing.combinator.Parsers

enum Token {
  case A()
  case B()
  case C()
}

object Parser extends Parsers {
  override type Elem = Token

  def tokenA: Parser[Token.A] = ???
  def tokenB: Parser[Token.B] = ???
  def tokenC: Parser[Token.C] = ???

  def tokenABC: Parser[Token.A | Token.B | Token.C] =
    tokenA | tokenB | tokenC  // error
}

Unfortunately the definition tokenABC doesn't compile:

Found:    Parser[Token]
Required: Parser[Token.A | Token.B | Token.C]
    tokenA | tokenB | tokenC

A workaround is to ascribe tokenA to the desired union type:

def tokenABC: Parser[Token.A | Token.B | Token.C] =
  (tokenA: Parser[Token.A | Token.B | Token.C]) | tokenB | tokenC

However this looks unnatural and arguably not very intuitive.

If we look at the source, this is how the choice operator is defined:

def | [U >: T](q: => Parser[U]): Parser[U]

And it is now evident why the code in the above example fails to compile. The obvious change would be to update the signature to:

def | [U](q: => Parser[U]): Parser[T | U]

Which would definitely break cross compatibility and therefore wouldn't be a viable solution.

FlorianCassayre avatar Apr 06 '22 08:04 FlorianCassayre