If you have access to the code of Tool
you can define default implicit in the companion object of Tool
(not companion of Toolkit
or User
). Local implicit "overrides" implicit in companion.
case class Tool(msg: String)
object Tool {
implicit lazy val tool = Tool("default")
}
u.usingGenericTool() //[from generic usage] using tool: default
u.usingSpecificTool() // [from generic usage] using tool: shovel
Suppose you don't have access to the code of Tool
. Indeed, if you define the default implicit locally or in a parent of User
you'll have error ambiguous implicit values
. You can hide implicit by name
class User {
val toolkit = new Toolkit()
implicit lazy val tool = Tool("default")
def usingGenericTool() {
toolkit.usingTool("from generic usage")
}
def usingSpecificTool() {
implicit lazy val tool = Tool("shovel") // the same name!
toolkit.usingTool("from generic usage")
}
}
val u = new user.User
u.usingGenericTool() //[from generic usage] using tool: default
u.usingSpecificTool() // [from generic usage] using tool: shovel
NullPointerException on implicit resolution
Caching the circe implicitly resolved Encoder/Decoder instances
How can an implicit be unimported from the Scala repl?
Scala implicit def do not work if the def name is toString
Is there a workaround for this format parameter in Scala?
Another option is shapeless.LowPriority
(names of implicits can be different)
// libraryDependencies += "com.chuusai" %% "shapeless" % "2.3.10"
class User {
val toolkit = new Toolkit()
implicit def tool0(implicit lp: LowPriority) = Tool("default")
def usingGenericTool() {
toolkit.usingTool("from generic usage")
}
def usingSpecificTool() {
implicit lazy val tool = Tool("shovel")
toolkit.usingTool("from generic usage")
}
}
u.usingGenericTool() //[from generic usage] using tool: default
u.usingSpecificTool() // [from generic usage] using tool: shovel
Implicit parameter precedence
General way of ensuring implicit definition always has higher/lower priority
If you have access to the code of Toolkit
you can use default parameter
class Toolkit {
def usingTool(source: String)(implicit tool: Tool = Tool("default")) {
println(s"[$source] using tool: ${tool.msg}")
}
}
u.usingGenericTool() //[from generic usage] using tool: default
u.usingSpecificTool() // [from generic usage] using tool: shovel
In Scala 3 there is scala.compiletime.summonFrom
def usingGenericTool() = {
inline given Tool = summonFrom {
case given Tool => summon[Tool]
case _ => Tool("default")
}
toolkit.usingTool("from generic usage")
}
u.usingGenericTool() //[from generic usage] using tool: default
u.usingSpecificTool() // [from generic usage] using tool: shovel
https://docs.scala-lang.org/scala3/reference/metaprogramming/compiletime-ops.html#summoning-implicits-selectively
In Scala 2 we can achieve this behavior with a macro
def usingGenericTool(): Unit = {
implicit lazy val tool0: Tool = summonOrElse(Tool("default"))
toolkit.usingTool("from generic usage")
}
u.usingGenericTool() //[from generic usage] using tool: default
u.usingSpecificTool() // [from generic usage] using tool: shovel
// in a different subproject
import scala.language.experimental.macros
import scala.reflect.macros.blackbox
def summonOrElse[A](default: A): A = macro summonOrElseImpl[A]
def summonOrElseImpl[A: c.WeakTypeTag](c: blackbox.Context)(default: c.Tree): c.Tree = {
import c.universe._
c.typecheck(q"""
val ${c.internal.enclosingOwner.name.toTermName} = null
_root_.scala.Predef.implicitly[${weakTypeOf[A]}]
""", silent = true) match {
case EmptyTree => default
case t => t
}
}
See also
Finding the second matching implicit
create an ambiguous low priority implicit