While reading Functional Programming in Scala by Chiusano and Bjarnason, I encountered the following code in chapter 9, Parser Combinators:
trait Parsers[ParseError, Parser[+_]] { self =>
...
def or[A](s1: Parser[A], s2: Parser[A]): Parser[A]
implicit def string(s: String): Parser[String]
implicit def operators[A](p: Parser[A]) = ParserOps[A](p)
implicit def asStringParser[A](a: A)(implicit f: A => Parser[String]):
ParserOps[String] = ParserOps(f(a))
case class ParserOps[A](p: Parser[A]) {
def |[B>:A](p2: Parser[B]): Parser[B] = self.or(p,p2)
def or[B>:A](p2: => Parser[B]): Parser[B] = self.or(p,p2)
}
}
I understand that if there is a type incompatibility or missing parameters during compilation, the Scala compiler would look for a missing function that converts the non-matching type to the desired type or a variable in scope with the desired type that fits the missing parameter respectively.
If a string occurs in a place that requires a Parser[String], the string function in the above trait should be invoked to convert the string to a Parser[String].
However, I've difficulties understanding the operators and asStringParser functions. These are the questions that I have:
- For the implicit operators function, why isn't there a return type?
- Why is ParserOps defined as a
case classand why can't the|ororfunction be defined in the Parsers trait itself? - What exactly is the
asStringParsertrying to accomplish? What is its purpose here? - Why is
selfneeded? The book says, "Use self to explicitly disambiguate reference to the or method on the trait," but what does it mean?
I'm truly enjoying the book but the use of advanced language-specific constructs in this chapter is hindering my progress. It would be of great help if you can explain to me how this code works. I understand that the goal is to make the library "nicer" to use through operators like | and or, but don't understand how this is done.
orinParsers, not the one inParserOps. - Jörg W Mittag