Thomas Matecki Thomas Matecki - 2 months ago 9
Scala Question

Dynamic Stable identifiers in Scala Pattern Match

Using Scala, is there any way to dynamically construct a list patterns to be pattern matched against?

For example, suppose I'm using stable identifiers to parse a list of Strings, like this:

def matchingAndDispatch(xs: List[String])= {
case `namespace` :: value :: `elementTerminator` :: rest => {
// Do Something...
}
case `openBracket` :: rest => {
// Do Something Else...
}
case `closeBracket` :: `elementTerminator` :: rest => {
// Or perhaps something else...
}
}


Now, suppose there are going to be a lot of case clauses and I wanted the ability to store them in a collection of some sort that could be changed at runtime - not necessarily the patterns themselves, but the collection of patterns could be changed. I've made up the imaginary class MatchClause in the code below to explain more or less what I have in mind - basically traverse a collection of pattern(i.e. Match Clauses) and match one at a time:

def matchingAndDispatch(xs: List[String], matchingClauses:List[MatchClause])= {
if(!matchingClauses.empty){
case matchingClauses.head => {
// Do Something...
}
case _ => matchingAndDispatch(xs, matchingClause.tail)
}
}else throw new Error("no match")


Is there anything in the Scala API that would serve this purpose? I haven't found anything. Or perhaps I'm going about this the wrong way?

Answer
val `namespace` = "namespace"
val `elementTerminator` = "elementTerminator"
val `openBracket` = "openBracket"
val `closeBracket` = "closeBracket"

// list of partial functions from list of strings to string:    
val patterns = List[PartialFunction[List[String], String]](
  { case `namespace` :: value :: `elementTerminator` :: rest => "case1" },
  { case `openBracket` :: rest => "case2" },
  { case `closeBracket` :: `elementTerminator` :: rest => "case3" })

def matchingAndDispatch(xs: List[String], patterns: List[PartialFunction[List[String], String]]): String = {
  patterns.find(_.isDefinedAt(xs)).map(_(xs)).getOrElse("unknown")
}

Test:

matchingAndDispatch(List("namespace", "somevalue", "elementTerminator"), patterns)
> case1

matchingAndDispatch(List("namespace", "somevalue", "elementTerminator", "more"), patterns)
> case1

matchingAndDispatch(List("namespace", "somevalue", "not_terminator", "more"), patterns)
> unknown
Comments