简体   繁体   中英

Understanding Scala Implicits

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:

  1. For the implicit operators function, why isn't there a return type?
  2. Why is ParserOps defined as a case class and why can't the | or or function be defined in the Parsers trait itself?
  3. What exactly is the asStringParser trying to accomplish? What is its purpose here?
  4. Why is self needed? 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.

  1. Every method has a return type. In this case, it's ParserOps[A] . You don't have to write it out explicitly, because in this case it can be inferred automatically.
  2. Probably because of the automatically provided ParserOps.apply -factory method in the companion object. You need fewer val s in the constructor, and you don't need the new keyword to instantiate ParserOps . It is not used in pattern matching though, so, you could do the same thing with an ordinary (non- case ) class, wouldn't matter.
  3. It's the "pimp-my-library"-pattern. It attaches methods | and or to Parser , without forcing Parser to inherit from anything. In this way, you can later declare Parser to be something like ParserState => Result[A] , but you will still have methods | and or available (even though Function1[ParserState, Result[A]] does not have them).
  4. You could put | and or directly in Parsers , but then you would have to use the syntax

     |(a, b) or(a, b) 

    instead of the much nicer

     a | b a or b 

There are no "real operators" in Scala, everything is a method. If you want to implement a method that behaves as if it were an infix operator, you do exactly what is done in the book.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM