Define an abstract class or trait that depends on an implicit - scala

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._

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.

Related

Scala inherited type doesn't satisfy parent type requirement

Have been using Scala for a while, and hoping to get into domain modeling using type constraints. In the following code, I am trying to design a domain. Excuse the cryptic classnames, wanted to focus on the specific problem I am facing at hand.
Here is the code:
import scala.collection.mutable
class ClassTypeTest
{
trait TSM
{
def fit[T <: TSM](): FR[T]
}
abstract class FM extends TSM{}
trait MP[T <: TSM]
{
def name: String
}
trait FiM[T <: TSM]
{
val fittedParams = mutable.HashMap[String, MP[T]]()
}
class FR[T <: TSM](fm: FiM[T])
// Now define classes related to SMA
//===================================
abstract class SMAP extends MP[SMA]
class SMAO(n: String) extends SMAP
{
override def name = n
}
class SMAM(smao: SMAO) extends FiM[SMA]
{
fittedParams(smao.name) = smao
}
class SMA extends FM
{
override def fit[SMA]() =
{
val fim = new SMAM(new SMAO("x"))
//*******************************
// Following line shows the error:
// Error:(40, 25) type mismatch;
// found : ClassTypeTest.this.SMAM
// required: ClassTypeTest.this.FiM[SMA]
// new FR[SMA](fim)
//*******************************************
new FR[SMA](fim)
}
}
}
I am defining SMAM as extends FiM[SMA], so why is the compiler complaining about Type mismatch. Required FiM[SMA], found: SMAM. Have I ill-defined one of the type parameters or constraints?
I am trying to constrain the type of fit method, and the FR object to one of the subclasses of TSM. How do I achieve this?
I am defining SMAM as extends FiM[SMA], so why is the compiler complaining about Type mismatch. Required FiM[SMA], found: SMAM.
SMA in def fit[SMA]() = ... in
class SMA extends FM
{
override def fit[SMA]() =
{
val fim = new SMAM(new SMAO("x"))
new FR[SMA](fim)
}
}
is a type parameter. It's arbitrary type, not the class SMA (the type parameter SMA hid class SMA). You could use arbitrary identifier here, e.g. T
class SMA extends FM
{
override def fit[T]() =
{
val fim = new SMAM(new SMAO("x"))
new FR[T](fim)
}
}
So I guess the error
type mismatch;
found : SMAM
required: FiM[T]
is clear now.
trait TSM
{
def fit[T <: TSM](): FR[T]
}
This defines a trait with a method fit that for any type T returns an instance of FR[T]
class SMA extends TSM
{
override def fit[SMA]() =
{
val fim = new SMAM(new SMAO("x"))
new FR[SMA](fim)
}
}
This overrides method fit defined in the trait. SMA in method definition is a type parameter, not a reference to the class name. You can (probably, should) replace it with T, it'll be equivalent, but a bit less confusing:
override def fit[T <: TSM](): FR[T] = new FR(new SMAM(new SMAO("x")))
This will give you a more descriptive error - something to the effect that you are trying to return FR[SMA] where FR[T] is expected.
The bottom line is you cannot make a method in subclass less "generic" than it is declared in a superclass. To better understand why that is the case, consider this:
abstract class Foo extends TSM
val sma: TSM = new SMA()
val foo: FR[Foo] = sma.fit[Foo]()
This should work: sma is an instance of TSM, and TSM.fit is supposed to work for any type parameter which is a subclass of TSM, which Foo clearly is.
But your SMA.fit as written can only return FR[SMA], so the snippet above would not work if it was allowed.
How to fix this?
Well, that depends on what you actually want. One possibility is to parametrize the trait itself rather than the method:
trait TSM[T <: TSM[_]] {
def fit(): FR[T]
}
class SMA extends TSM[SMA]
{
override def fit() = new FR(new SMAM(new SMAO("x")))
}

Scala - Using guice multibinder on a generic interface fails

