irregular irregular - 3 months ago 23
Scala Question

Scala multiple implicit parameters with defaults resulting in ambiguous values

I've been running into issues refactoring common code out of the 3 methods and into

makeRequest()
but I get ambiguous implicit matching from the compiler. I am not sure if this is from having defaults for the implicit methods or some other issue but my goal is for getRequest/deleteRequest/postRequest can simply call makeRequest("GET")/makeRequest("DELETE")/makeRequest("POST"). Previously none of the parameters were implicit, I'm just attempting to reach the goal by using 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")


I get this and same with 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")

Answer

I think you should revisit using all these implicit unless you are doing some really funky DSL.

Here is one way to solve the problem you are having. As you may have guessed, implicit work on the Type not the name, so having two implicit with the same type is a No-No.

Since Scala 2.10, Scala allows you to "inline" classes using AnyVal (SIP-15). Here is an example of a so called value class:

case class Path(p: String) extends AnyVal

Now instead of using Strings to represent your entities you enclose them in this nice "wrapper" class. The cool thing about this is that the compiler will take care of replacing all Path objects in your code with a String during compilation time. So in terms of compiled code, you get the same performance as if you used a String. You gain a lot of type safety without paying a runtime penalty.

Now coming back to your case, here is how to solve the problem you have:

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")

Here is how to use it:

scala> foo
res0: String = ab
Comments