Home > Software design >  Scala Implicit Parameters Projection Conflict , "Ambigious Implicit Values" Error
Scala Implicit Parameters Projection Conflict , "Ambigious Implicit Values" Error

Time:12-31

I have been reading Bruno's TypeClasses paper and he mentioned that implicits in the argument list are projected/propagated into the implicit scope. I followed the example with this code:

package theory

import cats.implicits.toShow

import java.io.PrintStream
import java.util.Date

object Equality extends App {
  import cats.Eq

//  assert(123 == "123")
  println(Eq.eqv(123, 123).show)

  implicit val out: PrintStream = System.out

  def log(m: String)(implicit o: PrintStream ): Unit =
    o.println(m)

  def logTime(m: String)(implicit  o: PrintStream): Unit =
    log(s"${new Date().getTime} : $m")

}

The kicker is that this code will not compile, with:

ambiguous implicit values:
 both value out in object Equality of type java.io.PrintStream
 and value o of type java.io.PrintStream
 match expected type java.io.PrintStream
    log(s"${new Date().getTime} : $m")

So, I assume that the compiler sees 2 instances of the same implicit and complains. I was able to silence the compiler by explicitly adding the PrintStream passed as an argument as the second argument to log:

def logTime(m: String)(implicit  o: PrintStream): Unit =
    log(s"${new Date().getTime} : $m")(o)

This works, but am I missing something? Why is there confusion inside the body of logTime()? I thought Bruno was implying that the implicit from the caller would be projected into the scope of the method. His example does not add the extra parameter to the log() call. Why does scalac see these as 2? I suppose I assumed that the implicit from the outer method would "hide" the val out. Not so.

If anyone can explain why I see this, it would be appreciated.

CodePudding user response:

Recall that the value of an implicit parameter is determined at the call site. That's why...

Equality.log("log this")

...won't compile unless an implicit value of the appropriate type is brought into scope.

implicit val ps: PrintStream = ...
Equality.log("log this")

The logTime() definition code is a call site for the log() method and, since it is defined within the Equality object, it has the implicit val out value available to it. But it is also the recipient of the implicit o value of the same type that was passed from its call site. Thus the ambiguity. Should the compiler send the implicit out value or the implicit o value to the log() method?

Now, it might seem a bit odd that the received implicit value (from the call site) is both assigned to a local identifier, o, and inserted into the local implicit namespace as well. It turns out that Scala-3 has modified that behavior and your code compiles without error, even without the new given/using syntax. (I assume the implicit out value is passed to the log() method and not the received o value.)

  • Related