Dynamic trait mixin

Hey everyone, I’m somewhat new at Scala coding and would like to have a parametric function that combines some traits, in the following example Foo.abc should take the traits to mixin with ABC to produce a result.

Just for reference Foo.iyk is the non-dynamic version of it.

I was trying to use a TypeTag, but the compiler says there’s no evidence for new ABC with X with Y with Z

Now, this is just an exercise on learning Scala, is this possible at all ? Is it possible to pass traits (types?) as first-order values to functions?

Thanks in advice for any answer!


trait A { def a: String }
trait B { def b: String }
trait C { def c: String }

trait X extends A { def a = "x" }
trait Y extends B { def b = "y" }
trait Z extends C { def c = "z" }

trait I extends A { def a = "i" }
trait J extends B { def b = "j" }
trait K extends C { def c = "k" }

trait ABC {
  this: A with B with C =>
  def abc = s"$a $b $c"
}

object Foo {
  def iyk = (new ABC with I with Y with K).abc

  // FAIL: class type required but M found: with M
  // def abc[M,N,O] = (new ABC with M with N with O).abc

  import scala.reflect.runtime.universe._
  def instanceOf[T](implicit t: TypeTag[T]): T =
    t.mirror.runtimeClass(t.tpe).newInstance.asInstanceOf[T]

  type AbcOf[M,N,O] = ABC with M with N with O
  def abcOf[M,N,O] = instanceOf[AbcOf[M,N,O]]

  def abc[M,N,O] = abcOf[M,N,O].abc
}

println(Foo.iyk) // works
println(Foo.abc[X,Y,Z]) // compiler fails as there's no evidence of ABC with X with Y with Z

AFAIK trait mixing is compile time process (i.e. static), but you’re pushing it to run time (to be dynamic). For dynamic trait mixing you would need to generate Java bytecode on the fly and for that you need to use Scala compiler as a library in your program.

Also TypeTags are generated at the place where types are fixed to something concrete (i.e. compiler generates TypeTags at the place it statically knows the class it should generate TypeTags for). So instead of:
def abc[M,N,O] = abcOf[M,N,O].abc
you would need
def abc[M,N,O](implicit t: TypeTag[AbcOf[M, N, O]]) = abcOf[M,N,O].abc
then change abcOf accordingly.

Update - here’s version that compiles but fails at runtime (for reasons I’ve just described):

trait A { def a: String }
trait B { def b: String }
trait C { def c: String }

trait X extends A { def a = "x" }
trait Y extends B { def b = "y" }
trait Z extends C { def c = "z" }

trait I extends A { def a = "i" }
trait J extends B { def b = "j" }
trait K extends C { def c = "k" }

trait ABC {
  this: A with B with C =>
  def abc = s"$a $b $c"
}

object Foo {
  def iyk = (new ABC with I with Y with K).abc

  import scala.reflect.runtime.universe._

  def instanceOf[T](implicit t: TypeTag[T]): T =
    t.mirror.runtimeClass(t.tpe).newInstance.asInstanceOf[T]

  type AbcOf[M,N,O] = ABC with M with N with O
  def abcOf[M,N,O](implicit t: TypeTag[AbcOf[M, N, O]]) = instanceOf[AbcOf[M,N,O]]

  def abc[M,N,O](implicit t: TypeTag[AbcOf[M, N, O]]) = abcOf[M,N,O].abc
}


object DynamicTraits extends App {
  println(Foo.iyk) // works
  println(Foo.abc[X,Y,Z]) // fails in runtime

}
1 Like

Thank you, @tarsa!