Can scala compiler do the micro optimization for simple pattern matching?

I resently submited a change to pekko, which changes from pattern matching to eq for performance reason, which generates less bytecode, but very annoying.

Can Scala compiler do this for us and then no one need to write it. And you can find this kind of trick in Scala stdlib too:

[1] chore: Micro optimization for `collect*` operator. by He-Pin · Pull Request #983 · apache/incubator-pekko · GitHub

The spec requries calling equals for case X patterns (Pattern Matching | Scala 3.4).

A static analyis could in principle identify cases where X is private and does not override equals. But the type system is not enough for that, X could have static type Object but be initialized with a class that does have an override.

So I don’t see a good way of implementing this optimization.

1 Like

Could this be feasible as a method on PartialFunction, or would allocating an Option erase the gains?

trait PartialFunction[-A, +B] extends (A => B) { self =>
  def applyOrNone[A1 <: A](a: A1): Option[B] = {
    val r = applyOrElse(a, PartialFunction.ApplyOrNoneSentinel)
    if (r.asInstanceOf[AnyRef] ne ApplyOrNoneSentinel) Some(r.asInstanceOf[B]) 
    else None
  }
}
object PartialFunction {
  private val ApplyOrNoneSentinel: scala.Function1[Any, Any] = (a: Any) => this
}
1 Like

The idiom to use reference eq is to match the singleton type:

scala> def f[A](xs: List[A]) = xs match { case _: Nil.type => 0 case _ => 1 }
def f[A](xs: List[A]): Int

scala> :javap #f
  public <A extends java.lang.Object> int f(scala.collection.immutable.List<A>);
    descriptor: (Lscala/collection/immutable/List;)I
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=2, locals=2, args_size=2
         0: aload_1
         1: getstatic     #22                 // Field scala/collection/immutable/Nil$.MODULE$:Lscala/collection/immutable/Nil$;
         4: if_acmpne     9
         7: iconst_0
         8: ireturn
         9: iconst_1
        10: ireturn
      StackMapTable: number_of_entries = 1
        frame_type = 9 /* same */
      LineNumberTable:
        line 1: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      11     0  this   L$line8/$read$$iw;
            0      11     1    xs   Lscala/collection/immutable/List;
    Signature: #15                          // <A:Ljava/lang/Object;>(Lscala/collection/immutable/List<TA;>;)I
    MethodParameters:
      Name                           Flags
      xs                             final

scala>

in the linked case,

       pf.applyOrElse(grab(in), NotApplied) match {
          case _: NotApplied.type             => completeStage()

That is per spec, and not an implementation detail.

4 Likes

Thanks, a follow up in ↓
[1] chore: Make use of pattern matching on singleton type instead. by He-Pin · Pull Request #1026 · apache/incubator-pekko · GitHub