Добавление метки к неизменяемым классам падежей в Scala - PullRequest
1 голос
/ 11 августа 2010

Я пытаюсь создать синтаксический анализатор для небольшого языка с командами, включающими метки и 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 и т. Д. - это классы падежей, расширяющие абстрактный класс 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 Ответ

4 голосов
/ 11 августа 2010

Вполне возможно создать свой собственный метод копирования:

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)

Но, прагматично, я бы не стал слишком быстро отказываться от использования переменных здесь. Про неизменные значения проще рассуждать, но те, которые вы получаете, достаточно хорошо изолированы внутри синтаксического анализатора, насколько я могу судить. Альтернативной идеей может быть создание метода, который конвертирует все в неизменяемую версию в конце, или если код синтаксического анализатора в конечном итоге хранит все данные где-либо еще, просто оставляя его изменяемым.

Еще один способ - сделать абстрактный класс не абстрактным, а сделать его классом 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))
...