Ladlestein
Ladlestein

Reputation: 6160

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?

Upvotes: 1

Views: 215

Answers (2)

R&#233;gis Jean-Gilles
R&#233;gis Jean-Gilles

Reputation: 32719

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.

Upvotes: 1

Owen
Owen

Reputation: 39366

For better or for worse, the way you usually use the Scala parser combinator libraries is to wrap everything in an enclosing trait or object that extends a trait like RegexParsers. I'm not quite sure why the API was designed like this.

But, anyway, once they all belong to the same instance of RegexParsers, they all see the same Parser type:

trait Everything extends RegexParsers {
    trait Outerparser {
        ...
    }

    trait Innerparser {
        ...
    }
}

And everyone is happy.


Don't think of it as putting everything in the same scope; think of it as the parser-combinator API's kooky way of importing names, ie, you could just as easily do

import scala.util.parsing.combinator._
import scala.util.parsing.input._

object blah extends RegexParsers
import blah._

trait Outerparser {
    ...
}

trait Innerparser {
    ...
}

Upvotes: 2

Related Questions