Skip to content

Latest commit

 

History

History
99 lines (89 loc) · 2.75 KB

10.markdown

File metadata and controls

99 lines (89 loc) · 2.75 KB

Pattern Matching Pitfalls

Java methods have a finite size, and the Scala compiler generates very large methods for complex pattern matching. It can be surprisingly easy to hit Java's limit and produce and exception in compilation. There is an open bug to address the problem in Scala.

There are several techniques for avoiding this limitation when writing Unfiltered applications. One is to restructure matches so that common cases are handled in one case, and differentiated in subsequent matches. Instead of this:

case ConditionA(Condition1(req)) => ...
case ConditionA(Condition2(req)) => ...

You might write:

case ConditionA(req) => req match {
  case Condition1(_) => ...
  case Condition2(_) => ...
  case _ => Pass
}

Another technique is to break up your cases into logical, named groups. These can be combined into a single intent.

val foos = unfiltered.filter.Intent {
  case ConditionA(Condition1(req)) => ...
  case ConditionA(Condition2(req)) => ...
}
val bars = unfiltered.filter.Intent {
  case ConditionB(Condition1(req)) => ...
}
val fooBar = unfiltered.filter.Planify(
  foos.onPass(bars)
)

For larger groupings of functionality, it may be useful to define singleton objects holding logically divided intents.

object Foos {
  import fun.stuff.into.scope._
  def intent = unfiltered.filter.Intent {
    case ConditionA(Condition1(req)) => ...
    case ConditionA(Condition2(req)) => ...
  }
}
object Bars {
  def intent = unfiltered.filter.Intent {
    case ConditionB(Condition1(req)) => ...
  }
}
object FooBar extends unfiltered.filter.Plan {
  def intent = Foos.intent.onPass(Bars.intent)
}

You could also make use of separate plans, to be chained together by the server.

object Foos extends unfiltered.filter.Plan {
  import fun.stuff.into.scope._
  def intent = {
    case ConditionA(Condition1(req)) => ...
    case ConditionA(Condition2(req)) => ...
  }
}
object Bars extends unfiltered.filter.Plan {
  def intent = {
    case ConditionB(Condition1(req)) => ...
  }
}

And lastly, if there is no logical way to divide up a partial function that is large enough to cause a compilation error, you can always split it arbitrarily with default cases:

def intent = {
  case ConditionA(Condition1(req)) => ...
  case ConditionA(Condition2(req)) => ...
  case req => req match => {
    case ConditionB(Condition1(req)) => ...
    case _ => Pass
  }
}

Be aware that this last intent is defined for all requests, so you must use onPass if you wish to chain another intent after it. Using the orElse method of such a PartialFunction would only produce a slower function that is otherwise identical to its parent.