繁体   English   中英

Scala:具有相同名称的多个隐式转换

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM