Создать двусмысленный скрытый низкий приоритет
Рассмотрим кодек по умолчанию, предлагаемый в 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
Но можем ли мы пойти в противоположном направлении? Можем ли мы создать неявный низкий уровень, который отключает ("неоднозначно") значение по умолчанию? Я смотрел на уравнение приоритета и играл с последствиями низкого приоритета, но мне еще предстоит создать нечто неоднозначное по умолчанию.
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
)
Вроде да.
Вы можете сделать это, создав новый тип. Т.е. тип, который является просто прокси для 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