了解Scala Implicits

时间:2018-04-13 12:12:25

标签: scala parsing functional-programming implicit case-class

在Chiusano和Bjarnason阅读 Scala中的函数编程时,我在第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)
  }
}

据我所知,如果在编译期间存在类型不兼容或缺少参数,Scala编译器会查找缺少的函数,该函数将非匹配类型转换为所需类型或范围内的变量,并且具有适合的所需类型分别缺少参数。

如果字符串出现在需要Parser[String]的地方,则应引发上述特征中的字符串函数以将字符串转换为Parser[String]

但是,我在理解operatorsasStringParser功能时遇到了困难。这些是我的问题:

  1. 对于隐式运算符函数,为什么不存在返回类型?
  2. 为什么ParserOps定义为case class,为什么不能在Parsers特征本身中定义|or函数?
  3. asStringParser到底想要完成的是什么?它的目的是什么?
  4. 为什么需要self?该书说,"使用self来明确消除对特征或方法的引用的歧义,"但这是什么意思?
  5. 我真的很喜欢这本书,但本章中使用高级语言特定的结构阻碍了我的进步。如果你能向我解释这段代码是如何工作的,那将会很有帮助。我知道我们的目标是让图书馆“更好”。通过|or等运营商使用,但不了解这是如何完成的。

1 个答案:

答案 0 :(得分:3)

  1. 每个方法都有一个返回类型。在这种情况下,它是ParserOps[A]。您不必明确地将其写出来,因为在这种情况下,它可以自动推断。
  2. 可能是因为伴随对象中自动提供的ParserOps.apply - 工厂方法。您在构造函数中需要更少的val,并且您不需要new关键字来实例化ParserOps。虽然它不用于模式匹配,所以,你可以用普通的(非case)类做同样的事情,不重要。
  3. 它是" pimp-my-library" -pattern。它将方法|or附加到Parser,而不强制Parser继承任何内容。通过这种方式,您可以稍后将Parser声明为ParserState => Result[A],但您仍然可以使用方法|or(即使Function1[ParserState, Result[A]]没有他们)。
  4. 您可以将|or直接放在Parsers中,但之后您必须使用语法

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

    而不是更好的

    a | b
    a or b
    
  5. 没有"真正的运营商"在Scala中,一切都是一种方法。如果你想实现一个行为就好像它是一个中缀运算符的方法,那么你就完全按照书中所做的那样做了。