在Scala中为不可变的case类添加标签

时间:2010-08-11 14:10:28

标签: scala functional-programming parser-combinators case-class

我尝试使用包含标签和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-like / immutable-objecty -way保持一致,我认为像这样定义Core 错误

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

但允许我用<APPENDLABELTO_CORE>替换部件:

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

有人能指出“scalaish”这样做吗?

(我试过c copy (label=Some(l)),但是抽象基类没有自动复制构造函数魔法)

1 个答案:

答案 0 :(得分:4)

完全可以创建自己的类似副本的方法:

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)

但是,实际上,我不会过于迅速地拒绝在这里使用变量。关于不可变值的推理更容易,但据我所知,你获得的值在解析器中非常孤立。另一个想法是创建一个方法,在最后将所有内容转换为不可变版本,或者如果解析器代码最终将所有数据存储在其他位置,只需将其保留为可变。

另一种方法是使抽象类不是抽象的,而是实际上使它成为一个案例类。您可以从案例类派生类(但是从案例类派生案例类是禁止的,但是)。然后,诀窍是将您可能需要保存的变量数据保存在字段中:

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))