If I understand correctly you want to check at compile time that there is local implicit io.Codec
("higher-priority") or produce compile error otherwise. This can be done with macros (using compiler internals).
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
Tested in 2.13.0.
libraryDependencies ++= Seq(
scalaOrganization.value % "scala-reflect" % scalaVersion.value,
scalaOrganization.value % "scala-compiler" % scalaVersion.value
)
Still working in Scala 2.13.10.
Scala 3 implementation
import scala.quoted.{Expr, Quotes, Type, quotes}
import dotty.tools.dotc.typer.{Implicits => dottyImplicits}
inline def localImplicitly[A]: A = ${impl[A]}
def impl[A: Type](using Quotes): Expr[A] = {
import quotes.reflect.*
given c: dotty.tools.dotc.core.Contexts.Context =
quotes.asInstanceOf[scala.quoted.runtime.impl.QuotesImpl].ctx
val typer = c.typer
val search = new typer.ImplicitSearch(
TypeRepr.of[A].asInstanceOf[dotty.tools.dotc.core.Types.Type],
dotty.tools.dotc.ast.tpd.EmptyTree,
Position.ofMacroExpansion.asInstanceOf[dotty.tools.dotc.util.SourcePosition].span
)
def eligible(contextual: Boolean): List[dottyImplicits.Candidate] =
if contextual then
if c.gadt.isNarrowing then
dotty.tools.dotc.core.Contexts.withoutMode(dotty.tools.dotc.core.Mode.ImplicitsEnabled) {
c.implicits.uncachedEligible(search.wildProto)
}
else c.implicits.eligible(search.wildProto)
else search.implicitScope(search.wildProto).eligible
val searchImplicitMethod = classOf[typer.ImplicitSearch]
.getDeclaredMethod("searchImplicit", classOf[List[dottyImplicits.Candidate]], classOf[Boolean])
searchImplicitMethod.setAccessible(true)
def implicitSearchResult(contextual: Boolean) =
searchImplicitMethod.invoke(search, eligible(contextual), contextual)
.asInstanceOf[dottyImplicits.SearchResult]
.tree.asInstanceOf[ImplicitSearchResult]
implicitSearchResult(true) match {
case success: ImplicitSearchSuccess => success.tree.asExprOf[A]
case failure: ImplicitSearchFailure =>
report.errorAndAbort(s"no local implicit ${Type.show[A]}: ${failure.explanation}")
}
}
Scala 3.2.0.
fallbackSystemCodec
so that all code that requires an implicit codec will break (won't compile) unless a higher priority implicit is in scope. The higher priority implicit is easy, but I haven't been able to create an implicit at the same priority level (and thus is ambiguous with) the fallback codec. – HunfallbackSystemCodec
, but do this without importingfallbackSystemCodec
because it's already in scope anyway, isn't it? – Hunio.Codec.fallbackSystemCodec
(because it's part of the stdlib). And as your 'clashing' implicit is not explicitly imported, you can't remove that either. Am I understanding this correctly? – Twink