I have the following interface:
trait Subject[T] {
def fetch() :Future[Option[T]]
}
And class:
class ChildSubject#Inject()(dao:Dao) extends Subject[String]{
def fetch(): Future[Option[String]] ={
dao.find("10").map{ name => Some(name)
}
}
And a module:
class SubjectModule extends AbstractModule with ScalaModule{
override def configure(): Unit = {
val subMulti = ScalaMultibinder.newSetBinder[Subject](binder)
subMulti.addBinding.to[ChildSubject]
}
}
And I try to inject this Set:
#Singleton
class SomeClass #Inject()(subjects: Set[Subject]){
subjects.map{
//do somthing
}
}
And I get the following error:
play.sbt.PlayExceptions$CompilationException: Compilation error[kinds
of the type arguments (com.test.Subject) do not conform to the expected
kinds of the type parameters (type T).
com.test.Subject's type parameters do not match type T's expected
parameters:
trait Subject has one type parameter, but type T has none]
Any idea??
Thanks!
You need a new TypeLiteral<Subject<String>>() for binding - generically typed interface like Subject<T> in Guice from Java. It's very likely that Scala requires the same in some form.
Something like this may work:
class SubjectModule extends AbstractModule with ScalaModule{
override def configure(): Unit = {
val subMulti = ScalaMultibinder.newSetBinder[Subject[String]](binder)
subMulti.addBinding.to[ChildSubject]
}
}

Slick 3 reusable generic repository

