Search code examples
scalagenericstype-erasuretype-parametertype-alias

How can I implement concrete class which extends trait defining a method with type by the type parameter's type alias


I would like ask for some help for advanced scala developers. My problem is that I would like to access a type alias belonging to a type parameters of a class' parent.

  case class MyModel(foo: String = "bar")

  case class MyDispatcher()

  trait Module[M, D] {
    type Dispatcher = D
    type Model = M
  }

  trait MySpecificModule[A <: Module[_, _]] {
    def dispatcher(): A#Dispatcher
  }

  class ModuleClass extends Module[MyModel, MyDispatcher] {
    //...
  }

  class MySpecificModuleClass extends MySpecificModule[ModuleClass] {
    override def dispatcher(): MyDispatcher = MyDispatcher()
  }

So basically MySpecificModule extends a generic trait, and should know the type of the dispatcher method. In this case of MySpecificModuleClass it should be MyDispatcher. But when I try to compile this code I am getting compilation error because the type of the method, is not the same as defined: A#Dispatcher, however in the reality it is.

Error:(21, 18) overriding method dispatcher in trait MySpecificModule of type ()_$2;
 method dispatcher has incompatible type
    override def dispatcher(): MyDispatcher = MyDispatcher()

             ^

I would appreciate any advice you suggest. Thanks in advance, Gabor

Resolved

case class MyModel(foo: String = "bar")

case class MyDispatcher()

trait AbstractModule {
  type Dispatcher
  type Model
}

trait Module[M, D] extends AbstractModule {
  type Dispatcher = D
  type Model = M
}

trait MySpecificModule[A <: AbstractModule] {
  def dispatcher(): A#Dispatcher
}

class ModuleClass extends Module[MyModel, MyDispatcher] {
  //...
}

class MySpecificModuleClass extends MySpecificModule[ModuleClass] {
  override def dispatcher(): MyDispatcher = MyDispatcher()
}

Solution

  • I don't fully understand Scala's reasoning here, but if you get rid of type parameters, things start to work:

    case class MyModel(foo: String = "bar")
    
    case class MyDispatcher()
    
    trait Module {
      type Dispatcher
      type Model
    }
    
    trait MySpecificModule[A <: Module] {
      def dispatcher(): A#Dispatcher
    }
    
    class ModuleClass extends Module {
      type Model = MyModel
      type Dispatcher = MyDispatcher
      //...
    }
    
    class MySpecificModuleClass extends MySpecificModule[ModuleClass] {
      override def dispatcher(): MyDispatcher = MyDispatcher()
    }
    

    And if you really want to have those type params, you can introduce a helper trait:

    trait AbstractModule[M, D] extends Module {
      type Model = M
      type Dispatcher = D
    }
    
    class ModuleClass extends AbstractModule[MyModel,MyDispatcher]