Reputation: 594
I'm trying to avoid to use a generic on Request
that contains an optional generic Body
to avoid to pass a generic everywhere when it's only needed when writing the body, and also because there's no type when body is not defined:
case class Body[A](content: A)(implicit val bodyWritable: BodyWritable[A])
case class Request(url: String, body: Option[Body[_]])
private def executeRequest(request: Request) = {
val wsClient: StandaloneWSClient = ???
val requestWithUrl = wsClient.url(request.url)
request.body.fold(requestWithUrl)(body => requestWithUrl.withBody(body.content)(body.bodyWritable))
}
The compilation failed with:
Error:(20, 90) type mismatch;
found : play.api.libs.ws.BodyWritable[(some other)_$1(in value body)]
required: play.api.libs.ws.BodyWritable[_$1(in value body)]
I'm wondering if there's a way to not type request.
I'm using "com.typesafe.play" %% "play-ws-standalone" % "2.0.4"
Upvotes: 0
Views: 477
Reputation: 15294
In short you cant do this way. When wildcard is involved, the type parameter of an expression is always different from others, even they come from the same variable. The type A body.content
and the type parameter A of body.bodyWritable
are resolved as different local anonymous types, even though they come from the same variable body
, but Scala does not know that, because they are separate expressions.
To resolve this, the most type-safe way (and recommended) is to add type parameter to Request and executeRequest to ensure the type is resolved as the same.
You can also create a local method that takes a type parameter to make sure the two expressions shares the same generic type:
private def executeRequest(request: Request) = {
val wsClient: StandaloneWSClient = ???
val requestWithUrl = wsClient.url(request.url)
def f[A](body: Body[A]) = requestWithUrl.withBody(body.content)(body.bodyWritable)
request.body.fold(requestWithUrl)(body => f(body)) // or shorten to request.body.fold(requestWithUrl)(f)
}
Alternatively, I sometimes move the calling site into the class where the type parameter is declared, in which the type parameter is guaranteed to be the same. Something like:
case class Body[A](content: A)(implicit val bodyWritable: BodyWritable[A]) {
def getRequest(req: WSRequest) = req.withBody(content)
}
case class Request(url: String, body: Option[Body[_]])
private def executeRequest(request: Request) = {
val wsClient: StandaloneWSClient = ???
val requestWithUrl = wsClient.url(request.url)
request.body.fold(requestWithUrl)(body => body.getRequest(requestWithUrl))
}
Upvotes: 2