Scala隐式参数需要重复吗?

Scala implicit parameters need to be repeated?

我有以下简单的测试用例:

trait Printable[T] {
  val string: String
  def print() = println("It works: " + string)
}

def foo[T](x: T)(implicit ev: T => Printable[T]) = {
  x.print()
}

implicit def doubleToPrint(x: Double): Printable[Double] = new Printable[Double] {
  override val string: String = x.toString
}

foo(2.0) // => It works: 2.0

但是,创建一个应该调用 foo(x) 的新函数 bar[T](x: T) 会引发错误,即 T => Printable[T] 中没有可用的隐式视图:

// this does not work
def bar[T](x: T) = {
  println("Some other stuff")
  foo(x)
}

但是,当添加相同的参数时,它当然有效:

// this does work
def bar[T](x: T)(implicit ev: T => Printable[T]) = {
  println("Some other stuff")
  foo(x)
}

在我看来,继续链接这些隐式参数非常麻烦,因为我假设 foo 函数将开始搜索隐式转换,而不是函数 bar 调用 foo。据我了解,当从 foo.

中调用时,implicit def doubleToPrint 也应该在 foo 的范围内

我忽略了什么?

回复dth

的补充问题

所以我明白了答案,这实际上是非常合乎逻辑的。 但是,上下文边界的解决方案在 foobar 都是另一个特征的一部分的情况下不起作用,因为特征中不允许上下文边界:

// not allowed to do trait FooBar[T : Printable]
trait FooBar[T] {
    def foo(x: T)(implicit ev: T => Printable[T]) = {
        println("Running foo...")
        x.print()
    }

    // this is not allowed
    def bar(x: T) = {
        println("But running bar first...")
        foo(x)
    }
}

那么不使用隐式参数也可以解决这个问题吗?

我自己的,不太好,解决方案

我得出的结论是我的特定代码中实际上不需要特征,可以用 abstract class FooBar[T <% Printable[T]].

替换 FooBar[T] 特征

这是我的问题的解决方案,但不是我添加的附加问题。

想一想,隐式参数是如何工作的:

当您在某处调用需要隐式参数的方法时,编译器会在上下文(作用域、涉及的类型)中查找合适的隐式参数。

现在看看你对 bar 的定义:

def bar[T](x: T) = {
  foo(x)
}

当您调用 foo 时,编译器会查找它找不到的 T => Printable[T] 类型的隐式值。没关系,稍后您将使用 Double 类型调用 bar,因为它不知道。

所以答案是肯定的,你必须到处传递隐式参数,你在上下文中找不到合适的值(所以通常到处都是,你不知道具体类型)。

但是有一个叫做上下文边界的语法糖,所以你可以这样定义 bar:

def bar[T: Printable](x: T) = foo(x)

您也可以像这样定义 foo 并使用 implicitly[Printable[T]] 访问这些值。因此,在这样简单的设置中,您完全不必担心隐式参数。

特质

上下文边界只是隐式参数的语法糖。隐式值在您定义类型绑定的位置传递。 IE。如果它是方法的类型参数,则将其传递给方法。

特征可能没有任何构造函数参数,因为由于线性化,您不知道它在继承层次结构中的最终位置。因此它可能没有任何隐式参数或类型界限。

所以你真的必须为这两个方法添加一个隐式参数。 如果你正在实现一些复杂的 API 这很好,如果这在用户代码中出现很多,也许你可以改变你的设计。

您可以为 classes 的类型参数使用上下文边界,但是隐式参数不会直接在 class 的方法内部使用。为此,您必须像这样提供本地隐式转换:

class FooBar[T: Printable] {

    implicit def conv(x: T) = implicitly[Printable[T]]

    def foo(x: T) = {
        println("Running foo...")
        x.print()
    }
    def bar(x: T) = {
        println("But running bar first...")
        foo(x)
    }
}

查看范围

还有视图边界作为替代方案。然而,它们已被弃用。如果你 运行 带有 -Xfuture 的 scalac 它会告诉你。

这里的问题是类型T
意思是,没有

(implicit ev: T => Printable[T]) 

编译器试图找到适用于任何类型 T 的隐式 他不能,因为只有 Double.

有隐式

但是如果你加上

(implicit ev: T => Printable[T]) 

编译器试图在你调用 bar 的地方找到隐含的。 如果你用 Double 参数调用它,他会接受 doubleToPrint 并传递给它。