代码之家  ›  专栏  ›  技术社区  ›  svrist

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

  •  1
  • svrist  · 技术社区  · 15 年前

    我正在尝试为一种小型语言创建一个解析器,其中包含命令labels和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>
    ...
    

    GOTO , IFCMD etc是扩展抽象类的case类 Core

    按照函数式/类scala/不可变对象的方式,我认为 就像这样 :

    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)) 但是抽象基类没有自动复制构造函数(magic)

    1 回复  |  直到 15 年前
        1
  •  4
  •   Rex Kerr    15 年前

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

    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的使用。对不可变的值进行推理比较容易,但据我所知,您得到的值在解析器中是非常孤立的。另一种想法是创建一个方法,在最后将所有内容转换为不可变的版本,或者如果解析器代码最终将所有数据都存储在其他地方,只留下可变的数据。

    另一种方法是使抽象类不是抽象的,而是实际地使其成为case类。您可以从case类派生类(但是,从case类派生case类是不允许的)。这样做的诀窍是将可能需要保存的变量数据保存在字段中:

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