Reputation: 2138
I've created simple Scala Play application that has traits with a monadic type and their respective implementation binded in Module configure as:
class Module extends AbstractModule {
override def configure() = {
bind(new TypeLiteral[UserDAO[DBIO]](){}).to(classOf[UserDAOImpl])
bind(new TypeLiteral[UserService[Future]](){}).to(classOf[UserServiceImpl[Future, DBIO]])
}
}
Those traits and implementations are:
///TRAITS
//UserDAO.scala
package models
trait UserDAO[DB[_]] {
def get(userId: Long): DB[Option[User]]
}
//UserService.scala
package services
import resources.UserResponse
import services.response.ServiceResponse
trait UserService[F[_]] {
def findUserById(id: Long): F[ServiceResponse[UserResponse]]
}
///IMPLEMENTATIONS
//UserDAOImpl.scala
package dao
import models.{DataContext, User, UserDAO}
import play.api.db.slick.DatabaseConfigProvider
import slick.dbio.{DBIO => SLICKDBIO}
import javax.inject.Inject
import scala.concurrent.ExecutionContext
class UserDAOImpl @Inject()(
protected val dbConfigProvider: DatabaseConfigProvider,
val context: DataContext
)(
implicit executionContext: ExecutionContext
) extends UserDAO[SLICKDBIO] {
import context.profile.api._
override def get(userId: Long): SLICKDBIO[Option[User]] = context.Users.filter(_.id === userId).result.headOption
}
//UserServiceImpl.scala
package services
import resources.Mappings.UserToResponseMapping
import cats.Monad
import cats.implicits._
import models.{DatabaseManager, UserDAO}
import resources.{NoModel, UserResponse}
import services.response.ServiceResponse
import util.Conversions.{errorToServiceResponse, objectToServiceResponse}
import javax.inject.Inject
class UserServiceImpl[F[_]: Monad, DB[_]: Monad]@Inject()(userRepo: UserDAO[DB],
dbManager: DatabaseManager[F, DB])
extends UserService[F] {
override def findUserById(id: Long): F[ServiceResponse[UserResponse]] = {
for {
user <- dbManager.execute(userRepo.get(id))
} yield user match {
case Some(user) =>user.asResponse.as200
case None => NoModel(id).as404
}
}
}
However, this fails to inject dependencies and throws the following errors:
play.api.UnexpectedException: Unexpected exception[CreationException: Unable to create injector, see the following errors:
1) models.UserDAO<DB> cannot be used as a key; It is not fully specified.
at services.UserServiceImpl.<init>(UserServiceImpl.scala:13)
at Module.configure(Module.scala:35) (via modules: com.google.inject.util.Modules$OverrideModule -> Module)
2) models.DatabaseManager<F, DB> cannot be used as a key; It is not fully specified.
at services.UserServiceImpl.<init>(UserServiceImpl.scala:13)
at Module.configure(Module.scala:35) (via modules: com.google.inject.util.Modules$OverrideModule -> Module)
3) cats.Monad<F> cannot be used as a key; It is not fully specified.
at services.UserServiceImpl.<init>(UserServiceImpl.scala:13)
at Module.configure(Module.scala:35) (via modules: com.google.inject.util.Modules$OverrideModule -> Module)
This question might be related to this one How to bind a class that extends a Trait with a monadic type parameter using Scala Guice?, and in my solution I've applied what is suggested as the answer, but it still fails.
Any suggestions?
Upvotes: 1
Views: 216
Reputation: 15305
If you look at the stacktrace, you can see the issue happens when Guice wants to create an instance of UserServiceImpl
:
... at services.UserServiceImpl.<init> ...
I suspect that Guice cannot know what to "inject" when trying to create this class. It cannot infer that it has to inject a UserDao[DBIO]
for instance, it only knows it has to inject a UserDao[DB]
with DB
being something unspecified.
How to fix that, I can't say for sure but I would look into either:
UserServiceImpl
and bind it instead of the generic one (like a class UserServiceFutureDBIO
)UserServiceImpl
and binding to an instance rather than binding to a class and letting Guice instantiate itUpvotes: 0