不清楚为什么我的范围内隐含转换不被接受为“隐含证据”

时间:2015-08-30 10:07:35

标签: scala implicit-conversion typeclass implicits

我一直在尝试隐式转换,而且我对使用这些转换的'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
        ^

我很困惑,因为我认为我的范围内隐含转换足以“证明”Int可以转换为FooInt,反之亦然。在此先感谢我让我直截了当!

更新:

在下面的彼得的优秀答案中没有注意到之后,灯泡继续为我提供了一个很好的理由,你想在你的API中使用隐式证据。我在回答这个问题时也详细说明了这个问题(也在下面)。

3 个答案:

答案 0 :(得分:5)

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

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

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

我们可以创建类型类实例,将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
}

现在我们可以在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)

答案 1 :(得分:3)

Integer.parseInt("")证明A可以转换为B.证明A可以是$variable = file_get_contents('your text file'); $data = explode(',',$variable); 到B.并且您没有任何隐含的证据证明可以反过来投射到FooInt(有充分的理由;)。

您正在寻找的是:

A =:= B

答案 2 :(得分:0)

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

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

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

因此,在我原始问题中给出的简单API的情况下:

<timepicker ng-show="vm.date" ng-model="vm.time" ng-change="vm.pickTime()" show-meridian="false"></timepicker>

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

Scala编程书中给出了a nice example of how this technique is used in Scala collections,特别是关于Traversables的toMap方法。

这本书指出了Map的构造函数

  

希望键值对,即两元组作为参数。如果我们有   序列[Traversable]对,创建一个Map不是很好   一步走出他们?这就是地图的作用,但我们有一个   困境。如果序列不是,我们不允许用户调用Map   一系列配对。

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