I'm reading the MEAP of the second edition of "Functional Programming in Scala" and I ran across the following in a listing:
In Parsers.scala:
trait Parsers[Parser[+_]]:
...
In Reference.scala we have:
object Reference extends Parsers[Reference.Parser]:
...
extension [A](p: Parser[A])
// A block providing implementations of the Parsers trait
...
In JSON.scala (an implemetation of a JSON parser using the Parsers trait):
def jsonParser[Parser[+_]](P: Parsers[Parser]): Parser[JSON] =
import P.*
...
and later in that file, in an example:
val parser = JSON.jsonParser(Reference)
...
I'm guessing that the import P.* in the jsonParser function is equivalent to import Reference.* but I've never seen this done before nor do I understand how this works. Is the argument to the function really just providing a reference (no pun intended) to the Reference object? Or is it doing something else? I would guess that this makes the specific type of parser available to the following code, but I'm just not sure.
At the very least I would like to see the documentation on this construct but I don't know what to search for.
Also, the 'self-reference' in the Reference object to extending Parsers[Reference.Parser] is a bit mind-bending. Is this (the Reference.Parser) making reference to the Parser extension block later in the object?
If so, it reminds me of the inheritance trick used by ATL back in the COM/DCOM days. That, also, took a while to really get a handle on...
import P.*just put all the methods defined inParsersin scope, it doesn't know, nor care, about any underlying implementation; it is just sugar syntax so you can do something likefoo(bar)instead ofP.foo(bar)- Aout the self reference I guess eitherParsersorReferencedefine a class, trait, type calledParserwhich is parametric, that is it, nothing fancy nor self-reference. - Luis Miguel Mejía Suárez