Scala layered pattern matching

I am sticking with multilevel pattern matching, in the code below I want to match one specific case that is checked at multiple levels: "cfe - Assignment, assignCfe.getRight - BinaryExpression, etc.", the solution looks ugly and I hope there is something better Scala has to offer me. :)

  def findAll(cfg: Cfg, consumer: Consumer[Violation]): Unit = {
    val fa = new FlowAnalyzer
    val states = fa.analyze(cfg)

    states.foreach { case (cfe, state) => cfe match {
      case assign: Assignment => assign.getRight match {
        case expression: BinaryExpression => expression.getOperator match {
          case Operator.Div | Operator.Rem => processDivisions()
          case _ =>
        }
        case _ =>
      }
      case _ =>
    }
    case _ =>
    }
  }

      

How do I get rid of those empty default cases at the end?

Another approach would be to use nested conditions, but IntelliJ IDEA suggests I replace those conditions with pattern matching

states.foreach { case (cfe, state) => if (cfe.isInstanceOf[Assignment]) {
      val assignment = cfe.asInstanceOf[Assignment]
      if (assignment.getRight.isInstanceOf[BinaryExpression]) {
        val expression = assignment.getRight.asInstanceOf[BinaryExpression]
        if (expression.getOperator == Operator.Div || expression.getOperator == Operator.Rem) processDivisions()
      }
    }}

      

+3


source to share


3 answers


Are Assignment

both BinaryExpression

case classes? Or do they have appropriate failing methods? If so, then you can insert pattern matches and ignore fields that you don't need. For example, something like:

def findAll(cfg: Cfg, consumer: Consumer[Violation]): Unit = {
  val fa = new FlowAnalyzer
  val states = fa.analyze(cfg)

  states.foreach {
    case (Assignment(_, BinaryExpression(_, _, Operator.Div | Operator.Rem)), _) => processDivisions()
    case _ =>
  }
}

      



This will reduce the default matches to 1.

If these are not case classes or you don't have extractors, you might consider writing your own if this is a common pattern (anti) in your code: http://docs.scala-lang.org/tutorials/tour/extractor -objects.html

+3


source


Another idea is that you can use the "pimp my library" pattern to define an implicit conversion of any object to a class that can do partial matching:

class PartialMatcher[A](a: A) {
  def partialMatch(f: PartialFunction[A, Unit]): Unit = if (f.isDefinedAt(a)) f(a)
}
implicit def makePartialMatcher[A](a: A) = new PartialMatcher(a)

      

Then just replace all those matches with partialMatch

:



  def findAll(cfg: Cfg, consumer: Consumer[Violation]): Unit = {
    val fa = new FlowAnalyzer
    val states = fa.analyze(cfg)

    states.foreach { case (cfe, state) => cfe partialMatch {
      case assign: Assignment => assign.getRight partialMatch {
        case expression: BinaryExpression => expression.getOperator partialMatch {
          case Operator.Div | Operator.Rem => processDivisions()
        }
      }
    }}
  }

      

Note that there are other reasons why you can avoid this kind of thing ... overusing implicit conversions can make the code much more difficult to understand. This is a stylistic choice.

0


source


Use .collect

:

def findAll(cfg: Cfg, consumer: Consumer[Violation]): Unit = {
  val fa = new FlowAnalyzer
  val states = fa.analyze(cfg)
  states.collect { case (assign: Assignment, _) => 
    assign.getRight
  }.collect { case expression: BinaryExpression => 
    expression.getOperator
  }.collect { case Operator.Div | Operator.Rem => 
    processDivisions
  }

      

0


source







All Articles