我想了解为什么scala编译器无法推断传递给超类的类型参数,以便我可以想出解决方法。解决方法建议也非常受欢迎!下面是我卡住的一个人为的例子(代码中解释问题的注释):为什么scala编译器不能从超类中推断出类型参数?
代码也在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
你可以做一个scastie? – nafg
@nafg感谢您的建议。我做了一个scala小提琴:https://scalafiddle.io/sf/bGtDio1/0 –
这可能会帮助你吗? http://stackoverflow.com/questions/6682824/how-can-i-combine-the-typeclass-pattern-with-subtyping –