使用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._
答案 0 :(得分:8)
隐含必须以简单名称提供,因此您可以在导入时重命名。
只是为了验证:
scala> import A._ ; import B.{ Imp => BImp, _ }
import A._
import B.{Imp=>BImp, _}
scala> 3.myPrint
3
答案 1 :(得分:0)
实际上,如果你替换
,它会起作用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,则必须重命名其中一个。
答案 2 :(得分:0)
如果有其他人遇到此问题,我会在此处找到部分解决方法:
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
它没有具体解决这个问题,但是如何使用含义是一个很好的总结。
然而,将所有这些部分放在一起,这似乎仍然是一个问题:
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更有意义。