Scala value has incompatible type? - scala

I am still new enough to Scala that the typing system is destroying me. I haven't yet thought of a solution or discovered a pattern that gets around this particular problem that I am trying to solve. Consider the following program:
ShapeType.scala
package models
abstract class ShapeType {
val themes: ShapeThemes[ShapeTheme] // I think this is where the problem is...?
}
class CircleShapeType extends ShapeType {
val themes = CircleShapeThemes
}
object CircleShapeType extends CircleShapeType
ShapeThemes.scala
package models
abstract class ShapeThemes[T <: ShapeTheme] {
val themes: List[T]
}
class CircleShapeThemes extends ShapeThemes[CircleShapeTheme] {
val themes = List(
new CircleShapeTheme,
new CircleShapeTheme,
new CircleShapeTheme
)
}
object CircleShapeThemes extends CircleShapeThemes
ShapeTheme.scala
package models
class ShapeTheme
class CircleShapeTheme extends ShapeTheme
When I attempt to compile the program (using sbt), I get the following error:
[error] /Users/mjs/Projects/sandbox/shape-types/src/main/scala/ShapeType.scala:8: overriding value themes in class ShapeType of type models.ShapeThemes[models.ShapeTheme];
[error] value themes has incompatible type
[error] val themes = CircleShapeThemes
[error] ^
[error] one error found
[error] (compile:compile) Compilation failed
[error] Total time: 2 s, completed Mar 14, 2015 5:08:43 PM
However, as far as I can tell, CircleShapeThemes is a ShapeThemes[ShapeTheme]. What am I missing?

CircleShapeThemes is not a ShapeThemes[ShapeTheme], it's a ShapeThemes[CircleShapeTheme].
"But", you may object, "a CircleShapeTheme is a ShapeTheme! Indeed, but that subclass relationship isn't propagated by default. You have to ask for it by making the type parameter covariant: abstract class ShapeThemes[+T <: ShapeTheme]

Related

How to bind a class that extends a Trait with a monadic type parameter using Scala Guice?

I need to bind the implementation of this trait:
trait ClientRepository[F[_]] {
def list(): F[Iterable[ClientDTO]]
}
to this implementation:
import cats.effect.IO
#Singleton
class ClientRepositoryImpl #Inject()(db: OldDataBase, c: IOContextShift)
extends ClientRepository[IO] {
override def list(): IO[Iterable[ClientDTO]] = ???
}
I'm using Scala Play! v2.7.2 and Scala v2.12.8, with scala-guice v4.2.1. In order to bind the trait to its implementation I would like to do something like that in my Module.scala:
class Module(environment: Environment, configuration: Configuration)
extends AbstractModule
with ScalaModule {
override def configure() = {
bind[ClientRepository].to[ClientRepositoryImpl[IO]].in[Singleton]
}
}
And the error I get is:
[error] app/Module.scala:37:9: kinds of the type arguments (ClientRepository) do not conform to the expected kinds of the type parameters (type T).
[error] ClientRepository's type parameters do not match type T's expected parameters:
[error] trait ClientRepository has one type parameter, but type T has none
[error] bind[ClientRepository].to[ClientRepositoryImpl[IO]].in[Singleton]
[error] ^
[error] app/Module.scala:37:31: ClientRepositoryImpl does not take type parameters
[error] bind[ClientRepository].to[ClientRepositoryImpl[IO]].in[Singleton]
[error] ^
[error]
I've also tried:
bind[ClientRepository[IO]].to[ClientRepositoryImpl].in[Singleton]
Module.scala:37:9: kinds of the type arguments (cats.effect.IO) do not conform to the expected kinds of the type parameters (type T).
[error] cats.effect.IO's type parameters do not match type T's expected parameters:
[error] class IO has one type parameter, but type T has none
[error] bind[ClientRepository[IO]].to[ClientRepositoryImpl].in[Singleton]
[error] ^
and bind[ClientRepository[IO[_]]].to[ClientRepositoryImpl].in[Singleton]
Module.scala:37:27: cats.effect.IO[_] takes no type parameters, expected: one
[error] bind[ClientRepository[IO[_]]].to[ClientRepositoryImpl].in[Singleton]
[error] ^
What's the correct way to fix this?
I found the proper solution using Guice's TypeLiteral, after reading this SO answer and this one.
The working solution is:
// In Module.scala configure()
bind(new TypeLiteral[ClientRepository[IO]] {}).to(classOf[ClientRepositoryImpl])
because we must provide a class that can be instantiated (with a type parameter, that in our case is IO). TypeLiteral, which is a special class that enables you to specify a full parameterized type, can be used to create the actual binding to a particular implementation of our Repo[F[_]]. A class with a generic parameter cannot be instantiated but we can force Guice to pick up a specific ClientRepository that has been constructed with the type parameter cats.effect.IO.
Last but not least whenever you have to inject the trait ClientRepository you have to specify the type parameter as well. For instance:
class ClientResourceHandler #Inject()(
routerProvider: Provider[ClientRouter],
clientRepository: ClientRepository[IO]
)
the ClientResourceHandler needs to call the repo, so we're injecting it using the trait ClientRepository[IO] (not just ClientRepository).

