Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can't put PartialFunction in scala class constructor

There appears to be a restriction that you can't use PartialFunction literals in class constructors:

scala> case class X(a: PartialFunction[Any, Any]) { def this() = this({case x => x}) }
<console>:7: error: Implementation restriction: <$anon: Any => Any> requires premature access to class X.
   case class X(a: PartialFunction[Any, Any]) { def this() = this({ case x => x}) }

My first question is why does a partial function literal need access to "this". My second question/observation is that in the Scala REPL, running the same code again crashes the REPL:

scala> case class X(a: PartialFunction[Any, Any]) { def this() = this({ case x => x}) }
java.lang.NullPointerException
    at scala.tools.nsc.Global$Run.compileLate(Global.scala:1595)
    at scala.tools.nsc.GlobalSymbolLoaders.compileLate(GlobalSymbolLoaders.scala:29)
    at scala.tools.nsc.symtab.SymbolLoaders$SourcefileLoader.doComplete(SymbolLoaders.scala:369)
    ...

And lastly, is there a good workaround for this issue?

like image 611
Yona Appletree Avatar asked Jun 13 '15 20:06

Yona Appletree


2 Answers

Your first question is answered in the comment section of this question

Quoting Imm:

Anonymous classes have access to their enclosing class. The compiler doesn't know that your anonymous partial function doesn't actually access anything (and it would be very hard to check this in full generality); it simply disallows creating any anonymous classes until you're into the class proper.

Why it crashes the REPL is a good question, you should probably submit a ticket to Typesafe with this code example.

A workaround is quite simple, just define the anonymous function outside of the class so the compiler knows the exact state you are closing over:

object X {
  val Default: PartialFunction[Any, Any] = { case x => x }
}

case class X(a: PartialFunction[Any, Any]) {
  def this() = this(X.Default)
}
like image 97
Akos Krivachy Avatar answered Sep 28 '22 01:09

Akos Krivachy


Your first question is answered in the comment section of this question.

The issue is that the compiler is too restrictive because it didn't know how to lift stuff out of the constructor parameter block.

The workaround is given in the other answer, which is what the compiler should be doing with that other stuff.

The other workaround is manual construction:

case class X(a: PartialFunction[Any, Any]) { def this() = this({
    class $anonfun extends runtime.AbstractPartialFunction[Any, Any] {
      override def applyOrElse[A, B >: Any](x: A, default: A => B): B = x match {
        case x => x
        //case _ => default(x) // completeness
      }
      override def isDefinedAt(x: Any) = x match {
        case x => true
        //case _ => false
      }
    }
    new $anonfun()
  })
}

The answer to your second question is that it is SI-9170, fixed for 2.11.7.

like image 30
som-snytt Avatar answered Sep 27 '22 23:09

som-snytt