[英]Scala: Multiple implicit conversions with same name
使用scala 2.10.3,我的目标是做以下工作:
object A {
implicit class Imp(i: Int) {
def myPrint() {
println(i)
}
}
}
object B {
implicit class Imp(i: String) {
def myPrint() {
println(i)
}
}
}
import A._
import B._
object MyApp extends App {
3.myPrint()
}
这失败了
value myPrint is not a member of Int
如果我给A.Imp和B.Imp赋予不同的名称(例如A.Imp1和B.Imp2),它就可以了。
更深入地潜入它,隐式转换似乎存在同样的问题。
这有效:
object A {
implicit def Imp(i: Int) = new {
def myPrint() {
println(i)
}
}
implicit def Imp(i: String) = new {
def myPrint() {
println(i)
}
}
}
import A._
object MyApp extends App {
3.myPrint()
}
然而,这不是:
object A {
implicit def Imp(i: Int) = new {
def myPrint() {
println(i)
}
}
}
object B {
implicit def Imp(i: String) = new {
def myPrint() {
println(i)
}
}
}
import A._
import B._
object MyApp extends App {
3.myPrint()
}
为什么? 这是scala编译器中的错误吗? 我需要这种情况,因为我的对象A和B派生自相同的特征(带有类型参数),然后定义其类型参数的隐式转换。 在这个特性中,我只能为隐式转换指定一个名称。 我希望能够将更多这些对象导入我的范围。 有没有办法做到这一点?
编辑 :我不能给隐式类赋予不同的名称,因为上面的例子只是解决问题。 我的实际代码看起来更像
trait P[T] {
implicit class Imp(i: T) {
def myPrint() {
...
}
}
}
object A extends P[Int]
object B extends P[String]
import A._
import B._
隐含必须以简单名称提供,因此您可以在导入时重命名。
只是为了验证:
scala> import A._ ; import B.{ Imp => BImp, _ }
import A._
import B.{Imp=>BImp, _}
scala> 3.myPrint
3
实际上,如果你更换它是有效的
import A._
import B._
同
import B._
import A._
我认为,发生的事情是A.Imp被B.Imp所遮蔽,因为它具有相同的名称。 显然,阴影适用于函数的名称,不考虑签名。 因此,如果导入A然后导入B,则只有B.Imp(i:String)可用,如果导入B然后导入A,则只有A.Imp(i:Int)可用。
如果需要同时使用A.Imp和B.Imp,则必须重命名其中一个。
如果其他人遇到这个问题,有一个部分解决方法,我在这里找到:
https://github.com/lihaoyi/scalatags/blob/3dea48c42c5581329e363d8c3f587c2c50d92f85/scalatags/shared/src/main/scala/scalatags/generic/Bundle.scala#L120
该代码由李浩义撰写,因此您可以非常自信没有更好的解决方案......
从本质上讲,人们仍然可以使用特征来相互定义方法,但是它需要样板来将这些含义复制到唯一的名称中。 这个例子可能更容易理解:
trait Chainable
object Chainable {
implicit val _chainableFromInt = IntChainable.chainable _
implicit val _chainableFromIntTrav = IntChainable.traversable _
implicit val _chainableFromIntOpt = IntChainable.optional _
implicit val _chainableFromIntTry = IntChainable.tried _
implicit val _chainableFromDom = DomChainable.chainable _
implicit val _chainableFromDomTrav = DomChainable.traversable _
implicit val _chainableFromDomOpt = DomChainable.optional _
implicit val _chainableFromDomTry = DomChainable.tried _
private object IntChainable extends ImplChainables[Int] {
def chainable(n:Int) = Constant(n)
}
private object DomChainable extends ImplChainables[dom.Element]{
def chainable(e:Element) = Insertion(e)
}
private trait ImplChainables[T] {
def chainable(t:T):Chainable
def traversable(trav:TraversableOnce[T]):Chainable =
SeqChainable(trav.map(chainable).toList)
def optional(opt:Option[T]):Chainable =
opt match {
case Some(t) => chainable(t)
case None => NoneChainable
}
def tried(tried:Try[T]):Chainable =
optional(tried.toOption)
}
}
换句话说,永远不要写:
trait P[T] {
implicit def foo(i: T) = ...
}
object A extends P[X]
因为在类型参数化特征中定义implicits会导致这些命名冲突。 虽然顺便提一下,上面链接中提到的特征是在许多类型上参数化的,但是想法是在同一范围内不需要该特征的任何实现。 (对于那些熟悉Scalatags的人来说,JSDom vs Text,all._ vs short._)
我还建议阅读: http : //www.lihaoyi.com/post/ImplicitDesignPatternsinScala.html
它没有具体解决这个问题,但是如何使用implicits是一个很好的总结。
然而 ,将所有这些部分放在一起,这仍然是一个问题:
trait ImplChainables[AnotherTypeClass]{
type F[A] = A=>AnotherTypeClass
implicit def transitiveImplicit[A:F](t: A):Chainable
implicit def traversable[A:F](trav: TraversableOnce[A]):Chainable = ...
}
这个特性允许的是:
object anotherImpl extends ImplChainables[AnotherTypeClass] {...}
import anotherImpl._
implicit val string2another: String=>AnotherTypeClass = ...
Seq("x"):Chainable
由于类型参数和上下文绑定(隐式参数),不能将它们(例如:Foo.bar _)扩展为函数值。 隐式参数部分已在Dotty中修复: http : //dotty.epfl.ch/blog/2016/12/05/implicit-function-types.html
我不知道是否有可能找到完整的解决方案,不用说这是一个复杂的问题。 如果相同的名称暗示只是有效并且可以避免整个问题,那将是很好的。 在任何情况下,添加一个unimport关键字比通过遮蔽它们来关闭implicits更有意义。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.