1
votes

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...

The import P.* just put all the methods defined in Parsers in scope, it doesn't know, nor care, about any underlying implementation; it is just sugar syntax so you can do something like foo(bar) instead of P.foo(bar) - Aout the self reference I guess either Parsers or Reference define a class, trait, type called Parser which is parametric, that is it, nothing fancy nor self-reference. - Luis Miguel Mejía Suárez