【问题标题】:create an ambiguous low priority implicit创建一个模棱两可的低优先级隐式
【发布时间】:2019-08-23 00:58:18
【问题描述】:

考虑io 包中提供的默认编解码器。

implicitly[io.Codec].name  //res0: String = UTF-8

这是一个“低优先级”隐式,因此很容易覆盖而不会产生歧义。

implicit val betterCodec: io.Codec = io.Codec("US-ASCII")

implicitly[io.Codec].name  //res1: String = US-ASCII

提高优先级也很容易。

import io.Codec.fallbackSystemCodec
implicit val betterCodec: io.Codec = io.Codec("US-ASCII")

implicitly[io.Codec].name  //won't compile: ambiguous implicit values

但是我们可以朝相反的方向走吗?我们可以创建一个禁用(“歧义”?)默认值的低级隐式吗?我一直在研究优先级方程式并使用低优先级隐式进行处理,但我还没有创建一些与默认值不明确的东西。

【问题讨论】:

  • 假设您的意思是“消除歧义”?否则不会编译?即你想创建一个新的'deafult'隐式确实编译,对吧?
  • 不完全。我想以与fallbackSystemCodec 相同的低优先级创建一个伪隐式,以便所有需要隐式编解码器的代码 中断(不会编译),除非更高优先级的隐式在范围内。较高优先级的隐式很容易,但我无法创建一个具有相同优先级的隐式(因此与后备编解码器有歧义)。
  • 我认为您混淆了优先级/搜索顺序与范围内的多个隐含。隐式是“低优先级”还是“高优先级”都没有关系。只要作用域内隐含的不止一个,编译器就会报错。
  • 但这就是我想要的。我想要编译器抱怨。我想写一个不会编译的隐式,因为它与fallbackSystemCodec 有歧义,但是在不导入fallbackSystemCodec 的情况下执行此操作,因为它已经在范围内,不是吗?
  • 一般来说,我不太明白这会对您有什么帮助。假设您实现了这种行为。那么你如何“修复”你的程序?您不能删除 io.Codec.fallbackSystemCodec(因为它是标准库的一部分)。而且由于您的“冲突”隐式未显式导入,因此您也无法删除它。我理解正确吗?

标签: scala implicit scala-macros scala-compiler


【解决方案1】:

如果我理解正确,您想在编译时检查是否存在本地隐式io.Codec(“更高优先级”),否则会产生编译错误。这可以通过宏来完成(使用编译器内部)。

import scala.language.experimental.macros
import scala.reflect.macros.{contexts, whitebox}

object Macros {

  def localImplicitly[A]: A = macro impl[A]

  def impl[A: c.WeakTypeTag](c: whitebox.Context): c.Tree = {
    import c.universe._

    val context = c.asInstanceOf[contexts.Context]
    val global: context.universe.type = context.universe
    val analyzer: global.analyzer.type = global.analyzer
    val callsiteContext = context.callsiteTyper.context

    val tpA = weakTypeOf[A]

    val localImplicit = new analyzer.ImplicitSearch(
      tree = EmptyTree.asInstanceOf[global.Tree],
      pt = tpA.asInstanceOf[global.Type],
      isView = false,
      context0 = callsiteContext.makeImplicit(reportAmbiguousErrors = true),
      pos0 = c.enclosingPosition.asInstanceOf[global.Position]
    ) {
      override def searchImplicit(
                                   implicitInfoss: List[List[analyzer.ImplicitInfo]],
                                   isLocalToCallsite: Boolean
                                 ): analyzer.SearchResult = {
        if (isLocalToCallsite)
          super.searchImplicit(implicitInfoss, isLocalToCallsite)
        else analyzer.SearchFailure
      }
    }.bestImplicit

    if (localImplicit.isSuccess)
      localImplicit.tree.asInstanceOf[c.Tree]
    else c.abort(c.enclosingPosition, s"no local implicit $tpA")
  }
}

localImplicitly[io.Codec].name // doesn't compile
// Error: no local implicit scala.io.Codec

implicit val betterCodec: io.Codec = io.Codec("US-ASCII")
localImplicitly[Codec].name // US-ASCII

import io.Codec.fallbackSystemCodec
localImplicitly[Codec].name // UTF-8

import io.Codec.fallbackSystemCodec
implicit val betterCodec: io.Codec = io.Codec("US-ASCII")
localImplicitly[Codec].name // doesn't compile
//Error: ambiguous implicit values:
// both value betterCodec in object App of type => scala.io.Codec
// and lazy value fallbackSystemCodec in trait LowPriorityCodecImplicits of type => //scala.io.Codec
// match expected type scala.io.Codec

在 2.13.0 中测试。

libraryDependencies ++= Seq(
  scalaOrganization.value % "scala-reflect" % scalaVersion.value,
  scalaOrganization.value % "scala-compiler" % scalaVersion.value
)

【讨论】:

  • 非常令人印象深刻。 +1。唉,仍然不是我希望达到的相当。考虑io.Source.fromURI() 方法。它采用隐式Codec 参数。我可以禁用默认的fallbackSystemCodec 吗?我的想法是在同一优先级创建一个冲突/模棱两可的隐含。然后fromURI() 将不起作用除非更高优先级的隐含在范围内。也许这是不可能的。也许有更好的方法。
  • @jwvh 让我们想想“禁用默认fallbackSystemCodec”是什么意思。您想在编译时检查是否存在本地隐式io.Codec(“更高优先级”),否则会产生编译错误。我猜localImplicitly[io.Codec]; io.Source.fromURI(new URI("aaa")) 满足条件。
  • @jwvh 您甚至可以将宏设为隐式宏。 trait ExistLocal[A] { def instance: A }; object ExistLocal { implicit def materialize[A]: ExistLocal[A] = macro impl[A]; def impl[A: c.WeakTypeTag](c: whitebox.Context): c.Tree = { ... if (localImplicit.isSuccess) q"new ExistLocal[$tpA] { def instance = ${localImplicit.tree.asInstanceOf[c.Tree]} }" else c.abort(c.enclosingPosition, s"no local implicit $tpA") } }; def fromURI(str: String)(implicit ev: ExistLocal[Codec]) = io.Source.fromURI(new URI(str)); fromURI("aaa")
  • @jwvh 应该是def fromURI(str: String)(implicit ev: ExistLocal[Codec]) = io.Source.fromURI(new URI(str))(ev.instance)
【解决方案2】:

有点,是的。

您可以通过创建“新类型”来做到这一点。 IE。一种类型,它只是io.Codec 的代理,并包装了实例。这意味着您还需要将所有隐式参数从 io.Codec 更改为 CodecWrapper,这可能是不可能的。

trait CodecWraper {
  def orphan: io.Codec
}

object CodecWrapper {
  /* because it's in the companion, this will have the highest implicit resolution priority. */
  implicit def defaultInstance: CodecWrapper = 
    new CodecWrapper {
      def orphan = new io.Codec { /* your default implementation here */ }
    }
  }
}

import io.Codec.fallbackSystemCodec
implicitly[CodecWrapper].orphan // io.Codec we defined above - no ambiguity

【讨论】:

  • 您的解决方案避免隐含歧义。我的目标是在不导入 fallbackSystemCodec 的情况下创建一个隐含的歧义,因为它应该已经在范围内。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多