我想了解爲什麼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 –