Support `@switch` for type-based pattern matches

Java 17 came with a preview of JEP 406. This makes it possible to pattern match based on the type of the scrutinee expression. Looking at the javap -c output of these new type-based switches, I see that they work using a new indy typeSwitch bootstrap method followed by a tableswitch. I suspect this is more efficient than the nested if strategy used by the Scala compiler.

Given that type-based match is quite a bit more widespread in Scala (especially on sealed types), is there any interest/proposal for adopting the (presumably more efficient) compilation strategy that Java uses? Obviously, we’d need to create our own bootstrap method, but I don’t see any reason this wouldn’t be possible…

7 Likes

It could be the case that the bootstrap method actually is generating a while loop underneath that checks the cases
e.g.

static String formatterPatternSwitch(Object o) {
    return switch (o) {
        case Integer i -> String.format("int %d", i);
        case Long l    -> String.format("long %d", l);
        case Double d  -> String.format("double %f", d);
        case String s  -> String.format("String %s", s);
        default        -> o.toString();
    };
}

gets translated to the equivalent of

def lookup(obj: Any, cases: Array[Class[_]]): Int = {
  val dynCls = obj.getClass
  var i = 0
  while (i < cases.length) {
    if (cases(i).isAssignableFrom(dynCls))
      return i
    i += 1
  }
  return cases.length
}
lookup(foo, Array(classOf[jl.Integer], classOf[jl.Long], classOf[jl.Double], classOf[jl.String])) match {
  case 0 => String.format("int %d", foo.asInstanceOf[jl.Integer])
  case 1 => String.format("long %d", foo.asInstanceOf[jl.Long])
  case 2 => String.format("double %f", foo.asInstanceOf[jl.Double])
  case 3 => String.format("String %s", foo.asInstanceOf[jl.String])
  case _ => foo.toString
}

but maybe that could be made an intrinsic of the vm

I think the current implementation does effectively translate into a while loop, but that’s a hopefully temporary implementation detail. This seems quite similar to the whole indy-fing of string concatenation: by moving the code for handling a high-level construct (match in this case) to the runtime you get

  • ability to improve the bootstrap method in future Scala releases without breaking binary compatibility
  • shorter (due to less boilerplate) bytecode

@dwijnand benchmarked various approaches to encoding class-based matches earlier this year: https://github.com/scala/scala/pull/9632