I am experiencing issues making Slick's TableQuery used in a generic fashion.
Observe the regular situation:
class AccountRepository {
override protected val dbConfig = DatabaseConfigProvider.get[JdbcProfile](Play.current)
val accounts = TableQuery[Accounts]
def all = db.run(accounts.result)
...
The idea would be to extract everything possible into generic trait or abstract class in order to avoid repetition. For the sake of simplicity I included only the problematic code.
abstract class GenericRepository[T] extends HasDatabaseConfig[JdbcProfile] {
override protected val dbConfig = DatabaseConfigProvider.get[JdbcProfile(Play.current)
val table = TableQuery[T]
}
And to use it like:
class AccountRepository extends GenericRepository[Accounts] {
However, that creates a compilation error:
type arguments [T] conform to the bounds of none of the overloaded alternatives of value apply: [E <: slick.lifted.AbstractTable[]]=> slick.lifted.TableQuery[E] [E <: slick.lifted.AbstractTable[]](cons: slick.lifted.Tag => E)slick.lifted.TableQuery[E]
Trying to fix the issue by setting a boundary doesn't help as well.
abstract class GenericRepository[T <: slick.lifted.AbstractTable[T]] extends HasDatabaseConfig[JdbcProfile] {
However, we end up with a different error:
class type required but T found
at following place:
val table = TableQuery[T]
Any idea about the solution?
You have to pass table query manually,
abstract class GenericRepository[T <: slick.lifted.AbstractTable[_]](query: TableQuery[T])
and in implementation,
class AccountRepository extends GenericRepository[Accounts](TableQuery[Accounts])
I hope this will solve your problem.
I guess if you can solve the initialization of tableQuery, then you can continue your GenericRepository. I am using Slick 3.0 with PostgreSQL.
In the slick.lifted.TableQuery, there is a method like the following
// object TableQuery
def apply[E <: AbstractTable[_]](cons: Tag => E): TableQuery[E] =
new TableQuery[E](cons)
So if we can get an instance of E on the fly, then we can get a generic way to create TableQuery. So reflection seems to be a possible way to solve it.
import scala.reflect.runtime.{ universe => ru }
import slick.lifted.{ AbstractTable, ProvenShape, Tag }
import slick.driver.PostgresDriver.api._
object Reflection {
val runtimeMirror = ru.runtimeMirror(getClass.getClassLoader)
def getTypeTag[T: ru.TypeTag] = ru.typeTag[T]
def createClassByConstructor[T: ru.TypeTag](args: Any*) =
runtimeMirror.reflectClass(getTypeTag[T].tpe.typeSymbol.asClass)
.reflectConstructor(ru.typeOf[T].declaration(ru.nme.CONSTRUCTOR)
.asMethod)(args: _*).asInstanceOf[T]
}
// context bound here is for createClassByConstructor to use
abstract class GenericTableQuery[U, T <: AbstractTable[U]: ru.TypeTag] {
import Reflection._
// look at following code: Students, if you want to initialize Students
// you're gonna need a tag parameter, that's why we pass tag here
val tableQuery = TableQuery.apply(tag => createClassByConstructor[T](tag))
}
// Sample Table
case class Student(name: String, age: Int)
class Students(tag: Tag) extends Table[Student](tag, "students") {
def name = column[String]("name")
def age = column[Int]("age")
override def * : ProvenShape[Student] = (name, age)
<> (Student.tupled, Student.unapply _)
}
// get TableQuery
object TestGenericTableQuery extends GenericTableQuery[Student, Students] {
val studentQuery = tableQuery
}
The codes mentioned above is just focused on the issue of generic TableQuery, try to combine it with your GenericRepository and your problem may get solved.
Anyway, hope it helps.

Strange implicit resolution behaviour with type classes and package objects

I'm trying to provide different sets of implementations for a list of type classes, where importing different package objects would give the end user a different version of a TypeClass implementation into scope, with the swap being completely invisible.
trait TypeClass[T] {
def name: String
}
trait DefaultTypeClasses {
implicit val String: TypeClass[String]
implicit val Int: TypeClass[Int]
}
trait FirstImplementor extends DefaultTypeClasses {
implicit object String extends TypeClass[String] {
def name = "test"
}
implicit object Int extends TypeClass[Int] {
def name = "int"
}
}
object FirstImplementor extends FirstImplementor
object Test {
import FirstImplementor._
def doSomething[T : TypeClass](value: T): Unit = {
println(implicitly[TypeClass[T]].name)
}
doSomething("test")
}
The above works as expected, but if do:
trait DefaultDefinitions extends DefaultTypeClasses {
}
package object something extends DefaultDefinitions with FirstImplementor {}
And then I import the same package object into the scope of the Test object, like this:
import com.blabla.something._ // should bring all type class definitions from FirstImplementor into scope.
object Test {
def doSomething[T : TypeClass](value: T): Unit = {
println(implicitly[TypeClass[T]].name)
}
doSomething("test")// Cannot find implicit value for TypeClass[String]
doSomething[String]("test")(String) // if passed explicitly it compiles as expected, no more imports necessary.
}
For whatever reason, the materialised type class is available in explicit scope by name, but not in implicit scope. Sounds like my knowledge of the SLS is experiencing a gap, could anyone please clarify?
But if you create Implicits object in package, it works fine. However I have no idea if original error is a bug in scalac or correct behavior
package object something {
object Implicits extends DefaultDefinitions with FirstImplementor
}
import something.Implicits._

Using case object to satisfy abstract type member

I have an architecture where I have a concept of a Component that is instantiated dynamically at run-time from a static configuration and/or run-time data. The static part of the data is provided as a Definition type, which each component must override within its companion object, along with a configurationReads to deserialize it from JSON.
import play.api.libs.json._
trait ComponentDefinition // Intentionally empty
trait ComponentCompanion {
type Definition <: ComponentDefinition
def configurationReads: Reads[Definition]
}
For components with static options, I supply a case class for the Definition type:
class ParticularComponent { /* blah */ }
object ParticularComponent extends ComponentCompanion {
case class Definition(option1: String, option2: Boolean) extends ComponentDefinition
val configurationReads = Json.reads[Definition]
}
This works great. In the past, I've used an empty case class for components that have no static options, but I know that a case object would be more idiomatic. Unfortunately, it doesn't seem to work. If I try:
class OptionlessComponent { /* blah */ }
object OptionlessComponent extends ComponentCompanion {
case object Definition extends ComponentDefinition
val configurationReads = Reads.pure(Definition)
}
I get the following error:
<console>:19: error: overriding method configurationReads in trait ComponentCompanion of type => play.api.libs.json.Reads[OptionlessComponent.Definition];
value configurationReads has incompatible type
val configurationReads = Reads.pure(Definition)
Is there a way to make this work with case object in analogy to the case class version?
I think the case object is creating a new Definition name in the value namespace, but not overloading the abstract type Definition in the type namespace. So this compiles:
class OptionlessComponent { /* blah */ }
object OptionlessComponent extends ComponentCompanion {
case object Definition extends ComponentDefinition
type Definition = Definition.type
val configurationReads = Reads.pure(Definition)
}
Obviously, this isn't exactly an unqualified win over the empty params case class version:
class OptionlessComponent { /* blah */ }
object OptionlessComponent extends ComponentCompanion {
case class Definition() extends ComponentDefinition
val configurationReads = Reads.pure(Definition())
}
If I'm missing something that would allow me to combine the elegance of both of these options, I'm open to suggestions.