不清楚为什么我的范围内隐式转换不被接受为 '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 中使用隐含证据的一个很好的理由。我在我自己对这个问题的回答中详细说明了这一点(也在下面)。
=:=
检查两种类型是否相等,FooInt
和 Int
肯定不相等,尽管这两种类型的值存在隐式转换。
我会创建一个 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 中的项目是成对的。
我一直在试验隐式转换,我对使用这些的 '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 中使用隐含证据的一个很好的理由。我在我自己对这个问题的回答中详细说明了这一点(也在下面)。
=:=
检查两种类型是否相等,FooInt
和 Int
肯定不相等,尽管这两种类型的值存在隐式转换。
我会创建一个 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 中的项目是成对的。