Varargs matching for empty collections

I’m porting old code to new versions of Scala and have a varargs match that no longer compiles. The closest I come to the old version is this new version, but it does not have the exact same behavior when there is no text, and in fact the compiler seems to warn me about it with

A repeated case parameter or extracted sequence is not matched by a sequence wildcard (_*), and may fail at runtime.

The old version will match all of

<sub>{"text"}</sub>
<sub>{" "}</sub>
<sub></sub>

but the new version does not match the last one. Is there a new syntax that will do this, one that may still compile under old Scala versions? I can add an extra case <sub></sub> => to catch it, but would rather not.

  def transform(n: Node): String = n match {
    // case <sub>{text @ _*}</sub> => // old version
    case <sub>{Seq(text @ _*)}</sub> => // new version
      text.text
    case _ => "non-match"
  }

Hello,
I’m having trouble replicating your example, what version of Scala and which libraries are you using ?

In general I would advise against using XML litterals, as they are slowly being deprecated.

There are now string interpolators which are a more general way of doing these kinds of embedded language operations:

They also do support pattern matching (but this is only documented in its SIP …)

And in particular there are these library-defined string interpolators for XML (which I have not tested, and am not sure are still maintained):

(see also Scala 3 xml string interpolator)

Personally I think more should be done to ensure we have a suitable replacement to XML literals

In addition to the range of Scala versions you are trying to target, it’s especially important to tell us precisely what version of scala-xml is on your classpath, as the semantics of pattern matches could conceivably be affected by changes such as the one at the top of the scala-xml 2.4.0 release notes: Release 2.4.0 · scala/scala-xml · GitHub

That is a Scala 2 warning, so more info about versions and how you’re building would be needed to help, as already mentioned. Hopefully it’s just a stale dependency.

1 Like

Here’s a Scala 2.12 version for scala-cli

//> using jvm 8
//> using scala 2.12.21
//> using dep org.scala-lang.modules::scala-xml:1.3.1

import scala.xml.Node

def transform(n: Node): Unit = {
  val transformed = n match {
    case <sub>{text @ _*}</sub> => text.text // old version
    case _ => "non-match"
  }
  println(s"""The result is "$transformed".""")
}

transform(<sub>{"text"}</sub>)
transform(<sub>{" "}</sub>)
transform(<sub>{""}</sub>)
transform(<sub></sub>)
transform(<sub/>)

and here’s the Scala 3.3 version for which transform also works for Scala 2, both needing the commented out extra line to match original output

//> using jvm 8
//> using scala 3.3.7
//> using dep org.scala-lang.modules::scala-xml:2.4.0

import scala.xml.Node

def transform(n: Node): Unit = {
  val transformed = n match {
    // case <sub>{text @ _*}</sub> => text.text // does not compile
    case <sub>{Seq(text @ _*)}</sub> => text.text // new version
    // case <sub/> => "" // extra line required
    case _ => "non-match"
  }
  println(s"""The result is "$transformed".""")
}

transform(<sub>{"text"}</sub>)
transform(<sub>{" "}</sub>)
transform(<sub>{""}</sub>)
transform(<sub></sub>)
transform(<sub/>)

So I wonder if there is any way to get the new version to work without including the extra line with the additional case.

1 Like

I think Seq(text @ _*) is equivalent to text: Seq, have you tried that ?

case <sub>{text: Seq[_]}</sub> => text.text

results in non-matches as well. Perhaps there are changes between the XML library versions 1.3.1 and 2.4.0 that result in the failed match.