Scala - Conditionally add traits to class instance during construction

1.5k views Asked by At

I'm trying create an instance of a class and mix in certain traits based on certain conditions. So Given:

class Foo

trait A

trait B

I can do something like

if (fooType == "A")
  new Foo with A
else if (footType == "B")
  new Foo With B

That works just fine for a simple case like this. My issue is that multiple traits can be mixed into the same instance based on other conditions, and on top of that the class being instantiated has a fair amount of parameters so this leads to a pretty ugly conditional block.

What I would like to do is determine the traits to be mixed in before hand something like this (which I know is not legal scala code):

val t1 = fooType match {
  case "a" => A
  case "b" => B
}

val t2 = fooScope match {
  case "x" => X
  case "y" => Y
}

new Foo with t1 with t2

Where A, B, X, and Y are all previously defined traits, and fooType and fooScope are inputs to my function. I'm not sure if there is anything I can do which is somewhat similar to the above, but any advice would be appreciated.

Thanks

2

There are 2 answers

0
Mario Camou On

I believe what you want to do is not possible. In Scala the type of an object has to be known at compile time, so new Foo with A works but new Foo with t1 will not because t1 is resolved only at run time.

0
bjfletcher On

A couple of ideas to throw in the mix...

Non-runtime Factory

With the complicated code full of conditionals that you speak of, get those to put together a list of possible combinations. This list can then be used to create all the classes. Very simple example:

(for(a <- Seq("A", "B"); b <- Seq("X", "Y")) yield
  s"class Foo$a$b extends $a with $b").mkString("\n")

which'll produce the following:

class FooAX extends A with X
class FooBX extends B with X
class FooAY extends A with Y
class FooBY extends B with Y

Then simply:

val ax = new FooAX()

which is nice because it's very strongly typed and everyone'll know exactly what type it is everywhere without much casting/inference headache.

Delegation

There's an interesting non-reflection/classloader technique here:

https://github.com/b-studios/MixinComposition

which takes in traits as parameters and then simply delegate to the appropriate trait.