如何在Scala中链接implicits?

Dan*_*ral 76 scala implicit-conversion implicits

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

斯卡拉不允许两个这样的隐式转换正在发生,但是,我不能得到AC使用隐含AB和另一个隐含的BC.有没有办法解决这个限制?

Dan*_*ral 106

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)
}
Run Code Online (Sandbox Code Playgroud)

编辑:自Scala 2.11 https://issues.scala-lang.org/browse/SI-7629(您可以使用类型类)以来,不推荐使用视图边界('<%')

但是,如果隐式定义本身需要隐式参数(View bound),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)
}
Run Code Online (Sandbox Code Playgroud)

"魔术!",你可能会说.不是这样.以下是编译器如何翻译每个:

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)
}
Run Code Online (Sandbox Code Playgroud)

因此,while bToC被用作隐式转换,aToB并且toA作为隐式参数传递,而不是被链接为隐式转换.

编辑

相关问题:

  • 很好的解释.不允许隐式转换链接的原因是避免复杂性和调试噩梦.我想知道为什么然后链接允许隐式参数? (5认同)
  • 太好了!我学到了新东西.这应该在"隐藏功能"页面上. (3认同)

Rap*_*ael 12

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

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
}
Run Code Online (Sandbox Code Playgroud)

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