PartialFunction syntax

I am a Scala newby. Trying to understand PartialFunction syntax.

As I understand it syntax A

 val f: PartialFunction[Int, Int] = {
  case x: Int if x != 0 => 100
    }

is the same as syntax B

    val f: PartialFunction[Int, Int] = {
      new PartialFunction[Int, Int]() {
        def apply(x: Int) = 100
        def isDefinedAt(x: Int) = x != 0
      }
    }

but this function contains only one case statement.

What if another case statement is added to syntax A, eg

 val f: PartialFunction[Int, Int] = {
  case 1 => 100
  case 2 => 200
  case x: Int if x != 0 => 300
    }

what would be the corresponding syntax B?

AFAIK, something like this:

 val f: PartialFunction[Int, Int] =
  new PartialFunction[Int, Int] {
    override def apply(x: Int): Int =
      x match {
        case 1 => 100
        case 2 => 200
        case _ if x != 0 => 300
    }

    override def isDefinedAt(x: Int): Int =
      x match {
        case 1 => true
        case 2 => true
        case _ if x != 0 => true
        case _ => false
    }

In any case, you should not care.

Just so. Keep in mind that the fully-spelled-out version is generated by the compiler; it is quite rare to bother doing so by hand. (Legal, and there are probably edge cases where it is appropriate, but I’m not sure I’ve done so in a dozen years of writing Scala.)

1 Like

What you really get is applyOrElse, which is what apply uses:

➜  ~ scala -Vprint:typer
Welcome to Scala 2.13.8 (OpenJDK 64-Bit Server VM, Java 17.0.1).
Type in expressions for evaluation. Or try :help.

scala>  val f: PartialFunction[Int, Int] = {
     |   case x: Int if x != 0 => 100
     |     }
[[syntax trees at end of                     typer]] // <console>
private[this] val f: PartialFunction[Int,Int] = ({
        @SerialVersionUID(value = 0) final <synthetic> class $anonfun extends scala.runtime.AbstractPartialFunction[Int,Int] with java.io.Serializable {
          def <init>(): <$anon: Int => Int> = {
            $anonfun.super.<init>();
            ()
          };
          final override def applyOrElse[A1 <: Int, B1 >: Int](x1: A1, default: A1 => B1): B1 = ((x1.asInstanceOf[Int]: Int): Int @unchecked) match {
            case (x @ (_: Int)) if x.!=(0) => 100
            case (defaultCase$ @ _) => default.apply(x1)
          };
          final def isDefinedAt(x1: Int): Boolean = ((x1.asInstanceOf[Int]: Int): Int @unchecked) match {
            case (x @ (_: Int)) if x.!=(0) => true
            case (defaultCase$ @ _) => false
          }
        };
        new $anonfun()
      }: PartialFunction[Int,Int]);

The purpose is to avoid double-evaluation of the pattern match:

if (pf.isDefinedAt(x)) pf.apply(x)

Here is the definition of PartialFunction.cond, which conceptually “supplies a default case” for a boolean test:

def cond[A](x: A)(pf: PartialFunction[A, Boolean]): Boolean = pf.applyOrElse(x, constFalse)
1 Like

wow… thx for your reply… this is mind-blowing… some questions in return…

  • where to find more info about scala command line flags? I would like to understand what ‘-Vprint:typer’ means and what else is possible…
  • why is the generated version of PartialFunction lacking the apply method? My best guess; the trait does not contain a method ‘apply’ because in some magical way the ‘apply’ method I stated in my code gets converted by the compiler?

I agree it’s at least a bit mind-bending.

For command options there is a page, though I normally just cargo cult or use the command line scalac -help and -X etc. I haven’t checked what scala-cli does yet, though I know Scala 3 doesn’t have all the bells and whistles, but an overlapping set.

The abstract class you see in the output has apply.