Kigyo
Kigyo

Reputation: 5768

Improving Pattern-matching Code

Assume the following data-structure.

sealed abstract class Formula {...}

//... some other case classes

sealed abstract class BinaryConnective(f0: Formula, f1: Formula) extends Formula {
  def getf0 = f0
  def getf1 = f1
}

object BinaryConnective {
  def unapply(bc : BinaryConnective) = Some((bc.getf0, bc.getf1))
}

final case class Conjunction(f0: Formula, f1: Formula) extends BinaryConnective(f0,f1)
final case class Disjunction(f0: Formula, f1: Formula) extends BinaryConnective(f0,f1)
final case class Implication(f0: Formula, f1: Formula) extends BinaryConnective(f0,f1)
final case class Equivalence(f0: Formula, f1: Formula) extends BinaryConnective(f0,f1)

I now wrote a function that has a lot of pattern-matching:

The return-type of getCondition is Formula => Option[HashMap[Variable, Formula]]

formula match {
//.. irrelevant cases not shown
case Conjunction(f0, f1) => (g : Formula) => {
    g match {
      case conj @ Conjunction(g0, g1) => {
        getCondition(f0)(conj.f0) match {
          case Some(map0) => {
            getCondition(f1)(conj.f1) match {
             case Some(map1) if map0.forall{case (key, value) => map1.get(key).map(_ == value).getOrElse(true)} => {
               Some(map0 ++ map1)
             }
             case _ => None
            }
          }
          case None => None
        }
      }
      case _ => None
    }
  }
}

Now to my question.

1) Is there a nicer way to express this code? A lot of matches going on.

Edit 1: I could not think of a nice-looking way to use things like map, filter etc.., but it seems very compact with for-comprehensions. I've also noticed that conj @ was not necessary at all, which also made it a little simpler.

case Conjunction(f0, f1) => (g: Formula) => g match {
  case Conjunction(g0, g1) => for {
      map0 <- getCondition(f0)(g0)
      map1 <- getCondition(f1)(g1)
      if map0.forall {case (key, value) => map1.get(key).map(_ ==  value).getOrElse(true)}
    } yield map0 ++ map1
  case _ => None
}

2) This is the match for Conjunction. I would have to repeat it for Disjunction, Implication and Equivalence. g has to be of the same class as formula. The only thing that would change is case conj @ Conjunction(g0, g1). I would have to adjust it to case disj @ Disjunction(g0, g1) if formula is a Disjunction etc... Is there a way to do it combined for all cases?

Upvotes: 0

Views: 80

Answers (1)

vptheron
vptheron

Reputation: 7466

Option should provide a lot of useful functions to simplify your code.

For example, when you write something like:

o match {
  case Some(e) => Some(transform(e))
  case _ => None
}

You could just call map: o.map(transform)

I also invite you to look at the filter function for the cases including a condition.

EDIT: great suggestion by @om-nom-nom: For comprehensions can also be used (they actually are sugar relying on map, flatMap, filter, etc):

for{
  e <- o
} yield transform(e)

Upvotes: 2

Related Questions