Scala多个隐式参数,默认值导致值不明确

Scala多个隐式参数,默认值导致值不明确,scala,implicit,Scala,Implicit,我在将常用代码从3个方法中重构为makeRequest()时遇到了一些问题,但我从编译器中得到了模糊的隐式匹配。我不确定这是因为隐式方法有默认值还是其他问题,但我的目标是getRequest/deleteRequest/postRequest可以简单地调用makeRequest(“GET”)/makeRequest(“DELETE”)/makeRequest(“POST”)。以前没有一个参数是隐式的,我只是试图通过使用隐式来达到目标 def makeRequest(method: String)

我在将常用代码从3个方法中重构为
makeRequest()
时遇到了一些问题,但我从编译器中得到了模糊的隐式匹配。我不确定这是因为隐式方法有默认值还是其他问题,但我的目标是getRequest/deleteRequest/postRequest可以简单地调用makeRequest(“GET”)/makeRequest(“DELETE”)/makeRequest(“POST”)。以前没有一个参数是隐式的,我只是试图通过使用隐式来达到目标

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")
我得到这个和删除请求相同

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

这里有一种方法可以解决您遇到的问题。正如您可能已经猜到的,隐式处理的是类型而不是名称,因此使用相同类型的两个隐式是不可能的

自Scala2.10以来,Scala允许您使用AnyVal()来“内联”类。下面是一个所谓的值类示例:

case class Path(p: String) extends AnyVal
现在不再使用字符串来表示实体,而是将它们封装在这个漂亮的“包装器”类中。关于这一点,最酷的是编译器会在编译期间用字符串替换代码中的所有路径对象。因此,就编译代码而言,您可以获得与使用字符串相同的性能。您可以在不支付运行时罚款的情况下获得大量类型安全性

现在回到您的案例,以下是如何解决您的问题:

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等类型使用隐式。由于隐式处理的是类型而不是名称,编译器不知道将“隐式字符串”放在何处。 这就是为什么应该使用自定义类型。我从上一次ScalaDays会议上学到的一点是,您应该为所有内容创建类型,因为编译器可以帮助您验证您的程序是否正确

然而,看看你的解决方案,我根本不会使用隐式。最好使用单个参数列表,并为所有或大多数值提供默认值。然后,“makeRequest”可以默认执行“Get/”,并且可以通过提供“path=“/search”或“method=”POST”来更改此行为


还可以为http方法使用某种类型的作用域类型,因为您已经知道什么是有效值以及要支持哪些值。

一个明显的危险信号是,对于
字符串
,您需要一个
隐式
-您应该尽可能避免为常见类型定义隐式。。。也就是说,要调试不明确的隐式,您需要查看上述方法的调用站点并找到范围内的所有隐式,因此您提供给我们的信息是不够的。@Alec,我将在这篇评论后在帖子中对此进行澄清,但之前getRequest/deleteRequest/postRequest只有显式参数,我正试图将这3个方法中的公共代码重构为makeRequest,这样我就可以使每个方法都只调用makeRequest(“GET”)。为了做到这一点,我试图使用implicitsImplicits只按类型搜索,而不是按名称搜索,因此使用
String
将无法按预期工作。@reactormah。那么,在这种情况下,在getRequest可以调用
makeRequest(“GET”)
之前尝试进行抽象是不可能的吗?当然可以,但是您必须将所有内容包装到一个case类中。我仍然不会这样做,因为这样会使代码更难阅读。包装类型的技巧非常有用!现在我将坚持使用非隐式值,因为为每个参数添加case类对我来说就像锅炉板代码。这无疑让我对它的工作方式有了更多的了解,谢谢