简体   繁体   中英

Scala ignore generic type

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"

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))
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM