Skip to content

Cumbersome disambiguation between same-name, conflicting def and val members inherited via multiple inheritance #23169

Open
@IFTA-KIM

Description

@IFTA-KIM

Compiler version

3.3.5, 3.6.4

Minimized example

trait Actor

trait LoggingAdapter {
  def print = println("LoggingAdapter")
}

trait DiagnosticLoggingAdapter extends LoggingAdapter {
  override def print = println("DiagnosticLoggingAdapter")
}

trait ActorLogging {
  def log: LoggingAdapter = new LoggingAdapter {}
}

trait DiagnosticActorLogging extends Actor {
  val log: DiagnosticLoggingAdapter = new DiagnosticLoggingAdapter {}
}

trait MyDiagnosticActorLogging extends DiagnosticActorLogging {
  def redefLog: DiagnosticLoggingAdapter = log
}

trait FSM extends Actor with ActorLogging

class MyActor extends FSM with MyDiagnosticActorLogging {
  // override val log: DiagnosticLoggingAdapter = super[MyDiagnosticActorLogging].log // Desired option, does not work due to val
  // override val log: LoggingAdapter = super[FSM].log // Type discrepancy, also not the desired behaviour but super[FSM].log works
  // override val log: DiagnosticLoggingAdapter = redefLog // Workaround that gives the desired result
}

new MyActor().log.print

Output

Without override val log: DiagnosticLoggingAdapter = redefLog

error overriding method log in trait ActorLogging of type => Playground.LoggingAdapter;
  value log in trait DiagnosticActorLogging of type Playground.DiagnosticLoggingAdapter class MyActor inherits conflicting members:
  method log in trait ActorLogging of type => Playground.LoggingAdapter  and
  value log in trait DiagnosticActorLogging of type Playground.DiagnosticLoggingAdapter
(Note: this can be resolved by declaring an override in class MyActor.)

With override val log: DiagnosticLoggingAdapter = redefLog

DiagnosticLoggingAdapter

Expectation

A slightly different case to the one mentioned in #23061 - I hoped for a cleaner way to resolve the multiple-inheritance naming conflict but didn't find one, unfortunately. Mainly opening this issue since it may be tangentially relevant to the aforementioned ticket, so apologies if this is not an actual issue but maybe an accepted limitation due to technical or conceptual reasons.

https://scastie.scala-lang.org/EQGARTbATHODep7eGr9YBA

Given the above class/trait structure (where we only control the implementation of the types prefixed with My...), the compiler will complain about conflicting members during the declaration of class MyActor, namely about def log: LoggingAdapter in ActorLogging (via FSM) and val log: DiagnosticLoggingAdapter in DiagnosticActorLogging (via MyDiagnosticActorLogging). The message also states "this can be resolved by declaring an override in class MyActor". However, when trying to declare it as override val log: DiagnosticLoggingAdapter = super[MyDiagnosticActorLogging].log (which is the desired behaviour) it also does not compile because log is a val in MyDiagnosticActorLogging / DiagnosticActorLogging.

Defining redefLog in MyDiagnosticActorLogging and referring to this def when overriding the member log in MyActor seems to do the job as a workaround. Is the restriction of super to def members unavoidable? If so, is there a cleaner way to disambiguate between the inherited members?

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions