I want to design a Scala program that accepts Scala files as parameters which can customize the execution of the program. In particular, I want to supply at runtime files that contain implementations of methods that will be invoked by the program. How can I properly depend on external files and invoke their methods dynamically at runtime? Ideally, I would also like those files to be able to depend on methods and classes in my program.
Example Scenario: I have a function that contains the line val p: Plant = Greenhouse.getPlant(), and the Greenhouse class with the getPlant method is defined in one of the files that will be supplied at runtime. In that file, the method getPlant returns a Rose, where Rose <: Plant and Plant is defined in the original program. How do I achieve (or approximate) this interdependency, assuming the files are only joined at runtime and not at compile-time?
Here's how to do it using only standard Scala. The non-obvious stuff is all in
GreenhouseFactory:Put your override expression in
external.scala:The output is:
The only tricky thing is that
GreenhouseFactoryneeds to prepend thatimportstatement to provide access to all the types and symbols needed by the external files. To make that easy, make a single package with all those things.The compiler
ToolBoxis sort of documented here. The only thing you really need to know, other than the weird imports, is thattoolbox.parseconverts a string (Scala source code) into an abstract syntax tree, andtoolbox.compileconverts the abstract syntax tree into a function with signature() => Any. Since this is dynamically compiled code, you have to live with casting theAnyto the type that you expect.