I want to understand why the scala compiler cannot infer a type parameter passed to a superclass so that I can come up with a workaround. Workaround suggestions are also very welcome! Here's a contrived example of what I'm stuck on (comments in the code explaining issues):
Code is also in a scala fiddle.
/** A Svc is a function that responds to requests
* @tparam Req[_] a request ADT whose instances specify their response type
*/
trait Svc[Req[_]] {
def apply[Resp](req: Req[Resp]): Resp
}
/** Service request ADT */
sealed trait MyReq[_]
// two requests have the same response type of String (i.e. MyReq[String]):
case class GetString(id: String) extends MyReq[String]
case class GetAltString(id: String) extends MyReq[String]
// this one is the only MyReq[Int]
case class GetInt(id: String) extends MyReq[Int]
/** Type class for marshalling a response for a concrete request type.
* This lets us handle marshalling differently for different requests
* that have the same response type (such as GetString and GetAltString above).
*
* @tparam ReqImpl concrete MyReq type. This is required to enforce unique marshaller
* per request when there are mutliple request types with the same response type.
*/
trait ReqMarshaller[ReqImpl <: MyReq[Resp], Resp] {
def marshal(r: Resp): String
}
class MySvc extends Svc[MyReq] {
// this apply function compiles and works just fine.
override def apply[Resp](req: MyReq[Resp]): Resp = req match {
case GetString(id) => id
case GetAltString(id) => id + id
case GetInt(id) => id.length
}
// This is the problem. I want to specify the request is a subclass so
// we get the specific marshaller for the request type and avoid
// ambiguous implicit errors.
// However, the Resp type parameter is always inferred as Nothing
// instead of the correct response type.
def marshal[ReqImpl <: MyReq[Resp], Resp](req: ReqImpl)(
implicit
marshaller: ReqMarshaller[ReqImpl, Resp]
): String = marshaller.marshal(apply(req))
// this method is just here to show that it won't work as a solution
// because it doesn't work when there are multiple request types with
// the same response type (causes ambiguous implicits errors)
def marshalGeneric[Resp](req: MyReq[Resp])(
implicit
marshaller: ReqMarshaller[_ <: MyReq[Resp], Resp]
): String = marshaller.marshal(apply(req))
}
implicit val getIntMarshaller: ReqMarshaller[GetInt, Int] = new ReqMarshaller[GetInt, Int] {
def marshal(i: Int): String = (i * i).toString
}
implicit val getStrMarshaller: ReqMarshaller[GetString, String] = new ReqMarshaller[GetString, String] {
def marshal(s: String): String = s
}
implicit val getAltStrMarshaller: ReqMarshaller[GetAltString, String] = new ReqMarshaller[GetAltString, String] {
def marshal(s: String): String = s + s
}
val svc = new MySvc
val myLength = svc(GetInt("me")) // 2
println(s"myLength: $myLength")
svc.marshalGeneric(GetInt("me")) // compiles and works
//svc.marshal(GetInt("me")) // fails to compile due to infering Resp type as Nothing
//svc.marshalGeneric(GetAltString("me")) // fails to compile because of ambiguous implicits