Vinay Pandey
Vinay Pandey

Reputation: 1179

Scala return "partial" class which must be mixed-in with trait later

I have a trait

trait DataDef {
    def getDataSource: java.sql.DataSource
}

And then several other traits, which inherit this trait, for example

trait Postgres extends DataDef {
      // Postgres implementation
}

and,

trait MySql extends DataDef {
     // For My SQL
}

And another abstract class (and I can convert it to trait, if required)

abstract class Strategy[T](strategyType: String) {
    self: DataDef =>

    def lookup(): Stream[T] = {
         // use self.getDataSource and strategyType here
    }
}

This way I can keep the code separate for Strategy and Data. For example,

class StrategyA extends Strategy[Int]("typeA") {
    self: DataDef =>
    // implementation of this strategy
}

Now, what I want to do is wire these two together so that when user passes certain strategyType and DataDef type, I can pass the relevant object. What I have so far is:

class getStrategy(strategyType: String, dataType: String) {
    strategyType match {
         case "typeA" => 
            dataType match {
                 case "postgres" => return StrategyA with Postgres
                 case "mysql" => return StrategyA with MySql
            }
         case "typeB" => 
            dataType match {
                 case "postgres" => return StrategyB with Postgres
                 case "mysql" => return StrategyB with MySql
            }
    }
}

The issue is there is so much of code being repeated and it does not look like and elegant solution. I should be able to return Postgres, MySql just once, and should not need to repeat the code over and over.

I want something like:

 def getStrategy(strategyType: String) {
      // return Strategy here, like new StrategyA
 }

 def getDataDef(dataType: String) {
     // return DataDef here 
 }

And then I should be able to mix it, like so:

 getStrategy(strategyType) with getDataDef(dataType)

I have looked into macros and they look promising, but very complicated. I was wondering if there is any other, simpler way of achieving what I want.

Upvotes: 1

Views: 148

Answers (1)

Alexey Romanov
Alexey Romanov

Reputation: 170815

Not literally what you want: with lives in the type world, not in the value world, and types can't be returned from methods.

But if you go with composition over inheritance you can do e.g.

abstract class Strategy[T](strategyType: String, self: DataDef) {
    // optional
    // import self._

    def lookup(): Stream[T] = {
         // use self.getDataSource and strategyType here
    }
}

class StrategyA(self: DataDef) extends Strategy[Int]("typeA", self) {
    // implementation of this strategy
}

def getStrategy(strategyType: String): DataDef => Strategy {
     // return e.g. new StrategyA(_)
}

def getDataDef(dataType: String) {
    // return DataDef here 
}

and then

getStrategy(strategyType)(getDataDef(dataType))

Upvotes: 2

Related Questions