2016-09-22 78 views
0

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

你可以做一個scastie? – nafg

+0

@nafg感謝您的建議。我做了一個scala小提琴:https://scalafiddle.io/sf/bGtDio1/0 –

+0

這可能會幫助你嗎? http://stackoverflow.com/questions/6682824/how-can-i-combine-the-typeclass-pattern-with-subtyping –

回答

4

的問題是,Scala是試圖以代替一次的推斷ReqImpl最先得到來自Resp推斷兩者ReqImplResp類型參數。由於Resp實際上並未出現在參數列表中,因此推斷爲Nothing,然後違反了Scala通知類型邊界。一種解決方法(我不記得在那裏我看到它第一次)是給同等類型req,但其中一個依賴於Resp明確:

def marshal[ReqImpl <: MyReq[Resp], Resp](req: ReqImpl with MyReq[Resp])(
    implicit marshaller: ReqMarshaller[ReqImpl, Resp] 
): String = marshaller.marshal(apply(req)) 

svc.marshal(GetInt("me"))現在編譯。

+0

這解決了編譯器推斷「Resp」類型爲「Nothing」 - 但我是仍然得到'svc.marshalGeneric(GetAltString(「me」))'含糊不清的含義的編譯器錯誤'。但是,你已經回答了我的問題。謝謝! –

+0

對於'marshalGeneric(GetAltString(「me」))''你確實有不明確的含義:'getStrMarshaller'和'getAltStrMarshaller'都是合適的。你想讓它做一些與固定的「元帥」不同的東西嗎? –

+0

當然你是對的。我忘了將'marshalGeneric(GetAltString(「me」))''改回'marshal(GetAltString(「me」))''。再次感謝! –

0

我想你會需要捕獲的Req類型參數,並且您apply功能Param類型之間的關係在你的SVC特質。然後你可以相應地修改剩下的東西。

trait Svc[Req[_ <: XX], XX] { 
    def apply[Resp <: XX](req: Req[Resp]): Resp 
} 
0

這樣做的一種方法是明確提及您的ReqImpl是參數化類型(Type infered to Nothing in Scala)。在你的情況下,它看起來就像這樣:

def marshal[ReqImpl[Resp] <: MyReq[Resp], Resp](req: ReqImpl[Resp])(
    implicit 
    marshaller: ReqMarshaller[ReqImpl[Resp], Resp] 
): String = marshaller.marshal(apply(req)) 

但有兩個問題的方法:

(1)在svc.marshal(GetInt("me"))斯卡拉將推斷RepImpl類型爲MyReq[Int],哪一種意義,但ReqMarshaller[GetInt, Int]不匹配。所以,你需要把它定義爲:

implicit val getIntMarshaller = new ReqMarshaller[MyReq[Int], Int] { 
    def marshal(i: Int): String = (i * i).toString 
} 

(2)現在你馬上有另外一個問題,你不能在同一時間定義兩個ReqMarshaller[MyReq[String], String]。也許用相同的類型參數定義兩個端點是一個壞主意(只是一個猜測,但某些東西不適合這裏,它不適用於Alexey Romanov的解決方案)。

UPDATE

(1)是通過使ReqMarshaller協變解決:

trait ReqMarshaller[+ReqImpl <: MyReq[Resp], Resp] { ... 

(2)仍然失敗,曖昧implicits。

相關問題