Define an abstract class or trait that depends on an implicit

inheritance, playframework-2.0, scala, traits

Solution

If you want the controler classes themselves to define the implicit, then just declare abstract implicit values, and define them in the derived classes.

abstract class EntityController[A<:Entity] extends Controller {
  protected implicit def entityWriter: Writes[A]
  protected implicit def entityListWriter: Writes[List[A]]
  ...      
}

class MyEntity extends Entity {
  ...
}

class MyEntityController extends EntityController[MyEntity] {
  protected def entityWriter: Writes[MyEntity] = ...
  protected def entityListWriter: Writes[List[MyEntity]] = ...    
}

However, it is much more practical to define these implicits outside the controller, typically in the companion object of your entity, so that they the compiler can find them automatically without import. Then, pass the implicit values to the constructor of `EntityController`:

abstract class EntityController[A<:Entity](implicit entityWriter: Writes[A], entityListWriter: Writes[List[A]] ) extends Controller {
  ...      
}

class MyEntity extends Entity {
  ...
}
object MyEntity {
  protected implicit def entityWriter: Writes[A]
  protected implicit def entityListWriter: Writes[List[A]]
}

class MyEntityController extends EntityController[MyEntity] {
  ...
}

Final note, the implicit to List[MyEntity] is probably unneeded( thus only the implicit for MyEntity would need to be explicitly defined). I have not checked, but usually when using this "typeclass pattern", the framework will already define an implicit for each List[T], provided that there is an implicit for T. This is probably the case, though I have not checked.

Problem

I have this play framework 2 code (simplified): ``` import formatters.json.IdeaTypeFormatter._ object IdeaTypes extends Controller { def list = Action { request => Ok(toJson(IdeaType.find(request.queryString))) } def show(id: Long) = Action { IdeaType.findById(id).map { ideatype => Ok(toJson(ideatype)) }.getOrElse(JsonNotFound("Type of idea with id %s not found".format(id))) } } ``` `IdeaType` class extends `Entity`, and it's companion object, `IdeaType`, extends `EntityCompanion`. As you may expect, I have this kind of code in every controller, so I'd like to extract the basic behavior to a trait, something like this: ``` abstract class EntityController[A<:Entity] extends Controller { val companion: EntityCompanion val name = "entity" def list = Action { request => Ok(toJson(companion.find(request.queryString))) } def show(id: Long) = Action { companion.findById(id).map { entity => Ok(toJson(entity)) }.getOrElse(JsonNotFound("%s with id %s not found".format(name, id))) } } ``` But I get the following error: ``` [error] EntityController.scala:25: No Json deserializer found for type List[A]. [error] Try to implement an implicit Writes or Format for this type. [error] Ok(toJson(companion.find(request.queryString))) [error] ^ [error] EntityController.scala:34: No Json deserializer found for type A. [error] Try to implement an implicit Writes or Format for this type. [error] Ok(toJson(entity)) [error] ^ ``` I don't know how to tell that the implicit `Writes` will be implemented by the classes implementing the `EntityController` trait (or inheriting the abstract class `EntityController`) -- edit so far now I'm doing it like this: ``` abstract class CrudController[A <: Entity]( val model: EntityCompanion[A], val name: String, implicit val formatter: Format[A] ) extends Controller { ``` and use it like this ``` object CrudIdeaTypes extends CrudController[IdeaType]( model = IdeaType, name = "type of idea", formatter = JsonIdeaTypeFormatter ) ``` I could't get scala to automatically pick it using implicits. I tried with this import but it didn't work ``` import formatters.json.IdeaTypeFormatter._ ```

Original source