Can't put PartialFunction in scala class constructor
Asked Answered
M

2

7

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?

Miltie answered 13/6, 2015 at 20:46 Comment(1)
See SI-8593. Also related: groups.google.com/forum/#!topic/scala-user/V085Tg0mKcUMatlick
A
7

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)
}
Archlute answered 13/6, 2015 at 20:58 Comment(0)
I
1

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.

Idioblast answered 14/6, 2015 at 0:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.