Implicit def prioritization

I expected the following to compile:

  trait  Low              { implicit def toInt(s: Boolean): Int    = ??? }
  object High extends Low { implicit def toStr(s: Boolean): String = ??? }
  import High._

  def baz(value: Int   ) = ???
  def baz(value: String) = ???  
  
  // error: "ambiguous reference to overloaded definition, ..."
  //baz(true)

Looks like I don’t understand this pattern, so a few question:

  1. is this pattern described somewhere in the docs (I only found SO answers)
  2. can this pattern be used in this case?
  3. if not, is there any way to make baz(true) choose toStr over toInt

Thanks!

is this pattern described somewhere in the docs

What pattern? The use of two traits for prioritization? If so, I believe it is called implicit prioritization.
And no, I haven’t see it on the docs.

if not, is there any way to make baz(true) choose toStr over toInt

I am not sure if it is possible, but I am sure that this doesn’t seem like a good design.
Implicit conversions are called a bad practice for a reason; now combine them with low priority instances as well as with ambiguities due to overloading and you would an API that works like magic and is difficult to understand and debug.

I would suggest you to instead use bool.toInt & bool.toString extension methods.

1 Like

What pattern? The use of two traits for prioritization?

Yes, I guess I should mention where I found it: this SO answer (but I feel like I’ve encountered LowPriorityImplicits in codebases on multiple occasions)

So I checked quickly I grepped out "LowPriorityImplicits" in these (they just happened to be cloned on my system already):

  • play
  • scalajs-react
  • quill
  • vegas

So I’d say good or bad, it’s definitely unfortunate that the docs don’t describe it :frowning:

1 Like

You don’t even have to look into an external library to find this pattern. The Predef object, whose members are always in scope, also inherits a LowPriorityImplicits trait, defined in the same class.

There is a similar example in the documentation, see the example at the end of this section. But the types for the overridden implicits are identical, which is not the case in your code (one has type Boolean => Int, the other Boolean => String, so the second is not overriding the first). Also note the quote from the SO answer you linked:

if alternatives have identical argument types, the one which is defined in a subclass wins

If you want to have baz(true) work, the way to go is probably to add another def baz(value: Boolean) = baz(your_bool_to_string_conversion). That is probably clearer than adding an implicit conversion. Of course, depending on your real use case, adding the additional overload may not be possible, in which case you can use extension methods like @BalmungSan suggested.

2 Likes