Search code examples
scalaparser-combinatorspath-dependent-type

Foiled by path-dependent types


I'm having trouble using, in one trait, a Parser returned from a method in another trait. The compiler complains of a type mismatch and it appears to me that the problem is due to the path-dependent class. I'm not sure how to get what I want.

trait Outerparser extends RegexParsers {

  def inner: Innerparser

  def quoted[T](something: Parser[T]) = "\"" ~> something <~ "\""
  def quotedNumber = quoted(inner.number)     // Compile error
  def quotedLocalNumber = quoted(number)      // Compiles just fine
  def number: Parser[Int] = ("""[1-9][0-9]*"""r) ^^ {str => str.toInt}

}

trait Innerparser extends RegexParsers {

  def number: Parser[Int] = ("""[1-9][0-9]*"""r) ^^ {str => str.toInt}

}

And the error:

[error] /Path/to/MyParser.scala:6: type mismatch
[error]  found   : minerals.Innerparser#Parser[Int]
[error]  required: Outerparser.this.Parser[?]
[error]   def quotedNumber = quoted(inner.number)

I sort-of get the idea: each "something" method is defining a Parser type whose path is specific to the enclosing class (Outerparser or Innerparser). The "quoted" method of Outerparser expects an an instance of type Outerparser.this.Parser but is getting Innerparser#Parser.

I like to be able to use quoted with a parser obtained from this class or some other class. How can I do that?


Solution

  • You can use a self-type annotation to make it compile while still preserving the modularity:

    trait OuterParser extends RegexParsers { this: InnerParser =>
      def quoted[T](something: Parser[T]) = "\"" ~> something <~ "\""
      def quotedNumber = quoted(number)     // Compile error
    }
    
    trait InnerParser extends RegexParsers {
      def number: Parser[Int] = ("""[1-9][0-9]*"""r) ^^ {str => str.toInt}
    }
    
    object MyCompleteParser extends OuterParser with InnerParser
    

    The self-type annotation basically says that OuterParser depends InnerParser (they must be mixed together to create a proper instantiatable class). The compiler thus knows for sure that in OuterParser and InnerParser, Parser refers to the same type.