简体   繁体   中英

Scala: extend subset of case class hierarchy with trait

Consider the following case class hierarchy, used to model context-free grammar rules.

case class Rule(head: Nonterminal, body: Expression)

trait BNF

sealed abstract class Expression

// Bracketed expression.
abstract class BracketedExpr(expr: Expression) extends Expression

case class Group(expr: Expression) extends BracketedExpr(expr)
case class Plus(expr: Expression) extends BracketedExpr(expr)
case class Opt(expr: Expression) extends BracketedExpr(expr)
case class Star(expr: Expression) extends BracketedExpr(expr)

// Concatenated.
case class Conc(list: List[Expression]) extends Expression

// Alternated.
case class Alt(list: List[Expression]) extends Expression

// Base expression.
abstract class Literal extends Expression with BNF

case class Nonterminal(value: String) extends Literal 
case class Terminal(value: String) extends Literal
case class Regex(value: String) extends Literal
case class Epsilon() extends Literal
case class EndOfInputSymbol() extends Literal

def fancyFunction(expr: Expression with BNF): ???

Now I want to specify that a Conc(atenation) and Alt(ernate) have the trait BNF if and only if all Expressions in list have the trait BNF. In this way, I would like to check upon method invocation whether all expressions in the sub hierarchy have the BNF trait.

How do I specify this?

The types should be known at compile time, but you can do runtime checks to see if your returned object is based on a particular trait.

Simple example based on your classes:

/** your classes here */

case class NullExpr() extends Expression

scala> def derive(l:List[Expression]) = l.fold(Epsilon()) {
     | (acc, elem) => if (!acc.isInstanceOf[BNF] || !elem.isInstanceOf[BNF]) 
     | NullExpr() else elem }
derive: (l: List[Expression])Expression

scala> derive(List(Epsilon(), Regex("abcd")))
res8: Expression = Regex(abcd)

scala> res8.isInstanceOf[BNF]
res9: Boolean = true

scala> derive(List(NullExpr(), Epsilon()))
res10: Expression = NullExpr()

scala> res10.isInstanceOf[BNF]
res11: Boolean = false

Now I want to specify that a Conc(atenation) and Alt(ernate) have the trait BNF if and only if all Expressions in list have the trait BNF.

It isn't possible for a single class to sometimes extend a trait and sometimes not. You could have separate classes Conc and ConcBNF , and similarly for Alt . Instead, I'd just do

sealed abstract class Expression {
  def isBNF: Boolean
}

abstract class Literal extends Expression {
  def isBNF = true
}

case class Conc(list: List[Expression]) extends Expression {
  def isBNF = list.all(_.isBNF) // or val isBNF if you want to avoid recalculation
}

// and so on

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM