Syntax is one issue with this idea, but another is efficiency. Suppose we use the syntax with {}
. Then we could use Regex like this:
"Foobar" match
case {"Foo(.*)".r}(x) => x
// evaluates to "bar"
The problem is that the Regex pattern needs to be compiled, which is inefficient and shouldn’t be done every time the match is executed. So the compiler should hoist that expression out and compile it to something like this:
// on package level
val Pat = "Foo(.*)".r
// whereever the match is:
"Foobar" match
case Pat(x) => x
But then, the expression inside the {}
could be using symbols (val
s, def
s etc.) from an excluding scope, so it’s not always possible to hoist it to the package level.
We could hoist it to the outermost scope that’s possible given the symbols that are being used. For example, given an extractor like this:
class Surround(prefix: String, suffix: String):
def unapply(s: String): Option[String] =
Option.when(s.startsWith(prefix) && s.endsWith(suffix)):
s.stripPrefix(prefix).stripSuffix(suffix)
Hoisting would happen at different levels for different examples.
Example 1:
def a(pre: String) =
def b(suf: String) =
"Foobar" match
case {Surround(pre, suf)}(x) => x
// translates to
def a(pre: String) =
def b(suf: String) =
val Pat = Surround(pre, suf) // hoisted to `b` because `suf` is local to `b`
"Foobar" match
case Pat(x) => x
Example 2:
def a(pre: String) =
def b =
"Foobar" match
case {Surround(pre, "bar"}(x) => x
// translates to
def a(pre: String) =
val Pat = Surround(pre, "bar") // hoisted to `a` because no variables in `b` are used
def b =
"Foobar" match
case Pat(x) => x
This is no doubt possible, but I don’t know if it’s a good or a bad idea. On the one hand it’s the most flexible: you can simply use surrounding variables as you would expect and it would work. On the other hand, it’s a performance pitfall: seemingly minor changes to the code could lead to different hoisting and thus wildly different performance characteristics.
I’d like to hear the community’s thoughts about this.