Reputation: 6160
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
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
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