How to override a TypeLiteral in Scala Guice when testing

In my Module.scala I'm binding a concrete implementation of a trait defined as follows:
trait AccessGroupRepository[F[_]] {}
#Singleton
class AccessGroupRepositoryImpl #Inject()(db: OldDataBase, c: IOContextShift)
extends AccessGroupRepository[IO] {}
and the binding is done using TypeLiteral:
bind(new TypeLiteral[AccessGroupRepository[IO]] {}).to(classOf[AccessGroupRepositoryImpl])
Now, I need to override this binding when testing with a Mockito mock:
override val application: Application = guiceApplicationBuilder
.overrides(bind(new TypeLiteral[AccessGroupRepository[IO]] {}).to(agRepoMock))
but I get the following error:
overloaded method value bind with alternatives:
[error] [T](implicit evidence$1: scala.reflect.ClassTag[T])play.api.inject.BindingKey[T] <and>
[error] [T](clazz: Class[T])play.api.inject.BindingKey[T]
[error] cannot be applied to (com.google.inject.TypeLiteral[api.v1.accessgroup.AccessGroupRepository[cats.effect.IO]])
[error] .overrides(bind(repoTypeLiteral).to(agRepoMock))
[error] ^
How could I solve that?
This question relates to How to bind a class that extends a Trait with a monadic type parameter using Scala Guice?
TypeLiteral isn't available yet in scala implementation of Play Guice API.
Current valid solution for generics, is creating a test module with desired mock definitions, and passing it within overrides:
object CustomMockComponentModule extends AbstractModule {
val agRepoMock = ...
#Provides
#Singleton
def mockBean(): AccessGroupRepository[IO] = agRepoMock
}
...
override val application: Application = guiceApplicationBuilder
.overrides(CustomMockComponentModule)
.build()

Inherited parse method conflict in scala

I'm working through some compilation errors (in Scala / Play Framework) and one I can't resolve is a conflict error. This is the error:
class Games inherits conflicting members:
[error] method parse in trait BaseControllerHelpers of type => play.api.mvc.PlayBodyParsers and
[error] lazy value parse in trait BodyParsers of type
play.api.mvc.PlayBodyParsers [error] (Note: this can be resolved by
declaring an override in class Games.)
And this is the function (or class) signature for the class:
class Games #Inject() (cc: ControllerComponents, actorSystem: ActorSystem)(val reactiveMongoApi: ReactiveMongoApi)(implicit mat: Materializer) extends AbstractController(cc) with MongoController with ReactiveMongoComponents {
In the error message you'll notice that it says that:
this can be resolved by declaring an override in class Games
But after having tried a few things - I am not sure how. If anyone has any suggestions on this or any other technique that could resolve this error please post. Thanks

scala inherited value do not find

