Scala 中具有路径相关类型(解析器)的纯函数式编程?
Pure Functional Programming with Path Dependent Types (Parsers) in Scala?
因此,当使用 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
是路径依赖类型,所以这段代码无法重构,使得bars
可以从构造函数中传入。请注意,在我的用例中,parserFromString
实际上创建了一个 MyParser
,使得 MyParser
构造变为 O(N!),其中 N = foos.size
.
所以假设现在我想通过另一个 foo
添加到 bars
。 FP 方法是重构以在构造函数中包含 bars
,然后定义类似 def +(foo: String): MyParser = copy(bars = bars :+ parserFromString(foo)
的内容,但如前所述,我不能从头开始重新创建。
我的解决方案是将 bars
设为 private var
并使用 Unit
方法 update
对其进行变异,即 def update(foo: String): Unit = bars +:= parserFromString(foo)
我的第一个问题很简单:我卡住了吗?我必须使用突变吗?
第二个问题:Parboiled2是否有此问题?它们是否使用路径依赖类型(乍一看不像),如果是,为什么 Scala 解析器使用路径依赖类型?
如果 parboiled2 不受路径依赖类型的影响,这本身就是使用它的理由!
如果有人想知道为什么我需要从参数创建 Parser
s,那是因为我正在实现一种语言,用户可以在其中定义宏并在定义其他宏时使用这些宏。所以不,在任何人试图告诉我改变设计之前,我不能。我也真的不想要可变性,因为稍后我将需要多线程。
Parser
is a path-dependent type, so this code cannot be refactored so that bars can be passed in from the constructor.
是的,它可以:
// 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)
没有,从examples.
可以看出
and if so why do Scala parsers use path-dependent types
如上所示,这应该不是问题。在某些情况下,我 运行 遇到了路径相关类型的麻烦,但没有使用解析器。
对于 Scala 解析器和 parboiled 之间的选择,我个人会更多地考虑性能,您更喜欢哪种 DSL 等。上次我查看 parboiled 时,它没有真正的方法来影响错误报告,但是这个is fixed now.
因此,当使用 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
是路径依赖类型,所以这段代码无法重构,使得bars
可以从构造函数中传入。请注意,在我的用例中,parserFromString
实际上创建了一个 MyParser
,使得 MyParser
构造变为 O(N!),其中 N = foos.size
.
所以假设现在我想通过另一个 foo
添加到 bars
。 FP 方法是重构以在构造函数中包含 bars
,然后定义类似 def +(foo: String): MyParser = copy(bars = bars :+ parserFromString(foo)
的内容,但如前所述,我不能从头开始重新创建。
我的解决方案是将 bars
设为 private var
并使用 Unit
方法 update
对其进行变异,即 def update(foo: String): Unit = bars +:= parserFromString(foo)
我的第一个问题很简单:我卡住了吗?我必须使用突变吗?
第二个问题:Parboiled2是否有此问题?它们是否使用路径依赖类型(乍一看不像),如果是,为什么 Scala 解析器使用路径依赖类型?
如果 parboiled2 不受路径依赖类型的影响,这本身就是使用它的理由!
如果有人想知道为什么我需要从参数创建 Parser
s,那是因为我正在实现一种语言,用户可以在其中定义宏并在定义其他宏时使用这些宏。所以不,在任何人试图告诉我改变设计之前,我不能。我也真的不想要可变性,因为稍后我将需要多线程。
Parser
is a path-dependent type, so this code cannot be refactored so that bars can be passed in from the constructor.
是的,它可以:
// 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)
没有,从examples.
可以看出and if so why do Scala parsers use path-dependent types
如上所示,这应该不是问题。在某些情况下,我 运行 遇到了路径相关类型的麻烦,但没有使用解析器。
对于 Scala 解析器和 parboiled 之间的选择,我个人会更多地考虑性能,您更喜欢哪种 DSL 等。上次我查看 parboiled 时,它没有真正的方法来影响错误报告,但是这个is fixed now.