TheKojuEffect
TheKojuEffect

Reputation: 21101

Declare self-type of a child trait in a parent trait in scala

I have some scala traits with same self-type declared as following.

trait BookDbModule {
  self: DbConfig => // Abstract this to a parent trait
  /* ... */
}

trait AuthorDbModule {
  self: DbConfig => // Abstract this to a parent trait
  /* ... */
}

I am trying to abstract the self-type declaration to a parent trait such that each of these traits does not have to define self-type. I tried the following.

trait DbModule {
  self: DbConfig =>
  // Some common DbModule methods
}

// !!! Illegal Inheritance, self-type BookDbModule does not conform to DbConfig
trait BookDbModule extends DbModule {
  // What needs to be used instead of extends?
  /* ... */
}

// !!! Illegal Inheritance, self-type AuthorDbModule does not conform to DbConfig
trait AuthorDbModule extends DbModule {
  // What needs to be used instead of extends?
  /* ... */
}

The error messages Illegal Inheritance makes sense to me as BookDbModule does not extend DbConfig.

Is there any way in Scala to enforce self-type of children traits in a parent trait?

Update: It seems like the question is little bit confusing.

What I want to achieve is, I want to omit necessity to set self-type for BookDbModule and AuthorDbModule by extending (or any other scala feature) the parent trait DbModule while has the self-type DbConfig.

So, basically, I am looking for a way to make children traits (BookDbModule and AuthorDbModule) be extended by only those classes with DbConfig by declaring self-type in parent DbModule but not in those children traits.

// This works but is there any way to omit necessity to write
// self: DbConfig =>
trait AuthorDbModule extends DbModule {
  self: DbConfig =>
  /* ... */
}

Please let me know if it is still confusing.

Thank You!

Upvotes: 3

Views: 669

Answers (2)

Mahdi
Mahdi

Reputation: 1882

The answer is no. It is not possible. In fact what you say is against the purpose of self typing.

trait DbModule {
  self: DbConfig =>
}

trait BookDbModule extends DbModule {
}

In your example (summarized here), DbModule says my children must somehow provide the functionality defined in DbConfig. But trait BookDbModule cannot show that unless it either extends DbConfig or explicitly self type it. And that is against what you wanted...

Upvotes: 0

yǝsʞǝla
yǝsʞǝla

Reputation: 16422

Take a look at this:

scala> trait DbConfig { def f = 123 }
defined trait DbConfig

DbModule that requires DbConfig implementation:

scala> trait DbModule { self: DbConfig => }
defined trait DbModule

BookDbModule is of type DbModule, still requires DbConfig implementation:

scala> trait BookDbModule extends DbModule { self: DbConfig => }
defined trait BookDbModule
scala> new BookDbModule with DbConfig {}.f
res0: Int = 123

BookDbModule is of type BookDbModule, requires DbConfig implementation directly:

scala> trait BookDbModule { self: DbConfig => }
defined trait BookDbModule
scala> new BookDbModule with DbConfig {}.f
res1: Int = 123

BookDbModule is of type BookDbModule, requires DbModule implementation, which in turn requires DbConfig implementation:

scala> trait BookDbModule { self: DbModule => }
defined trait BookDbModule

scala> new BookDbModule with DbConfig {}.f
<console>:14: error: illegal inheritance;
 self-type BookDbModule with DbConfig does not conform to BookDbModule's selftype BookDbModule with DbModule
       new BookDbModule with DbConfig {}.f
           ^

scala> new BookDbModule with DbConfig with DbModule {}.f
res3: Int = 123

You can also use inheritance:

trait BookDbModule extends DbModule with DbConfig
scala> new BookDbModule with DbConfig {}.f
res4: Int = 123

However you can't inherit a self type annotation somehow, so you can either resort to inheritance, or annotate with self types explicitly. Note that this simplification is also possible:

scala> trait DbConfig { def f = 123 }
defined trait DbConfig

scala> trait DbModule { self: DbConfig => }
defined trait DbModule

scala> trait DbModuleService extends DbModule with DbConfig
defined trait DbModuleService

Closest to what you are looking for, but has to use intermediate trait DbModuleService that is "complete":

scala> trait BookDbModule extends DbModuleService
defined trait BookDbModule

scala> new BookDbModule {}.f
res0: Int = 123

Or:

scala> trait DbConfig { def f = 123 }
defined trait DbConfig

scala> trait DbModule { self: DbConfig => }
defined trait DbModule

scala> trait DbModuleService extends DbModule with DbConfig
defined trait DbModuleService

scala> trait BookDbModule { self: DbModuleService => }
defined trait BookDbModule

scala> new BookDbModule with DbModuleService {}.f
res0: Int = 123

Upvotes: 1

Related Questions