将标签附加到 Scala 中的不可变案例类

发布于 2024-09-13 17:29:57 字数 949 浏览 5 评论 0原文

我试图为一种小型语言创建一个解析器,其中包含标签和 goto 等命令:

...
lazy val cmds  = opt("{")~>rep(cmd<~opt(";"))<~opt("}") ^^ {...}
lazy val cmd = ("if"~boolexpr~"then"~cmds~"else"~cmds 
   ^^ { case _~b~_~c1~_~c2 => IFCMD(boolexpr,c1
 | ident ~":="~numericLit ^^ {case i1~_~v => ASSIGN(i1,v) }
 | "goto" ~>ident ^^ { case l => GOTO(l) }
 | ident~":"~cmd ^^ { case l~_~c  => <APPENDLABELTO_CORE>
...

GOTOIFCMD 等是扩展抽象类 Core 的案例类

为了与功能/类scala/不可变对象方式保持一致,我认为像这样定义Core错误

abstract class Core(var label:Option[String] = None )

但允许我用以下内容替换该部分 : with:

 | ident~":"~cmd ^^ { case l~_~c  => c.label = Some(l); c }

任何人都可以指出执行此操作的“scalaish”方法吗?

(我已经尝试过 c copy (label=Some(l)) 但抽象基类没有自动复制构造函数的魔力)

Im trying to create a parser for a small language with commands including labels and goto:

...
lazy val cmds  = opt("{")~>rep(cmd<~opt(";"))<~opt("}") ^^ {...}
lazy val cmd = ("if"~boolexpr~"then"~cmds~"else"~cmds 
   ^^ { case _~b~_~c1~_~c2 => IFCMD(boolexpr,c1
 | ident ~":="~numericLit ^^ {case i1~_~v => ASSIGN(i1,v) }
 | "goto" ~>ident ^^ { case l => GOTO(l) }
 | ident~":"~cmd ^^ { case l~_~c  => <APPENDLABELTO_CORE>
...

the GOTO, IFCMD etc are case classes extending abstract class Core

In keeping with the functional/scala-like/immutable-objecty -way I'm thinking that defining Core like this is wrong:

abstract class Core(var label:Option[String] = None )

but would allow me to replace the part with <APPENDLABELTO_CORE> with:

 | ident~":"~cmd ^^ { case l~_~c  => c.label = Some(l); c }

Can anyone point out the "scalaish" way to do this?

( I've tried c copy (label=Some(l)) but the abstract base class hasn't got the automatic copy constructor magic )

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

夏日落 2024-09-20 17:29:57

完全有可能创建您自己的类似复制的方法:

abstract class Core(val label: Option[String]) {
  def set(label: Option[String]): Core
}
class Impl(label: Option[String] = None) extends Core(label) {
  def set(label: Option[String] = this.label) = new Impl(label)
}

如此使用:

scala> val i = new Impl
i: Impl = Impl@1930ebb

scala> i.label
res0: Option[String] = None

scala> i.set(label = Some("thing"))
res1: Impl = Impl@b28f30

scala> res1.label
res2: Option[String] = Some(thing)

但是,实际上,我不会太快地放弃在这里使用 vars。推断不可变值更容易,但据我所知,您得到的值在解析器中得到了很好的隔离。另一种想法是创建一个方法,在最后将所有内容转换为不可变版本,或者如果解析器代码最终将所有数据存储在其他地方,则仅使其可变。

另一种方法是使抽象类不再抽象,而是实际使其成为一个案例类。您可以从案例类派生类(但是,从案例类派生案例类是禁忌)。那么技巧就是将您可能需要保存的变量数据保存在字段中:

abstract class SpecializedStuff { }
case class ParticularStuff(val i: Int) extends SpecializedStuff
case class Core(details: SpecializedStuff, label: Option[String] = None)
class Impl(p: ParticularStuff) extends Core(p)

scala> val i = new Impl( ParticularStuff(5) )
i: Impl = Core(ParticularStuff(5),None)

scala> i.copy(label = Some("thing"))
res0: Core = Core(ParticularStuff(5),Some(thing))

It is entirely possible to create your own copy-like method:

abstract class Core(val label: Option[String]) {
  def set(label: Option[String]): Core
}
class Impl(label: Option[String] = None) extends Core(label) {
  def set(label: Option[String] = this.label) = new Impl(label)
}

used thusly:

scala> val i = new Impl
i: Impl = Impl@1930ebb

scala> i.label
res0: Option[String] = None

scala> i.set(label = Some("thing"))
res1: Impl = Impl@b28f30

scala> res1.label
res2: Option[String] = Some(thing)

But, pragmatically, I wouldn't be too quick to dismiss the use of vars here. It is easier to reason about immutable values, but the ones you get are pretty well isolated within the parser, as far as I can tell. An alternative idea would be to create a method that converts everything to an immutable version at the end, or if the parser code ends up storing all the data elsewhere anyway, just leaving it mutable.

Another way to go is to make the abstract class not abstract, but to actually make it a case class. You can derive classes from a case class (deriving case classes from case classes is a no-no, however). The trick then would be to make your variable data that you may need to preserve live in a field:

abstract class SpecializedStuff { }
case class ParticularStuff(val i: Int) extends SpecializedStuff
case class Core(details: SpecializedStuff, label: Option[String] = None)
class Impl(p: ParticularStuff) extends Core(p)

scala> val i = new Impl( ParticularStuff(5) )
i: Impl = Core(ParticularStuff(5),None)

scala> i.copy(label = Some("thing"))
res0: Core = Core(ParticularStuff(5),Some(thing))
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文