0

我一直在尝试隐式转换,并且我对使用这些的“丰富我的图书馆”模式有一个不错的理解。我试图将我对基本隐含的理解与隐含证据的使用结合起来......但我误解了一些至关重要的东西,如下面的方法所示:

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
        ^

我很困惑,因为我会认为我的范围内隐式转换将是足够的“证据”,即 Int 可以转换为 FooInt,反之亦然。提前感谢您让我明白这一点!

更新:

在对下面彼得的出色回答感到困惑之后,灯泡亮了,这是您希望在 API 中使用隐式证据的一个很好的理由。我在我自己对这个问题的回答中详细说明了这一点(也在下面)。

4

3 回答 3

5

=:=检查这两种类型是否相等并且FooInt绝对Int不相等,尽管这两种类型的值存在隐式转换。

我会创建一个可以将 a 转换为 a的CanConvert类型类:AB

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

我们可以创建类型类实例以转换IntFooInt,反之亦然:

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
}

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

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)
于 2015-08-30T10:47:34.550 回答
3

A =:= B不是A 可以转换为 B 的证据。它是 A 可以转换为 B 的证据。而且你没有cast任何隐含的证据表明 Int 可以转换为 FooInt 反之亦然(有充分的理由;)。

您正在寻找的是:

def swap(implicit ev: T => S, ev2: S => T) {
于 2015-08-30T10:50:16.870 回答
0

在完成这个练习之后,我想我对为什么要在 API 中使用隐式证据服务有了更好的理解。

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

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

因此,对于我最初的问题中给出的简单 API:

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

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


Programming in Scala 这本书给出了一个很好的例子,说明了如何在 Scala 集合中使用这种技术,特别是 Traversables 的 toMap 方法。

书中指出 Map 的构造函数

想要键值对,即二元组,作为参数。如果我们有一个序列 [Traversable] 对,那么用它们一步创建一个 Map 不是很好吗?这就是 toMap 所做的,但我们有一个两难的选择。如果序列不是对序列,我们不能允许用户调用 toMap。

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

于 2015-08-30T20:58:28.197 回答