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.