There is already a question about this: Scala: Implicit parameter resolution precedence. Which answers itself through a complicated blog post. I think the most important piece of information is in Martin Odersky's comment on the blog post:
Here's a more high-level explanation what goes on with implicit search
in Scala, and which corresponds to how the spec explains it, but in
slightly less formalistic language.
First, we look for implicits that are visible either as locals or as members of enclosing classes and packages or as imports - the
precise rule is that we should be able to access them using their name
only, without any prefix.
If no implicits are found in step 1, we look in the "implicit scope", which contains all sort of companion objects that bear some
relation to the type which we search for (i.e. companion object of the
type itself, of its parameters if any are given, and also of its
supertype and supertraits; the importance is to be as general as
possible without reverting to whole program analysis like Haskell
does).
If at either stage we find more than one implicit, disambiguation
kicks in. Disambiguation is exactly the same as for overloading
resolution. Static overloading resolution resolution rules are a bit
involved, and I won't repeat them here. If it's any consolation:
Java's rules and C#'s rules are considerably more complex than Scala's
in this area.
Now according to this explanation it are "the rules of static overloading resolution" which will disambiguate between notOption
and hitOption
. To be honest, I fail to see how.
This answer explains that indeed methods with more specific arguments have priority, but I don't know if or how that is related to the overloading rules.
If I were you I would not depend on this behavior too much, but use the easier to understand concept of implicit priority through inheritance. It's a good idea to put your implicits in the companion object anyway.
It boils down to the fact that implicits that are inherited have lower priority. So it's safe to put the implicit you fall back to if hitOption
doesn't match in a trait that the companion object extends.
case class OptionFinder[A](isOption: Boolean)
object OptionFinder extends LowerPriority {
implicit def hitOption[A]: OptionFinder[Option[A]] = OptionFinder(true)
}
trait LowerPriority {
implicit def notOption[A]: OptionFinder[A] = OptionFinder(false)
}
def myFunction[A](value: A)(implicit optionFinder: OptionFinder[A]): String = {
if (optionFinder.isOption) {"Found Option!"} else {"Found something else."}
}
This should also work if you put your implicits in a non companion object MyImplicits
and import them with import MyImplicits._
.