【问题标题】:Understanding Scala Implicits理解 Scala 隐式
【发布时间】:2018-09-23 19:07:39
【问题描述】:

在阅读 Chiusano 和 Bjarnason 的Scala 中的函数式编程时,我在第 9 章,解析器组合器中遇到了以下代码:

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 trait 本身中定义 |or 函数?
  3. asStringParser 究竟想要完成什么?它在这里的目的是什么?
  4. 为什么需要self?这本书说,“使用 self 明确消除对 trait 上的 or 方法的引用”,但这是什么意思?

我真的很喜欢这本书,但是本章中高级语言特定结构的使用阻碍了我的进步。如果你能向我解释这段代码是如何工作的,那将会很有帮助。我知道目标是通过|or 等运算符使库“更好”地使用,但不明白这是如何完成的。

【问题讨论】:

  • 1) 它被称为 类型推断,是 1973 年以后发明的几乎所有编程语言的基本特征。 4) 因为你想在 @987654334 中调用 or @,不是ParserOps中的那个。

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


【解决方案1】:
  1. 每个方法都有一个返回类型。在这种情况下,它是ParserOps[A]。您不必明确地写出来,因为在这种情况下可以自动推断。
  2. 可能是因为伴随对象中自动提供了ParserOps.apply-factory 方法。您在构造函数中需要更少的vals,并且您不需要new 关键字来实例化ParserOps。但是它不用于模式匹配,因此,您可以对普通(非case)类做同样的事情,没关系。
  3. 这是“pimp-my-library”模式。它将方法|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
    

Scala 中没有“真正的运算符”,一切都是方法。如果你想实现一个像中缀运算符一样行为的方法,你完全按照书中的方法去做。

【讨论】:

  • 很好的答案 - 让自己陷入本书的这一章,但这种解释很有意义。谢谢!
  • ...但我还是不太明白为什么asStringParser 接受任意类型A 加上隐式将其转换为字符串解析器。
  • @Grisha A => Parser[String] 部分在那里,例如,隐式方法可以将A = String 作为参数,然后使用string 将其转换为Parser[String],然后立即将其转换为ParserOps,以便您有效地在Strings 上获得解析器组合器操作。它被称为“链接隐式转换”。看一下this FAQ,特别是第二个代码块中bToC 转换的形状。 B1 = AB = Parser[String]C = ParserOps[String]也是一样。
  • 谢谢!他们是否试图通过A 尽可能保持灵活性,并期望它可能是其他String,只要存在隐式A => Parser[String] 转换?我无法为此创建一个用例。
  • @Grisha 如何组合Strings or 编译Regex 模式or 更复杂的组合一般Parser[X]s,例如?
猜你喜欢
  • 1970-01-01
  • 2019-01-26
  • 2017-01-30
  • 1970-01-01
  • 1970-01-01
  • 2020-12-08
  • 2019-12-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多