具有默认值的 Scala 多个隐式参数导致不明确的值
Scala multiple implicit parameters with defaults resulting in ambiguous values
我已经 运行 解决了将公共代码从 3 种方法重构到 makeRequest()
的问题,但我从编译器中得到了模糊的隐式匹配。我不确定这是由于隐式方法的默认值还是其他一些问题,但我的目标是 getRequest/deleteRequest/postRequest 可以简单地调用 makeRequest("GET")/makeRequest("DELETE")/makeRequest ("POST")。以前 none 的参数是隐式的,我只是试图通过使用 implicits
来达到目标
def makeRequest(method: String)(implicit path: String, base: String, params: Seq[(String, String)], body: Option[String], retriesLeft: Int): Future[WSResponse] = ???
def getRequest()(implicit path: String, base: String = baseUrl, params: Seq[(String, String)] = Seq(), body: Option[String] = None, retriesLeft: Int = retries): Future[WSResponse] = makeRequest("GET")
def deleteRequest()(implicit path: String, base: String = baseUrl, params: Seq[(String, String)] = Seq(), body: Option[String] = None, retriesLeft: Int = retries): Future[WSResponse] = makeRequest("GET")
def postRequest[T]()(path: String, body: T, base: String = baseUrl, params: Seq[(String, String)] = Seq(), retriesLeft: Int = retries)
(implicit wrt: play.api.http.Writeable[T], ct : play.api.http.ContentTypeOf[T]): Future[WSResponse] = makeRequest("POST")
我明白了,和 deleteRequest 一样
ambiguous implicit values:
[error] both value base of type String
[error] and value path of type String
[error] match expected type String
[error] def getRequest()(implicit path: String, base: String = baseUrl, params: Seq[(String, String)] = Seq(), body: Option[String] = None, retriesLeft: Int = retries): Future[WSResponse] = makeRequest("GET")
我认为你应该重新使用所有这些隐式,除非你正在做一些非常时髦的 DSL。
这是解决您遇到的问题的一种方法。正如您可能已经猜到的那样,隐式作用于类型而不是名称,因此有两个具有相同类型的隐式是一个禁忌。
从 Scala 2.10 开始,Scala 允许您使用 AnyVal (SIP-15) "inline" classes。这是所谓的值 class:
的示例
case class Path(p: String) extends AnyVal
现在不再使用字符串来表示您的实体,而是将它们封装在这个漂亮的 "wrapper" class 中。最棒的是,编译器会在编译期间用字符串替换代码中的所有 Path 对象。因此,就编译代码而言,您可以获得与使用字符串相同的性能。您可以获得很多类型安全性,而无需付出运行时间的代价。
现在回到你的案例,这里是解决你遇到的问题的方法:
case class Path(s: String) extends AnyVal
case class BaseUrl(s: String) extends AnyVal
def foo(implicit a: Path, b: BaseUrl) = a.s ++ b.s
implicit val a: Path = Path("a")
implicit val b: BaseUrl = BaseUrl("b")
使用方法如下:
scala> foo
res0: String = ab
正如 Marios 所说,问题在于您正在对 String 和 Int 等类型使用隐式。由于隐式作用于类型而非名称,因此编译器不知道将 'implicit String' 放在哪里。
这就是应该使用自定义类型的原因。我从上次 ScalaDays 会议上学到的一件事是你应该为所有东西创建类型,因为编译器可以帮助你验证你的程序是否正确。
但是,看看你的解决方案,我根本不会使用隐式。最好使用单个参数列表并为所有或大多数值提供默认值。然后 'makeRequest' 可以默认执行 'Get /' 并且可以通过提供 'path = "/search"' 或 'method = "POST"' 来更改此行为。
也对 http 方法使用某种作用域类型,因为您已经知道什么是有效值以及您希望支持哪些值。
我已经 运行 解决了将公共代码从 3 种方法重构到 makeRequest()
的问题,但我从编译器中得到了模糊的隐式匹配。我不确定这是由于隐式方法的默认值还是其他一些问题,但我的目标是 getRequest/deleteRequest/postRequest 可以简单地调用 makeRequest("GET")/makeRequest("DELETE")/makeRequest ("POST")。以前 none 的参数是隐式的,我只是试图通过使用 implicits
def makeRequest(method: String)(implicit path: String, base: String, params: Seq[(String, String)], body: Option[String], retriesLeft: Int): Future[WSResponse] = ???
def getRequest()(implicit path: String, base: String = baseUrl, params: Seq[(String, String)] = Seq(), body: Option[String] = None, retriesLeft: Int = retries): Future[WSResponse] = makeRequest("GET")
def deleteRequest()(implicit path: String, base: String = baseUrl, params: Seq[(String, String)] = Seq(), body: Option[String] = None, retriesLeft: Int = retries): Future[WSResponse] = makeRequest("GET")
def postRequest[T]()(path: String, body: T, base: String = baseUrl, params: Seq[(String, String)] = Seq(), retriesLeft: Int = retries)
(implicit wrt: play.api.http.Writeable[T], ct : play.api.http.ContentTypeOf[T]): Future[WSResponse] = makeRequest("POST")
我明白了,和 deleteRequest 一样
ambiguous implicit values:
[error] both value base of type String
[error] and value path of type String
[error] match expected type String
[error] def getRequest()(implicit path: String, base: String = baseUrl, params: Seq[(String, String)] = Seq(), body: Option[String] = None, retriesLeft: Int = retries): Future[WSResponse] = makeRequest("GET")
我认为你应该重新使用所有这些隐式,除非你正在做一些非常时髦的 DSL。
这是解决您遇到的问题的一种方法。正如您可能已经猜到的那样,隐式作用于类型而不是名称,因此有两个具有相同类型的隐式是一个禁忌。
从 Scala 2.10 开始,Scala 允许您使用 AnyVal (SIP-15) "inline" classes。这是所谓的值 class:
的示例case class Path(p: String) extends AnyVal
现在不再使用字符串来表示您的实体,而是将它们封装在这个漂亮的 "wrapper" class 中。最棒的是,编译器会在编译期间用字符串替换代码中的所有 Path 对象。因此,就编译代码而言,您可以获得与使用字符串相同的性能。您可以获得很多类型安全性,而无需付出运行时间的代价。
现在回到你的案例,这里是解决你遇到的问题的方法:
case class Path(s: String) extends AnyVal
case class BaseUrl(s: String) extends AnyVal
def foo(implicit a: Path, b: BaseUrl) = a.s ++ b.s
implicit val a: Path = Path("a")
implicit val b: BaseUrl = BaseUrl("b")
使用方法如下:
scala> foo
res0: String = ab
正如 Marios 所说,问题在于您正在对 String 和 Int 等类型使用隐式。由于隐式作用于类型而非名称,因此编译器不知道将 'implicit String' 放在哪里。 这就是应该使用自定义类型的原因。我从上次 ScalaDays 会议上学到的一件事是你应该为所有东西创建类型,因为编译器可以帮助你验证你的程序是否正确。
但是,看看你的解决方案,我根本不会使用隐式。最好使用单个参数列表并为所有或大多数值提供默认值。然后 'makeRequest' 可以默认执行 'Get /' 并且可以通过提供 'path = "/search"' 或 'method = "POST"' 来更改此行为。
也对 http 方法使用某种作用域类型,因为您已经知道什么是有效值以及您希望支持哪些值。