This seems like a simple thing but I can't understand it...
This compiles:
object CanFoo1 {
def foo(): Unit = {
println("Yup, I can foo alright")
}
}
object CanFoo2 {
def foo(): Unit = {
println("And I can foo with the best")
}
}
trait A {
type CanFoo = { def foo(): Unit }
def fooers: Seq[CanFoo]
}
class B extends A {
def fooers = Seq(
// CanFoo1, // <- won't compile when this is uncommented
CanFoo2
)
}
But uncommenting the // CanFoo1, line gives:
error: type mismatch;
found : Seq[Object]
required: Seq[B.this.CanFoo]
(which expands to) Seq[AnyRef{def foo(): Unit}]
def fooers = Seq(
^
one error found
So it seems like the compiler understands that a collection containing just one element Seq(CanFoo2) (or Seq(CanFoo1)) is of the correct type, but when both objects are in the collection it gives up? What am I doing wrong here?
So it seems like the compiler understands that a collection containing
just one element Seq(CanFoo2) (or Seq(CanFoo1)) is of the correct
type, but when both objects are in the collection it gives up? What am
I doing wrong here?
When you pass CanFoo1 or CanFoo2 to the Seq apply, the sequence is inferred to be of type CanFoo1.type or CanFoo2.type respectively, it is not inferred to be of type CanFoo.
When you pass in both elements to the Seq, the compiler tries to look for a common type to which it can validly infer to make the code compile, and the only type it can find is Object, but fooers is said to be of type Seq[CanFoo], so the compiler yells.
You can help the compiler a little by explicitly writing the type of the collection:
class B extends A {
def fooers = Seq[CanFoo](
CanFoo1,
CanFoo2
)
}
Related
I'm trying to make some functions which use both the whole Enumeration and single values chosen from an Enumeration as arguments.
It's easy to make a generic function which accepts any kind of Enumeration object as it's argument, but how can I make a generic function which accepts any single value from an Enumeration as it's argument?
object Xenu extends Enumeration {
type Xenu = Value
val foo, bar, baz = Value
}
object Yenu extends Enumeration {
type Yenu = Value
val blip, blop, blap, blep = Value
}
def doStuff[E <: Enumeration](e:E):Int = {
// Works fine
println("Got an enum: " + e.toString())
e.values.size
}
def funnyBusiness[E <: Enumeration](v: E.Value): Unit = {
// This doesn't work!
println(v)
}
doStuff(Xenu) // Works
doStuff(Yenu) // Works
funnyBusiness(Xenu.bar) // Wrong
funnyBusiness(Yenu.blip) // Wrong
Can you help me write a generic function declaration for "funnyBusiness" such that I can accept any single value from any Enumeration?
There is not much you will be able to achieve with such a signature, but here you go:
The problem in your funnyBusiness is that you use the . notation for the type E, which does not exists. To use a type-dependant type, you use the # notation, such as E#Value.
Apart from this, your function is fine:
def funnyBusiness[E <: Enumeration](v: E#Value): Unit = {
println(v)
}
And the solution turns out to be very easy:
def funnyBusiness[E <: Enumeration](v: E#Value): Unit = {
// This doesn't work!
println(v)
}
I'm trying to implement basically the same thing that is discussed here, but in my specific situation, it's not working.
Currently I have a function that validates a JSON response from our server. The problem is, it has the JSON type hardcoded into the method:
def fakeRequest[A: Writes](target: () => Call, requestObject: A): Any = {
route(FakeRequest(target()).withJsonBody(Json.toJson(requestObject))) match {
// ... stuff happens
Json.parse(contentAsString(response)).validate[GPInviteResponse]
^
Note the hardcoded GPInviteResponse type.
So, to make this a totally generic and reusable method, it would be great to pass in the type that is being validated.
I tried this:
def fakeRequest[A: Writes, B](target: () => Call, requestObject: A, responseType: B): Any = {
route(FakeRequest(target()).withJsonBody(Json.toJson(requestObject))) match {
// ... stuff happens
Json.parse(contentAsString(response)).validate[B]
^
That almost works, but I get a No Json deserializer found for type B. Makes sense, so I changed it to:
def fakeRequest[A: Writes, B: Reads](target: () => Call, requestObject: A, responseType: B): Any = {
But, now I get No Json deserializer found for type controllers.GPInviteResponse.type. So the question is: Is it possible to pass a type like this (or is there some other magic to make it work)?
There is a deserializer defined for the type... I've re-read it half a dozen times to make sure there's no typo:
case class GPInviteResponse(inviteSent: Boolean, URL: Option[String], error: Option[GPRequestError] = None) {
def this(error: GPRequestError) = this(false, None, Option(error))
}
object GPInviteResponse {
implicit val readsInviteResponse = Json.reads[GPInviteResponse]
implicit val writesInviteResponse = Json.writes[GPInviteResponse]
}
Edit
Included below is a simplified test case that demonstrates the problem. At the moment, this won't compile (error is shown below). I think I understand why it won't work (vaguely) but I have no idea regarding a solution. My theory as to why it won't work: While the provided type GPInviteRequest does have an implicit Reads/Writes method, Scala cannot make the connection that an instance B is actually a GPInviteRequest so it concludes that B does not have Reads/Writes.
case class GPInviteResponse(inviteSent: Boolean)
object GPInviteResponse {
implicit val readsInviteResponse = Json.reads[GPInviteResponse]
implicit val writesInviteResponse = Json.writes[GPInviteResponse]
}
class TestInviteServices extends PlaySpecification {
"try to validate a type" in {
tryToValidate(GPInviteRequest(true))
}
def tryToValidate[B: Reads, Writes](i: B) = {
val json = Json.toJson(i).toString
Json.parse(json).validate[B].isSuccess must beTrue
}
}
The above test yields:
[error]
/Users/zbeckman/Projects/Glimpulse/Server-2/project/glimpulse-server/test/application/TestInviteServices.scala:46:
No Json serializer found for type B. Try to implement an implicit
Writes or Format for this type. [error] val json =
Json.toJson(i).toString [error] ^ [error]
/Users/zbeckman/Projects/Glimpulse/Server-2/project/glimpulse-server/test/application/TestInviteServices.scala:133:
No Json deserializer found for type controllers.GPInviteResponse.type.
Try to implement an implicit Reads or Format for this type. [error]
fakeRequest(controllers.routes.GPInviteService.invite, i,
GPInviteResponse) match { [error] ^
Your error message:
No Json serializer found for type B. Try to implement an implicit Writes or Format for this type.
In this function, how is the toJson method supposed to know how to serialize your type B?
def tryToValidate[B: Reads, Writes](i: B) = {
val json = Json.toJson(i).toString
Json.parse(json).validate[B].isSuccess must beTrue
}
You haven't pulled in a writer/reader into scope, the compiler doesn't know where to look for one and that's why it's telling you to implement one. Here's a quick solution
case class GPInviteResponse(inviteSent: Boolean)
object GPInviteResponse {
implicit val format = Json.format[GPInviteResponse]
}
def tryToValidate[B](i: B)(implicit format: Format[B]) = {
val json = Json.toJson(i).toString
Json.parse(json).validate[B]
}
Note: using the format method is equivalent to defining both a reads and writes.
Now, there is an implicit formatter for B in scope, so the compiler knows where to find it and injects it into the validate method which takes a reader implicitly:
// From play.api.libs.json
def validate[T](implicit rds: Reads[T]): JsResult[T] = rds.reads(this)
Edit
You can add type parameters to your function and then reference them in the validate[T] method, like so:
// Define another case class to use in the example
case class Foo(bar: String)
object Foo {
implicit val format = Json.format[Foo]
}
def tryToValidate[B, C](implicit f1: Format[B], f2: Format[C]) = {
val j1 = """{"inviteSent":true}"""
val j2 = """{"bar":"foobar"}""" //
Json.parse(j1).validate[B]
Json.parse(j2).validate[C]
}
// Example call
tryToValidate[GPInviteResponse, Foo]
Try it this way :
def tryToValidate[B](i: B)(implicit format : Format[B]) = {
val json = Json.toJson(i).toString
Json.parse(json).validate[B].isSuccess must beTrue
}
I am using a library which has a class that has a generic type that can be quite complicated. I need to write a method that takes a parameter with the generic type that a val of the library class has, and I would like to avoid having to write out the type in the method signature. I thought I might be able to create an implicit class which adds a type to the val that I could use in the method signature, kind of like:
// This comes from a library and can't be changed
case class LibraryClass[A](a: A)
//----------------------------------
object MyCode {
val thing = LibraryClass(3)
implicit class LibraryClassWithType[A](lc: LibraryClass[A]) {
type TheType = A
}
def doStuff(something: thing.TheType): Unit = {
println(something)
}
}
This does not compile (TheType is not a member of LibraryClass). But if I wrap it in the class myself, it works
val thingWithType = LibraryClassWithType(thing)
def doStuff(something: thingWithType.TheType): Unit = {
println(something)
}
Is there something I am missing that will make this work, or is this kind of implicit conversion not valid Scala?
I haven't been able to do this sort of thing with implicits, but I have had to do something similar where I just instantiated these sorts of type holders:
case class LibraryClass[A](a: A)
object MyCode {
val thing = LibraryClass(3)
class HigherTypeHolder[A,F[A]](a: F[A]) {
type AT = A
}
val th = new HigherTypeHolder(thing)
def doStuff(something: th.AT): Unit = {
println(something)
}
}
You can do what (I think) you want like this:
implicit val thing = LibraryClass(3)
def doStuff[A](something: A)(implicit lc: LibraryClass[A])
What I don't understand is why this needs to be so complicated. Why not for example stick with your second approach, without implicits, that works, or why not just do
def doStuff[A](something: A) to begin with?
I'm writing a Play! 2.1 application using ReactiveMongo. each persistable case class has an object that holds 2 implicit objects, implementing BSONReader[...] and BSONWriter[...], and each case class has methods to return these:
trait Persistable {
implicit def getReader: BSONReader[Persistable]
implicit def getWriter: BSONWriter[Persistable]
val collectionName: String
}
case class MyObj () extends Persistable {
override val collectionName: String = MyObj.collectionName
override def getReader: BSONReader[MyObj] = MyObj.MyObjBSONReader
override def getWriter: BSONWriter[MyObj] = MyObj.MyObjBSONWriter
}
object MyObj{
val collectionName: String = "MyObj"
implicit object MyObjBSONReader extends BSONReader[MyObj] {
def fromBSON(document: BSONDocument): MyObj = {
val doc = document.toTraversable
new MyObj(
)
}
}
implicit object MyObjBSONWriter extends BSONWriter[MyObj] {
def toBSON(myObj: MyObj) = {
BSONDocument(
)
}
}
for some reason, getReader seems to work fine, but getWriter errors:
overriding method getWriter in trait Persistable of type =
reactivemongo.bson.handlers.BSONWriter[models.persistable.Persistable];
method getWriter has incompatible type
what am i doing wrong? both seem to have similar signatures.
another hint is that if i remove the return type from getWriter, i get complie time error in eclipse:
type mismatch; found : models.persistable.MyObj.MyObjBSONWriter.type required:
reactivemongo.bson.handlers.BSONWriter[models.persistable.Persistable]
UPDATE:
I did as #AndrzejDoyle said below, but then the implementation of Persister, which was the heart of this exercise, complains:
def insert(persistable: Persistable) = {
val collection = db(persistable.collectionName)
import play.api.libs.concurrent.Execution.Implicits._
implicit val reader = persistable.getReader
implicit val writer = persistable.getWriter
collection.insert(persistable)
}
error:
trait Persistable takes type
parameters
It is due to covariance and contravariance.
The mongodb reader is defined as BSONReader[+DocumentType]. The + in the generic parameter, means that this class is covariant in that parameter. Or more fully,
If B is a subclass of A, then BSONReader[B] is a subclass of BSONReader[A].
Therefore you can use a BSONReader[MyObj] everywhere that a BSONReader[Persistable] is required.
On the other hand, the writer is contravariant: BSONWriter[-DocumentType]. This means that
If B is a subclass of A, then BSONWriter[B] is a superclass of BSONWriter[A].
Therefore your BSONWriter[MyObj] is not a subclass of BSONWriter[Persistable], and so cannot be used in its place.
This might seem confusing initially (i.e. "why does contravariance make sense when it's 'backwards'?"). However if you think about what the classes are doing, it becomes clearer. The reader probably produces some instance of its generic parameter. A caller then might expect it to produce a Persistable - if you have a version that specifically produces MyObjs instead then this is fine.
The writer on the other hand, is probably given an object of its generic parameter. A caller with a BSONWriter[Persistable] will call the write() method, passing in an instance of Persistable to be written. Your implementation can only write instances of MyObj, and so it doesn't actually match the interface. On the other hand, a BSONWriter[Object] would be a subclass of any BSONWriter, since it can (from a type perspective) accept any type as an argument.
The fundamental problem seems to be that your Persistable trait is looser than you intended. You probably want each implementation to return a reader and writer parameterized on itself, rather than on Persistable in general. You can achieve this via self-bounded generics:
trait Persistable[T <: Persistable[T]] {
implicit def getReader: BSONReader[T]
implicit def getWriter: BSONWriter[T]
val collectionName: String
}
and then declare the class as MyObj[MyObj]. Now the reader and writer are expected to be parameterised on MyObj, and your existing implementations will compile.
Given a parameterized type:
trait Document[S]
I want to bind an instance of this for an embedded interpreter, e.g.
def test[S](doc: Document[S]) = tools.nsc.interpreter.NamedParam("document", doc)
This wants both a TypeTag and a ClassTag of Document[S]. Note that I need to be able to bind the full type, i.e. Document[S] and not just Document[_].
How would I go about this? I imagine I would add something to document, e.g.
trait Document[S] {
def tt: reflect.runtime.universe.TypeTag[Document[S]] = ???
def ct: reflect.runtime.universe.ClassTag[Document[S]] = ???
}
(why the heck do I need two different tags to get the named parameter?)
EDIT: The following makes it compile
trait Document[S] {
implicit def systemType: reflect.runtime.universe.TypeTag[S]
}
def test[S](doc: Document[S]) = {
import doc.systemType
tools.nsc.interpreter.NamedParam("document", doc)
}
But I still end up with a binding to Document[_] in the interpreter, so my type parameter to Document is lost?!