ChatGPT解决这个技术问题 Extra ChatGPT

如何在 Scala 中链接隐式?

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

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

@ryeguy Here's a meta question for the pimp/enrich debate,因为该死的标签。 那个标签...

C
Community

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 作为 隐式参数 传递,而不是作为隐式转换链接。

编辑

感兴趣的相关问题:

关于隐式的类型、起源和优先级的讨论


很好的解释。禁止隐式转换链接的原因是为了避免复杂性和调试噩梦。我想知道为什么隐式参数允许链接?
好的!我学到了一些新东西。这应该在“隐藏功能”页面上。
谢谢!一个小问题:当我在 REPL 中尝试时,我必须在 T2 中的函数 aToBbToC 中添加显式结果类型。
@Agl 在即将到来的 2.9 中不需要,但我已经修改了代码以使其与 2.8 兼容。谢谢。
请注意,您要尝试执行的链接涉及更高种类的类型,然后类型推断再次可以解决您的问题。即我有 M[A]。我有一个隐式 A=>B 和一个隐式 M[] => N[],其中 M 和 N 都是一元的。我想使用这两个转换创建一个 N[B] 。链接这些需要一个额外的方法调用,第一个捕获 M[_],第二个捕获 A。
R
Raphael

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

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。在不太明显的情况下,这可能会掩盖潜在的问题。


S
Sagie Davidovich

Here's 也是累积路径的代码。

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))

关注公众号,不定期副业成功案例分享
关注公众号

不定期副业成功案例分享

领先一步获取最新的外包任务吗?

立即订阅