我一直在阅读Bruno的TypeClasses论文,他提到参数列表中的隐词被投影/传播到隐式范围中。我以这个代码为例:
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")
}
关键是该代码将不编译,带有:
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")
因此,我假设编译器看到了两个相同的隐式实例并进行了抱怨。通过显式添加作为参数传递的PrintStream作为日志的第二个参数,我可以使编译器静音:
def logTime(m: String)(implicit o: PrintStream): Unit =
log(s"${new Date().getTime} : $m")(o)
这很管用,但我是不是遗漏了什么?为什么logTime((的主体内部存在混乱?我认为Bruno是在暗示来自调用者的隐式将被投影到方法的范围中。他的例子没有将额外的参数添加到log((调用中。为什么scalac把这些看作2?我想我假设从外部隐含的方法将";隐藏";val out。并非如此。
如果有人能解释我为什么看到这个,我将不胜感激。
回想一下,隐式参数的值是在调用站点确定的。这就是为什么。。。
Equality.log("log this")
除非将适当类型的隐式值引入作用域,否则不会编译。
implicit val ps: PrintStream = ...
Equality.log("log this")
logTime()
定义代码是log()
方法的调用站点,由于它是在Equality
对象中定义的,因此它具有可用的implicit val out
值。但它也是从其调用站点传递的相同类型的implicit o
值的接收方。因此产生了歧义。编译器应该向log()
方法发送implicit out
值还是implicit o
值?
现在,接收到的隐式值(来自调用站点(同时被分配给本地标识符o
、和,并插入到本地隐式命名空间中,这似乎有点奇怪。事实证明,Scala-3已经修改了这种行为,即使没有新的given
/using
语法,您的代码也能顺利编译。(我假设implicit out
值被传递给log()
方法,而不是接收到的o
值。(