//I am trying to dynamically define , compile and access case classes that are //nested
//
package org.example
import scala.reflect.runtime.currentMirror
import scala.tools.reflect.ToolBox
object DynamicCompilation {
def compileCaseClasses(code: String): Option[ClassLoader] = {
try {
val tb = currentMirror.mkToolBox()
val compiled = tb.compile(tb.parse(code))
// Get class loader from the compiled object
val classLoader = compiled.getClass.getClassLoader
// Print the class loader information
println(s"Class loader being compiled is : $classLoader")
Some(classLoader)
} catch {
case e: Throwable =>
println(s"Compilation Error: ${e.getMessage}“)
e.printStackTrace()
None
}
}
def getClassByName(classLoader: ClassLoader, className: String): Option[Class[_]] = {
try {
Some(classLoader.loadClass(s"org.example.$className”)) // Assuming package org.example
} catch {
case e: ClassNotFoundException =>
println(s"Class org.example.$className not found: ${e.getMessage}")
None
}
}
def main(args: Array[String]): Unit = {
val code = “”"
{
case class Address(
city: Option[String],
country: Option[String],
street: Option[String]
)
case class Person(
address: Option[Address],
age: Option[Long],
name: Option[String]
)
case class Root(
people: Option[Seq[Person]]
)
}
“”"
compileCaseClasses(code) match {
case Some(classLoader) =>
getClassByName(classLoader, "org.example.Address") match {
case Some(addressClass) =>
println(s"Successfully compiled Address class: $addressClass")
case None => println("Could not find Address class.")
}
getClassByName(classLoader, "Person") match {
case Some(personClass) =>
println(s"Successfully compiled Person class: $personClass")
// ... (Example of creating an instance and accessing fields)
case None => println("Could not find Person class.")
}
getClassByName(classLoader, "Root") match {
case Some(rootClass) =>
println(s"Successfully compiled Root class: $rootClass")
case None => println("Could not find Root class.")
}
case None =>
println("Compilation failed.")
}
}
}
May I ask what your use case is? This sort of dynamic compilation is unusual in the Scala world, and access is inherently going to be challenging, since the types don’t exist at the primary compile time.
That said: what’s the question? It’s not obvious what’s going on here, or what problems you’re hitting.
I want to access the case classes I just compiled on the fly. especially if I used one of them to apply type safety to a dataframe to create a dataset in spark
val ds = df.as[Address]
Wouldn’t macros make more sense for your use case? There is also a Scala 3 library that, by my understanding does a similar thing GitHub - VirtusLab/iskra: Typesafe wrapper for Apache Spark DataFrame API
As for dynamic compilation we do that for mdoc but it’s not possible to get those compiled types reliably really, but rather have them behind a trait. Alternatively, you could potentially add those compiled classes to filesystem and the classpath, but that might break easily
1 Like
If I’m understanding what you’re trying to do (which I might not), it’s impossible. If, say, Address
is only defined at runtime, then
val ds = df.as[Address]
can’t compile, since Address
doesn’t exist at compile-time.
(That’s not a Scala limitation – it’s just inherent in the different between compile-time and runtime.)
I agree with @tgodzik that macros might work for your needs, but that’s because it’s a compile-time operation. If stuff really can’t be defined until runtime, then it’s not really possible to use it in a typesafe way like that.
1 Like
Good point – I don’t think I’ve seen that used in the wild yet, but it’s an intriguing new technique.
Based on the docs, though, I think it requires knowing and stating the types upfront, even if the implementation is generated at runtime – that’s how it gets around my “impossible” statement above. (Indeed, it’s an interesting illustration of the difference between compile-time types and runtime classes.)
And it isn’t obvious to me whether this can work when starting out with a String at compile time. (Might be possible; I don’t know the new metaprogramming systems deeply enough yet.)
2 Likes
Oh! I missed that. No, pretty sure you cannot start out with a string and use that. Gotta spin up a full compiler for that.
As i understand, the meta programming of scala 3 can be narrowed to only what’s required for the dynamic behavior specified. Not really what happens as far as i know.