简体   繁体   English

在Scala中使用路径相关类型(解析器)进行纯函数编程?

[英]Pure Functional Programming with Path Dependent Types (Parsers) in Scala?

So when using Scala Parsers one may have: 因此,在使用Scala解析器时,可能会有:

case class MyParser(foos: List[String]) extends Parsers {
  val bars: List[Parser[String]] = foos.map(parserFromString)

  // Expensive function
  def parserFromString(s: String): Parser[String] = ???
}

Parser is a path-dependent type, so this code cannot be refactored so that bars can be passed in from the constructor. Parser是与路径有关的类型,因此无法重构此代码,以便可以从构造函数中传递bars Note that parserFromString in my use case actually creates a MyParser in such a way that MyParser construction becomes O(N!) where N = foos.size . 需要注意的是parserFromString在我的使用情况下,实际上创建了一个MyParser以这样的方式MyParser建设成为O(N!),其中N = foos.size

So suppose now I wish to add to bars via another foo . 所以假设现在我想通过另一个foo添加到bars The FP way would be to refactor to include bars in the constructor, then define something like def +(foo: String): MyParser = copy(bars = bars :+ parserFromString(foo) , but as mentioned I can't I have to recreate from scratch. FP方式将是重构以在构造函数中包含bars ,然后定义类似def +(foo: String): MyParser = copy(bars = bars :+ parserFromString(foo) ,但是如上所述,我不必从头开始重新创建。

My solution is just make bars a private var and mutate it with a Unit method update , ie def update(foo: String): Unit = bars +:= parserFromString(foo) 我的解决方案是使barsprivate var变量,并使用Unit方法update对其进行突变,即def update(foo: String): Unit = bars +:= parserFromString(foo)

My first question is quite simple: am I stuck? 我的第一个问题很简单:我被卡住了吗? Do I have to use mutation? 我必须使用突变吗?

Second question: does Parboiled2 suffer from this? 第二个问题:Parboiled2是否遭受此痛苦? Do they use path-dependent types (at a glance it doesn't look like it), and if so why do Scala parsers use path-dependent types? 他们是否使用依赖于路径的类型(乍一看看起来并不像),如果是,为什么Scala解析器使用依赖于路径的类型?

If parboiled2 does not suffer from path-dependent types this alone can be a reason to use it! 如果parboiled2没有依赖于路径的类型,那么这可能就是使用它的原因!

If anyone is wondering why I need to create Parser s from parameters it's because I'm implementing a language where users can define macros and use those macros in defining other macros. 如果有人想知道为什么需要从参数创建Parser ,那是因为我正在实现一种语言,用户可以定义宏并在定义其他宏时使用这些宏。 So no, before anyone tries telling me to change the design, I can't. 所以不可以,在任何人告诉我更改设计之前,我不能。 I also really don't want mutability since I'm going to be needing multi-threading later. 我也真的不想要可变性,因为以后我将需要多线程。

Parser is a path-dependent type, so this code cannot be refactored so that bars can be passed in from the constructor. Parser是与路径有关的类型,因此无法重构此代码,以便可以从构造函数中传递小节。

Yes, it can: 是的,它可以:

// could also be trait and mixed in where you need it
object MyParsers extends Parsers {
  case class MyParser(bars: List[Parser[String]]) {
    def +(foo: String): MyParser = copy(bars = bars :+ parserFromString(foo))
    ...
  }
}

does Parboiled2 suffer from this? 受此困扰吗? Do they use path-dependent types (at a glance it doesn't look like it) 他们是否使用依赖于路径的类型(乍一看看起来并不像)

No, as you can see from the examples . 不,从示例中可以看到。

and if so why do Scala parsers use path-dependent types 如果是这样,为什么Scala解析器使用路径相关类型

As shown above, this shouldn't be a problem. 如上所示,这应该不成问题。 I have run into trouble with path-dependent types in some circumstances, but not in use of parsers. 遇到了在某些情况下,路径依赖类型的麻烦,但没有使用解析器。

For choice between Scala-parsers and parboiled, I'd personally think more about performance, which DSL you prefer, etc. Last time I've looked at parboiled, it had no real way to affect error reporting, but this is fixed now . 对于在Scala解析器和parboiled之间进行选择,我个人会更多地考虑性能,您更喜欢哪种DSL等。上次查看parboiled时,它没有影响错误报告的真正方法,但是现在已解决

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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