tags:

views:

67

answers:

2

I'm missing some fairly simple syntax I gather. I'm trying to rewrite an element label to something else and keep everything else intact.

object htmlRule extends RewriteRule {
 override def transform(n: Node): Seq[Node] = n match {
   case Elem(prefix, "document", attribs, scope, child@_*)  =>
     Elem(prefix, "html", attribs, scope, child)
   case other => other
 }
}

Now, I ask for an explanation of two things:

1) What exactly does "child@_*" mean in plain English?

2) How can I capture the value of "child@_*" and just let it pass right through to the new element? Currently, I get the following error, which makes sense.

[error]  found   : Seq[scala.xml.Node]
[error]  required: scala.xml.Node
[error]       Elem(prefix, "html", attribs, scope, child)

I'm not wedded to this either, so if there's a better way to simply change the element name of a specific node, let's here it...

Thanks, --tim

+3  A: 

The notation:

case ... bindVar @ patternConstraint ... => /* bindVar is bound to what patternConstraint matched here */

Allows you to match "within" a value (the patternConstraint part) while capturing the overall value subjected to that constraint (as bindVar).

In your particular case, child @ _*, the _ means "don't care", the * means a sequence of values and child @ means bind child to the entire sequence.

A frequent use of this capability is a nested pattern (usually mentioning case classes):

case expr @ Expr(op, lhs, rhs) => // Do stuff with expr, op, lhs and rhs

Here, the target of the match is tested to see if it is an instance of Expr and if it is, op is bound to the Expr's operator, lhs and rhs are bound to its left- and right-hand sides, resp. and expr is bound to the Expr instance itself.

Randall Schulz
Thanks Randall,Can you explain the weirdness that it matches an Elem with a node sequence _* but yet, the Elem constructor doesn't take a node sequence? First, is that correct? Second, if so, what's the way to achieve my desired behavior? Thanks for you detailed binding explanation:)
williamstw
Use child:_* to apply the Seq[Node] as the Node* vararg parameter.
Silvio Bierman
Thanks Silvio, I'm still unsure exactly why this magical syntax works but it does. Clearly I have a whole new area of Scala learning to do:)
williamstw
A: 

Elem is not a case class, it is an "ordinary" class with a companion object. There is a way to write ordinary (non-case) classes to simulate case classes. But more typically one views case classes as a shorthand for writing a bunch of boilerplate code, some of it in the class and some of it in an implicitly created companion. In this case, the pertinent bits are the unapply and / or unapplySeq methods in the companion object. These methods are what make non-case classes amenable to pattern matching.

For Elem the pertinent matching "signature" is that of object scala.xml.Elem#unapplySeq(...), specifically:

def unapplySeq(n: Node): Option[(String, String, MetaData, NamespaceBinding, Seq[Node])]

There you can see the pattern of bindings created when matching with Elem. They do in fact correspond to the pattern you're using in your sample code.

Randall Schulz