如何在 Scala 中使用链隐式?

Pip-my-library 模式允许我通过提供从该类到实现该方法的方法的隐式转换,似乎向一个类添加了一个方法。

但是 Scala 不允许发生两次这样的隐式转换,所以我不能使用隐式 AB和另一个隐式 BCAC。有办法绕过这个限制吗?

8941 次浏览

Scala 对添加方法的自动转换有一个限制,那就是它在寻找方法时不会应用多个转换。例如:

class A(val n: Int)
class B(val m: Int, val n: Int)
class C(val m: Int, val n: Int, val o: Int) {
def total = m + n + o
}


// This demonstrates implicit conversion chaining restrictions
object T1 { // to make it easy to test on REPL
implicit def toA(n: Int): A = new A(n)
implicit def aToB(a: A): B = new B(a.n, a.n)
implicit def bToC(b: B): C = new C(b.m, b.n, b.m + b.n)


// won't work
println(5.total)
println(new A(5).total)


// works
println(new B(5, 5).total)
println(new C(5, 5, 10).total)
}

编辑: 由于 Scala 2.11 https://issues.scala-lang.org/browse/SI-7629 (您可以改用类型类) ,因此不推荐使用视图边界(“ <%”)

但是,如果隐式定义本身需要一个隐式参数(视图绑定) ,Scala 威尔会根据需要寻找额外的隐式值。继续上面的例子:

// def m[A <% B](m: A) is the same thing as
// def m[A](m: A)(implicit ev: A => B)


object T2 {
implicit def toA(n: Int): A = new A(n)
implicit def aToB[A1 <% A](a: A1): B = new B(a.n, a.n)
implicit def bToC[B1 <% B](b: B1): C = new C(b.m, b.n, b.m + b.n)


// works
println(5.total)
println(new A(5).total)
println(new B(5, 5).total)
println(new C(5, 5, 10).total)
}

你可能会说“神奇!”,但事实并非如此。下面是编译器将如何翻译每一个:

object T1Translated {
implicit def toA(n: Int): A = new A(n)
implicit def aToB(a: A): B = new B(a.n, a.n)
implicit def bToC(b: B): C = new C(b.m, b.n, b.m + b.n)


// Scala won't do this
println(bToC(aToB(toA(5))).total)
println(bToC(aToB(new A(5))).total)


// Just this
println(bToC(new B(5, 5)).total)


// No implicits required
println(new C(5, 5, 10).total)
}


object T2Translated {
implicit def toA(n: Int): A = new A(n)
implicit def aToB[A1 <% A](a: A1): B = new B(a.n, a.n)
implicit def bToC[B1 <% B](b: B1): C = new C(b.m, b.n, b.m + b.n)


// Scala does this
println(bToC(5)(x => aToB(x)(y => toA(y))).total)
println(bToC(new A(5))(x => aToB(x)(identity)).total)
println(bToC(new B(5, 5))(identity).total)


// no implicits required
println(new C(5, 5, 10).total)
}

因此,当使用 bToC作为隐式转换时,aToBtoA作为 隐式参数传递,而不是作为隐式转换链接。

剪辑

相关的利益问题:

注意,您也可以使用隐式参数构建圆。然而,这些是由编译器检测到的,如下所示:

class Wrap {
class A(implicit b : B)
class B(implicit c : C)
class C(implicit a : A)


implicit def c = new C
implicit def b = new B
implicit def a = new A
}

但是,给用户的错误并不像它们可能的那样清楚; 它只是对所有三个建筑工地的 could not find implicit value for parameter抱怨。在不那么明显的情况下,这可能会掩盖根本问题。

下面的 代码也累积了路径。

import scala.language.implicitConversions


// Vertices
case class A(l: List[Char])
case class B(l: List[Char])
case class C(l: List[Char])
case class D(l: List[Char])
case class E(l: List[Char])


// Edges
implicit def ad[A1 <% A](x: A1) = D(x.l :+ 'A')
implicit def bc[B1 <% B](x: B1) = C(x.l :+ 'B')
implicit def ce[C1 <% C](x: C1) = E(x.l :+ 'C')
implicit def ea[E1 <% E](x: E1) = A(x.l :+ 'E')


def pathFrom(end:D) = end


pathFrom(B(Nil))   // res0: D = D(List(B, C, E, A))