4

I could not find an answer to this simple question, maybe I used the wrong keywords to search.

To create an AST I need nodes such as Number, Add, Sub, Mul, Div and so forth. Since many math operations share the same structure, how can I treat them all under the same pattern matching case? E.g. the lines below are said to be not syntactically correct:

object AST {
  sealed abstract class Expr
  case class MathOp(e1: Expr, e2: Expr) extends Expr
  case class Number extends Expr

  case class Add(e1: Expr, e2: Expr) extends MathOp(e1, e2)
  case class Sub(e1: Expr, e2: Expr) extends MathOp(e1, e2)
}

The intention is to be able to do:

expr match {
  case MathOp(e1: Expr, e2: Expr) => //do something that would be done to Add, Sub, Mul, Div
  case Number => //do another thing
}
  • Case classes and inheritance are like oil and soap; they don't mix well. Combine them at your own risk. – cmbaxter Jun 19 '13 at 10:47

2 Answers2

5

case classes do much more than adding pattern matching extractors, e.g. they add equality, product iterator and arity, etc., and so odd things happen under inheritance. Therefore, case class inheritance was deprecated before and is now impossible in Scala 2.10.

For your case, you want a custom extractor (unapply method):

object AST {
  sealed trait Expr
  object MathOp {
    def unapply(m: MathOp): Option[(Expr, Expr)] = Some(m.e1 -> m.e2)
  }
  sealed trait MathOp extends Expr {
    def e1: Expr
    def e2: Expr
  }
  case class Number extends Expr

  case class Add(e1: Expr, e2: Expr) extends MathOp
  case class Sub(e1: Expr, e2: Expr) extends MathOp
}

Related question

Community
  • 1
  • 1
0__
  • 66,707
  • 21
  • 171
  • 266
  • I have seen this workaround before (it is probably the official one), but IMO when one uses case classes, at least to pattern matching, one is looking for simplicity. But ok, I can delimit an untouchable area inside the AST object to keep the `traits` and `unapplies` or find some way to encapsulate/'implicitate' them. –  Jun 21 '13 at 08:14
0

As far as I could understand the problem, I have no reason to use "case classes" for the children. Hopefully the correct code is as follows:

object AST {
  sealed abstract class Expr
  case class MathOp(e1: Expr, e2: Expr) extends Expr
  case class Number extends Expr

  class Add(e1: Expr, e2: Expr) extends MathOp(e1, e2)
  class Sub(e1: Expr, e2: Expr) extends MathOp(e1, e2)
}

expr match {
  case MathOp(e1: Expr, e2: Expr) => //do something that would be done to Add, Sub, Mul, Div
  case Number => //do another thing
}
  • Actually, I will need to pattern match the sub cases later... Any help? –  Jun 19 '13 at 09:53
  • Back to the `case classes` and `override val` the args. –  Jun 19 '13 at 09:56
  • 1
    Case-to-case inheritance is prohibited. Great... What is a non-leaf extractor? –  Jun 19 '13 at 10:14