Scala version 2.11.8
I have parent class
abstract class FR(externalId:String, code:String, message:String) extends Serializable {
val this.externalId=externalId;
val this.code = code;
val this.message = message;
def toString:String={
return "FRworks";
}
}
The child class is:
class RD extends FR {
def this(lpTransaction:LPTransaction)={
externalId =lpTransaction.getField("somethinghere").toString
...
}
}
The error is:
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=256m; support was removed in 8.0
[info] Loading project definition from F:\workspace\frankcheckAPI\project
[info] Set current project to frankcheckapi (in build file:/F:/workspace/frankcheckAPI/)
[info] Compiling 20 Scala sources to F:\workspace\frankcheckAPI\target\scala-2.11\classes...
[error] F:\workspace\frankcheckAPI\src\main\scala\com\cardaccess\fraudcheck\RD.scala:9: 'this' expected but identifier found.
[error] externalId =lpTransaction.getField("somethinghere").toString
[error] ^
[error] one error found
[error] (compile:compileIncremental) Compilation failed
when I add this in front of externalId the error still:
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=256m; support was removed in 8.0
[info] Loading project definition from F:\workspace\frankcheckAPI\project
[info] Set current project to frankcheckapi (in build file:/F:/workspace/frankcheckAPI/)
[info] Compiling 20 Scala sources to F:\workspace\frankcheckAPI\target\scala-2.11\classes...
[error] F:\workspace\frankcheckAPI\src\main\scala\com\cardaccess\fraudcheck\ReDFraudCheckResponse.scala:9: '}' expected but '.' found.
[error] this.externalId =lpTransaction.getField("somethinghere").toString
[error] ^
[error] F:\workspace\frankcheckAPI\src\main\scala\com\cardaccess\fraudcheck\ReDFraudCheckResponse.scala:12: eof expected but '}' found.
[error] }
[error] ^
[error] two errors found
[error] (compile:compileIncremental) Compilation failed
Your code is very Java-influenced. There are couple of things wrong here; when you fix the obvious ones like missing class parameters in RD it boils down to not being able to reassign to val.
Let me give you an improved, Scala-fied version of the whole code.
abstract class FR(val externalId: String, val code: String, val message: String) extends Serializable
// dummy class
class LPTransaction {
def getField(s: String) = s
}
class RD(externalId: String, code: String, message: String) extends FR(externalId, code, message) {
def this(lpTransaction: LPTransaction) = {
this(lpTransaction.getField("somethinghere").toString, "defaultCode", "defaultMessage")
}
println(externalId)
}
val a = new RD(new LPTransaction) // prints "somethinghere"
Main improvements are:
You don't need private fields to be populated using arguments in the constructor. Scala favors immutability. Make your class arguments "vals", this means they will be available as public fields (instead of getters you will access them directly; this is contrary to OOP's encapsulation principle, but here it's ok because nobody can mess with them anyway since they are immutable; they may only be fetched)
Your subclass RD should be taking same fields as parameters as its parent class. Of course, you can then define an auxiliary constructor that takes only LPTransaction, but then you need to feed the parent class with some default values for the other parameters.
The rest kind of follows from this. I added the dummy implementation of LPTransaction to be able to compile. I also threw in a println statement in RD class just for the sake of example. Feel free to ask if something's not clear.
//scala automatically generates getters for passed in params. No need to set them explicitly. For immutable params use val, for mutable use var
abstract class FR(var externalId:String, val code:String, val message:String) extends Serializable {
//need to use override annotation for superclass methods
override def toString:String={
return "FRworks";
}
}
// notice how constructor parameters are passed to the base class when defining the child class.
class RD extends FR("someID","code","msg") {
def printId() = println(externalId)
}
val x = new RD
x.externalId = "new ID" //works because externalId is var (mutable)
x.code = "new code" //error because code is val (immutable)
x.printId //correctly prints the external id: someID

SORM Persisted 'id' undefined

I am just trying to learn SORM and am playing with what I think is some simple sample code. To whit:
case class Book(var author:String, var title:String);
object Db extends Instance(
entities = Set(Entity[Book]()),
url = "jdbc:h2:mem:test",
user = "",
password = "",
initMode = InitMode.Create
)
And then:
val b : Book with Persisted = Db.save( Book("foo","bar") )
When attempting to compile this, I get:
[error] /Users/rjf/IdeaProjects/PlayTest/app/controllers/Application.scala:22: type mismatch;
[error] found : models.Book
[error] required: sorm.Persisted with models.Book
[error] val b : Book with Persisted = Db.save( Book("foo","bar") )
[error] ^
If I change the Book declaration to:
case class Book(var author:String, var title:String) extends Persisted;
I then get:
[error] /Users/rjf/IdeaProjects/PlayTest/app/models/Book.scala:17: class Book needs to be abstract, since:
[error] it has 2 unimplemented members.
[error] /** As seen from class Book, the missing signatures are as follows.
[error] * For convenience, these are usable as stub implementations.
[error] */
[error] def id: Long = ???
[error] def mixoutPersisted[T]: (Long, T) = ???
[error] case class Book(var author:String, var title:String) extends Persisted;
[error] ^
Apologies for the newbie question. No doubt that the fact I'm learning Scala at the same time is a contributing factor.
To solve your issue, just remove the explicit type annotation:
val b = Db.save( Book("foo","bar") )
Don't worry, you'll still be able to access the same interface as Book with Persisted.
As to why this happens, this seems to be a bug of Scala, and it's been reported.
A sidenote
Don't use vars in case class declaration. Immutability is the whole point of case classes. The correct declaration would be:
case class Book(author:String, title:String)
which is the same as
case class Book(val author:String, val title:String)