2

This is easier to explain in code. I want to do something like:

import scala.reflect.runtime.currentMirror
import scala.tools.reflect.ToolBox
val toolbox = currentMirror.mkToolBox()

val universe: scala.reflect.runtime.universe.type = scala.reflect.runtime.universe
import universe._

class A { def a = "hello A" }

val c = toolbox.compile(q"""class C(x: Int) extends A { def r = x }""")

Note how the dynamically generated class C inherits from a class A that's known/already-compiled.

  1. Is there some way to use a toolbox that knows about A?
  2. How do I use a dynamically generated class?
devth
  • 2,738
  • 4
  • 31
  • 51

1 Answers1

3

Here is an example. The "pasted" class is loaded by the REPL's class loader, so you can supply it explicitly. You can also use tb.eval.

$ scala
Welcome to Scala version 2.11.7 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_60).
Type in expressions to have them evaluated.
Type :help for more information.

scala> import reflect.runtime._,universe._,tools.reflect.ToolBox
import reflect.runtime._
import universe._
import tools.reflect.ToolBox

scala> :pa -raw
// Entering paste mode (ctrl-D to finish)

package p { class Parent(val i: Int) }

// Exiting paste mode, now interpreting.


scala> val tb = runtimeMirror($intp.classLoader).mkToolBox()
tb: scala.tools.reflect.ToolBox[reflect.runtime.universe.type] = scala.tools.reflect.ToolBoxFactory$ToolBoxImpl@5e316c74

scala> tb.compile(tb.parse("""case class Child(j: Int) extends p.Parent(42) ; val c = Child(17) ; c.j"""))()
res0: Any = 17

scala> val tb = currentMirror.mkToolBox()
tb: scala.tools.reflect.ToolBox[reflect.runtime.universe.type] = scala.tools.reflect.ToolBoxFactory$ToolBoxImpl@59a67c3a

scala> tb.compile(tb.parse("""case class Child(j: Int) extends p.Parent(42) ; val c = Child(17) ; c.j"""))()
res1: Any = 17

There is surely an example in the docs somewhere or on this site.

som-snytt
  • 39,429
  • 2
  • 47
  • 129
  • Nice. Is it possible to use `Child` outside the toolbox? e.g. pass it to a method back in normal Scala-land. – devth Dec 01 '15 at 00:12
  • 1
    There is no normal Scalaland, only the multiverse. You can return an arbitrary value from tb.eval, an instance of `Child`, `classOf[Child]`, etc. `tb.define` gives you a symbol for a top-level class that you can pass back to toolbox (normally classes are local classes). http://stackoverflow.com/q/12122939/1296806 similarly http://stackoverflow.com/q/12122939/1296806 – som-snytt Dec 01 '15 at 07:44
  • Apparently this code doesn't work outside the repl as `$intp` is a magic repl-ony value. Is there an equivalent that I can compile? – devth Dec 02 '15 at 13:55
  • That was just to show that mirrors have a defining class loader. `currentMirror` does the work of picking the class loader. – som-snytt Dec 02 '15 at 17:47
  • Problem is I can't get it to work outside the REPL. Using `currentMirror` I get "not found: type Parent". – devth Dec 02 '15 at 18:27
  • Ah, figured out the problem! I was trying to use a top-level classname without the package. For some reason unknown to me Scala doesn't like that. – devth Dec 02 '15 at 18:32
  • Yeah, I just tried it and it just worked. And I just tried omitting the p in p.Parent and it still works. But I'm glad it works for you, too. – som-snytt Dec 02 '15 at 18:43
  • Thanks for your help! I posted a related followup question here if you care to gander: http://stackoverflow.com/questions/34053796/performance-and-caching-of-generated-code-in-scala – devth Dec 02 '15 at 21:54