2016-09-22 105 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。

相关问题