I have multiple hierarchy of Scala traits that can compose an object in many ways. Example:
trait MainA { someDefs... }
trait SubA1 extends MainA { someDefs... }
trait SubA2 extends MainA { someDefs... }
trait MainB { someDefs... }
trait SubB1 extends MainB { someDefs... }
trait SubB2 extends MainB { someDefs... }
The composition should look (abstractly) like this:
new ComposedTrait with MainA with MainB
What I want to do is to create a composed trait on runtime with the prompted desired sub-traits of each case. On regular Java I know there is a method in class Class to obtain the class that matches with the name (Class.forName("name") I think), but it doesn't work in this case. This is what should look like:
//Should yield a ComposedTrait with a SubA2 and SubB1 traits
new ComposedTrait with Class.forName(SubA2) with Class.forName(SubB1)
Is there any way of making this possible? Like an alternative to the Class.forName that works in this scenario of composition?
If you know parent names at compile time you can write a macro (see sbt settings for macro projects)
import scala.language.experimental.macros
import scala.reflect.macros.{blackbox, whitebox}
def makeInstance[A](parentNames: String*): A = macro makeInstanceImpl[A]
def makeInstanceImpl[A: c.WeakTypeTag](
c: whitebox/*blackbox*/.Context
)(parentNames: c.Tree*): c.Tree = {
import c.universe._
def getValue(tree: Tree): String = tree match {
case q"${name: String}" => name
}
// def getValue(tree: Tree): String = c.eval(c.Expr[String](c.untypecheck(tree)))
val parents = parentNames.map(name => tq"${TypeName(getValue(name))}")
q"new ${weakTypeOf[A]} with ..$parents"
}
Please notice that if you make a macro whitebox then it can return a value of more precise type than declared (A)
val instance = makeInstance[ComposedTrait]("SubA2", "SubA1")
// checking the type
instance: ComposedTrait with SubA2 with SubA1 // not just ComposedTrait
// scalacOptions += "-Ymacro-debug-lite"
//scalac: {
// final class $anon extends ComposedTrait with SubA2 with SubA1 {
// def <init>() = {
// super.<init>();
// ()
// }
// };
// new $anon()
//}
Scala Reflection--Create Instance from class name string of this class type
If you know parent names at runtime you can use reflective toolbox (runtime compilation)
import scala.reflect.runtime
import scala.reflect.runtime.universe._
import scala.tools.reflect.ToolBox
val rm = runtime.currentMirror
val tb = rm.mkToolBox()
def makeInstance[A: TypeTag](parentNames: String*): A = {
val parents = parentNames.map(name => tq"${TypeName(name)}")
tb.eval(q"new ${typeOf[A]} with ..$parents").asInstanceOf[A]
}
makeInstance[ComposedTrait]("SubA2", "SubA1")
Please notice that in such case statically you can't have a value of more precise type than ComposedTrait based on runtime strings (parent names).
If you need to create ComposedTrait at runtime you can do this as well
val classSymbol = tb.define(q"trait ComposedTrait".asInstanceOf[ClassDef])
def makeInstance(parentNames: String*): Any = {
val parents = parentNames.map(name => tq"${TypeName(name)}")
tb.eval(q"new $classSymbol with ..$parents")
}
makeInstance("SubA2", "SubA1")
Related
I'm trying to extract ClassSymbols for all type parameters' bounds of a method.
The "solution" I came up with:
Macro annotation implementation:
#compileTimeOnly("Compile-time only annotation")
class classSyms extends StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro impl
}
object classSyms {
def impl(c: whitebox.Context)(annottees: c.Tree*) = {
import c.universe._
annottees.toList foreach {
case q"$mods def $templatename[..$typeparams](...$paramss): $tpt = $body" =>
typeparams foreach {
case q"$mods type $name[..$tparams] >: $low <: $high" =>
if (!high.isEmpty) {
//requires FQCN, does not work with imported names
val classSymbol = c.mirror.staticClass(high.toString)
println(classSymbol)
}
}
}
q"..$annottees"
}
}
Example:
package pack.age
trait Test
package another.pack.age
import pack.age._
trait Bar{
#classSyms
def foo[M <: pack.age.Test, T](): Unit //works ok
#classSyms
def baz[M <: Test, T](): Unit //throws scala.ScalaReflectionException: class Test not found.
}
The problem is such requirements of specifying fully-qualified class name as a parameter bound does not make this macro implementation very useful (no one wants to write this long fqcn stuff, especially if the name is imported).
Is it possible to extract ClassSymbol by an imported name?
Try to use c.typecheck.
Replace
val classSymbol = c.mirror.staticClass(high.toString)
with
val classSymbol = c.typecheck(tq"$high", mode = c.TYPEmode).symbol.asClass
When attempting to get the name of a class via a WeakTypeTag reference when defining a macro implementation, I can't seem to get the proper info if multiple layers of polymorphism are applied.
For example if I have the following setup:
object MacroSupport {
def get_name_impl[A: c.WeakTypeTag](c: blackbox.Context): c.Expr[String] = {
val nameOfA: String = weakTypeOf[A].toString
...
}
def getName[A] = macro get_name_impl[A]
}
abstract class GenericInterface[T] {
def getName: String = MacroSupport.getName[T]
}
case class ContainerA(
someValue: String
)
class FunctionalClass extends GenericInterface[ContainerA] {
val containerName: String = getName
}
What I hope to achieve is having any number of FunctionalClass's, each with their own Container class, and they can report the name of their container, which is used for some meta configuration. Basically MacroSupport and GenericInterface will exist in a library I'm writing while the FunctionalClass and Container levels will be written by others using the library.
The issue I'm having, due to the pass through type in the GenericInterface, FunctionalClass.containerName == "t", and attempts to access Type declarations yield nothing. How can I get the type information from the FunctionalClass declaration to the MacroSupport level?
Try materialization of type class
https://docs.scala-lang.org/overviews/macros/implicits.html#implicit-materializers
import scala.reflect.macros.blackbox
import scala.language.experimental.macros
object MacroSupport {
def getName[A](implicit gn: GetName[A]): String = gn()
trait GetName[A] {
def apply(): String
}
object GetName {
implicit def materializeGetName[A]: GetName[A] = macro materializeGetNameImpl[A]
def materializeGetNameImpl[A: c.WeakTypeTag](c: blackbox.Context): c.Expr[GetName[A]] = {
import c.universe._
c.Expr[GetName[A]] {
q"""
new MacroSupport.GetName[${weakTypeOf[A]}] {
override def apply(): _root_.java.lang.String = ${weakTypeOf[A].toString}
}
"""
}
}
}
}
import MacroSupport.GetName
abstract class GenericInterface[T: GetName] {
def getName: String = MacroSupport.getName[T]
}
case class ContainerA(
someValue: String
)
class FunctionalClass extends GenericInterface[ContainerA] {
val containerName: String = getName
}
(new FunctionalClass).containerName // ContainerA
By the way, shapeless.Typeable does the job. Typeable[A].describe is like our MacroSupport.getName[A].
I'm tying to create a typesafe wrapper around lwjgl. Most importantly, I want it to be a compile time error to pass the wrong constants to a function, e.g. calling glEnable(GL_ARRAY_BUFFER). This whould be rather easy if it weren't for different contexts supporting different constants for the same function.
I figured I'd use type classes to model which constants can be passed to which function. I've got a solution but admittedly it's a bit ugly and fails to apply a certain implicit:
trait GlConst { val glConst: Int }
trait GlConstCompanion[C <: GlConst] { val instance: C }
class GlDepthTest private () extends GlConst { val glConst = GL_DEPTH_TEST }
object GlDepthTest extends GlConstCompanion[GlDepthTest] {
val instance = new GlDepthTest
}
class GlLineSmooth private () extends GlConst { val glConst = GL_LINE_SMOOTH }
object GlLineSmooth extends GlConstCompanion[GlLineSmooth] {
val instance = new GlLineSmooth
}
class GlArrayBuffer private () extends GlConst { val glConst = GL_ARRAY_BUFFER }
object GlArrayBuffer extends GlConstCompanion[GlArrayBuffer] {
val instance = new GlArrayBuffer
}
// type class for arguments to glEnable
trait GlEnableCap[T <: GlConst] extends GlConst
object GlContext33 {
implicit object GlDepthTestIsEnableCap extends GlEnableCap[GlDepthTest] {
val glConst = GlDepthTest.instance.glConst
}
implicit object GlLineSmoothIsEnableCap extends GlEnableCap[GlLineSmooth] {
val glConst = GlLineSmooth.instance.glConst
}
def glEnable[T <: GlConst : GlEnableCap](t: T): Unit = println(implicitly[GlEnableCap[T]].glConst)
}
object Test extends App {
import GlContext33._
implicit def constComp2Const[C <: GlConst](cc: GlConstCompanion[C]): C = cc.instance
// works
glEnable(GlDepthTest.instance)
// fails to apply implicit glConstComp2Comp
glEnable(GlDepthTest)
// fails intentionally
glEnable(GlArrayBuffer)
}
Is there a way to get the implicit to work? Or is there an even better way to wrap OpenGL's constants?
As a rule of thumb, don't use implicits if you don't have to.
In this case you can solve it just as well using only type bounds:
// Start writing your ScalaFiddle code here
val GL_DEPTH_TEST = 1
val GL_LINE_SMOOTH = 1
val GL_ARRAY_BUFFER = 1
trait GlConstCap
trait GlEnableConstCap extends GlConstCap
trait GlBufferConstCap extends GlConstCap
trait GlConst[C <: GlConstCap] { val value: Int }
object GlDepthTest extends GlConst[GlEnableConstCap] {
val value = GL_DEPTH_TEST
}
object GlLineSmooth extends GlConst[GlEnableConstCap] {
val value = GL_LINE_SMOOTH
}
object GlArrayBuffer extends GlConst[GlBufferConstCap] {
val value = GL_ARRAY_BUFFER
}
object GlContext33 {
def glEnable[T <: GlConst[GlEnableConstCap]](t: T): Unit = println(t.value)
}
object Test extends App {
import GlContext33._
// works
glEnable(GlDepthTest)
// fails to apply implicit glConstComp2Comp
glEnable(GlDepthTest)
// fails intentionally
glEnable(GlArrayBuffer)
}
Try it out!
Note: You might want to add contravariance to C in GlConst if you want to create deeper inheritance structures of GlConstCap.
I hope this helps.
Scala fiddle here
import scala.reflect.ClassTag
import scala.language.existentials
class SomeClass[T <: SomeClass[T, R], R] extends EarlyInit[T, R] {}
trait EarlyInit[T <: SomeClass[T, R], R] {
self: SomeClass[T, R] =>
val clazz = getClass.getSuperclass
val potentialFields = clazz.getDeclaredClasses.toList
potentialFields.foreach {
field => {
// This correctly prints out fields of object members.
// but invokation throws an error every time.
println(s"${field.getName}")
}
}
}
class SomeThing extends SomeClass[SomeThing, Any] {
val stringTest = "test"
object name
object test
}
object SomeThing extends SomeThing {}
val x = SomeThing
How do I access and initialise object members(name and test) with reflection?
You can achieve this with scala reflection API:
import scala.reflect.runtime.universe._
class SomeClass[T <: SomeClass[T, R]: TypeTag, R] extends EarlyInit[T] {}
class EarlyInit[T: TypeTag] {
val mirror = runtimeMirror(this.getClass.getClassLoader)
val reflection = mirror.reflect(this)
typeTag[T].tpe.members.filter(_.isModule).foreach(m => reflection.reflectModule(m.asModule).instance)
}
class SomeThing extends SomeClass[SomeThing, Any] {
val stringTest = "test"
object name {
println("name initialized")
}
object test {
println("test initialized")
}
}
object SomeThing extends SomeThing {}
val x = SomeThing
You need to make EarlyInit a class in order to be able to get a TypeTag evidence. Then you just search for all modules and access them (they would be initialized in the process)
Update
You can also simplify EarlyInit a bit and get rid of type parameter. You actually can get a Type of this directly from mirror:
trait EarlyInit {
val mirror = runtimeMirror(this.getClass.getClassLoader)
val reflection = mirror.reflect(this)
mirror
.classSymbol(this.getClass)
.toType
.members
.filter(_.isModule)
.foreach(m => reflection.reflectModule(m.asModule).instance)
}
I have a macro which enumerates the direct sub types of a sealed trait:
import scala.reflect.macros.Context
import language.experimental.macros
object Checker {
def apply[A]: Unit = macro applyImpl[A]
def applyImpl[A: c.WeakTypeTag](c: Context): c.Expr[Unit] = {
val tpe = c.weakTypeOf[A].typeSymbol.asClass
require (tpe.isSealed)
tpe.typeSignature // SI-7046
require (tpe.knownDirectSubclasses.nonEmpty)
import c.universe._
c.Expr[Unit](reify {} .tree)
}
}
Then this works:
sealed trait A
case class A1(i: Int) extends A
object NotNested {
val nada = Checker[A]
}
But this fails:
object Nested {
sealed trait A
case class A1(i: Int) extends A
val nada = Checker[A]
}
[error] java.lang.IllegalArgumentException: requirement failed:
Did not find sub classes
I thought I ran into SI-7046, so I added the call to tpe.typeSignature, but that doesn't help apparently.
I need a work around for this using Scala 2.10.2. Somehow I must enforce some extra type trees to be initialised, I guess?
A possible workaround is following (what do you think #EugeneBurmako).
val cls: ClassSymbol = sub.asClass
println(s"knownDirectSubclasses = ${cls.knownDirectSubclasses}")
// print "knownDirectSubclasses = Set()"
val subsub = cls.owner.typeSignature.decls.filter {
case c: ClassSymbol =>
cls != c && c.selfType.baseClasses.contains(cls)
case _ => false
}
println(s"subsub = $subsub")
// print the actual sub classes