不清楚为什么我的范围内隐式转换不被接受为 'implicit evidence'

unclear why my in-scope implicit conversions are not accepted as 'implicit evidence'

我一直在试验隐式转换,我对使用这些的 'enrich-my-libray' 模式有相当的了解。我试图将我对基本隐式的理解与隐式证据的使用结合起来......但我误解了一些关键的东西,如下面的方法所示:

import scala.language.implicitConversions

object Moo extends App {

  case class FooInt(i: Int)
  implicit def cvtInt(i: Int) : FooInt = FooInt(i)
  implicit def cvtFoo(f: FooInt) : Int = f.i

  class Pair[T, S](var first: T, var second: S) {
    def swap(implicit ev: T =:= S, ev2: S =:= T) {
      val temp = first
      first = second
      second = temp
    }

    def dump() = {
      println("first is " + first)
      println("second is " + second)
    }
  }

  val x  = new Pair(FooInt(200), 100)
  x.dump
  x.swap
  x.dump
}

当我运行上述方法时,我得到这个错误:

    Error:(31, 5) Cannot prove that nodescala.Moo.FooInt =:= Int.
      x.swap
        ^

我很困惑,因为我认为我的范围内隐式转换就足够了 'evidence' Int 可以转换为 FooInt,反之亦然。预先感谢您让我直截了当!

更新:

在被 Peter 的出色回答弄糊涂后,下面,灯泡亮了,这是我想在 API 中使用隐含证据的一个很好的理由。我在我自己对这个问题的回答中详细说明了这一点(也在下面)。

=:= 检查两种类型是否相等,FooIntInt 肯定不相等,尽管这两种类型的值存在隐式转换。

我会创建一个 CanConvert 类型 class,它可以将 A 转换为 B :

trait CanConvert[A, B] {
  def convert(a: A): B
}

我们可以创建类型 class 实例来将 Int 转换为 FooInt,反之亦然:

implicit val Int2FooInt = new CanConvert[Int, FooInt] {
  def convert(i: Int) = FooInt(i)
}

implicit val FooInt2Int = new CanConvert[FooInt, Int] {
  def convert(f: FooInt) = f.i
}

现在我们可以在 Pair.swap 函数中使用 CanConvert :

class Pair[A, B](var a: A, var b: B) {
  def swap(implicit a2b: CanConvert[A, B], b2a: CanConvert[B, A]) {
    val temp = a
    a = b2a.convert(b)
    b = a2b.convert(temp)
  }

  override def toString = s"($a, $b)"

  def dump(): Unit = println(this)
}

我们可以将其用作:

scala> val x = new Pair(FooInt(200), 100)
x: Pair[FooInt,Int] = (FooInt(200), 100)

scala> x.swap

scala> x.dump
(FooInt(100), 200)

A =:= B is not A can be converted to B. 它是 A can be cast to B 的证据。而你没有任何地方的隐含证据表明 Int 可以转换为 FooInt 反之亦然(有充分的理由;)。

您要找的是:

def swap(implicit ev: T => S, ev2: S => T) {

完成本练习后,我想我对 为什么 想要在 API.[=12= 中使用隐式证据服务有了更好的理解]

隐含证据在以下情况下非常有用:

  • 你有一个参数化的类型class,它提供了多种方法 作用于参数给定的类型,并且
  • 当这些方法中的一种或多种仅在附加时才有意义 约束放在参数化类型上。

所以,在我最初的问题中给出的简单 API 的情况下:

class Pair[T, S](var first: T, var second: S) {
    def swap(implicit ev: T =:= S, ev2: S =:= T)  = ???
    def dump() =  ???
}

我们有一个类型 Pair,它将两个东西放在一起,我们可以随时调用 dump() 来检查这两个东西。在某些情况下,我们还可以交换对中第一项和第二项的位置。这些条件是由隐含证据约束给出的。


《Scala 编程》一书提供了 一个很好的例子来说明如何使用这种技术 在Scala集合中使用,特别是在Traversables的toMap方法上。

书上指出Map的构造函数

wants key-value pairs, i.e., two-tuples, as arguments. If we have a sequence [Traversable] of pairs, wouldn’t it be nice to create a Map out of them in one step? That’s what toMap does, but we have a dilemma. We can’t allow the user to call toMap if the sequence is not a sequence of pairs.

所以有一个类型 [Traversable] 的例子,它有一个不能在所有情况下使用的方法 [toMap]...它只能在编译器可以 'prove' 时使用(通过隐含证据)表明 Traversable 中的项目是成对的。