Mutiple given instances in Scala 3

I test the following code. The compiler complains ambiguous implicit arguments: both given instance A3 and given instance A2 match type A[T] of an implicit parameter of method apply in object A. Though it’s expected, I am wondering in Scala 3 how I can make use of given instances to achieve the effect where compiler will auto pickup the corresponded type instance for something like ad-hoc polymorphism? Maybe I misunderstand the concept?

trait A[T]:
  def f(f: => T): T

object A: 
  def apply[T: A](): A[T] = summon[A[T]]

class MyA {
  def f(msg: String) = println(msg)
}

given A3: A[Int] = new A[Int] {
  def f(f: => Int): Int = {println(s"execute f(=>int): ${f}"); f}
} 

given A2: A[String] = new A[String] {
  def f(f: => String): String = {println(s"execute f(=>String): ${f}"); f}
}

given A1 (using myA: MyA): A[String] = new A[String] {
  def f(f: => String): String = {myA.f(f); f}
}

@main def execute(): Unit = {
  //given instance: A[String] = A1(using MyA())
  val a = A()
  a.f("hey!")
}

Sounds like a good use case for implicit prioritization.
Check: Scastie - An interactive playground for Scala.

1 Like

I did not know implicit prioritization[1]. The code I attempt for testing is below. It looks like a few changes need to make in order to achieve such effect (choose between multiple given instances).

  1. A trait (here it’s called LowPriority) within which the given instance located there would have lower priority to be used, compare to that located in the object A. In the code below, it’s A2

  2. In order to allow passing implicit instance (with using keyword), change default context bound apply() to that with using keyword. Change def apply[T: A](): A[T] = summon[A[T]] to def apply[T](using ev: A[T]): ev.type = ev

Am I correct in this sense?

And thanks for the sample, it’s really helpful!!

trait A[T]:
  def f(f: => T): T

object A extends LowPriority {

  //def apply[T: A](): A[T] = summon[A[T]]
  def apply[T](using ev: A[T]): ev.type = ev // this allow A1 to pass MyA instance 

  // given instances below's priority are higher than that of A2

  given A1 (using myA: MyA): A[String] = new A[String] {
    def f(f: => String): String = {myA.f(s"A1 MyA $f"); f}
  }

  given A3: A[Int] = new A[Int] {
    def f(f: => Int): Int = {println(s"A3 execute f(=>int): ${f}"); f}
  }

}

class MyA {
  def f(msg: String) = println(msg)
}

trait LowPriority { // low priority

  given A2: A[String] = new A[String] {
    def f(f: => String): String = {println(s"A2 execute f(=>String): ${f}"); f}
  }

}

object Main {

  // this will call A2 instance
  def execute1(): Unit = {
    val a = A[String]
    a.f("hey!")
  }

  // this will call A1 (using MyA) instance
  def execute2(): Unit = {
    given myA: MyA = new MyA
    val a = A[String]
    a.f("hey!")
  }

  def execute3(): Unit = {
    val a = A[Int]
    a.f(3)
  }

  def main(args: Array[String]): Unit = {
    execute1()
    execute2()
    execute3()
  }

}

[1]. scala - Is there a way to control which implicit conversion will be the default used? - Stack Overflow

Oh sorry, I shouldn’t have assumed. Thanks for adding the link to StackOverflow!

Yes, the idea is to put in the LowPriority trait all the default instances.
Note, you can have more than two levels of priority; but don’t abuse it, unless required.

Ham no, sorry.
That change was totally aesthetical, I don’t like context bounds and it is better to return the singleton type of the instance, since that may be more specific; you can see that summon does that.

1 Like

Thanks for the explanation and advice! It’s useful, and I learn implicit prioritization. That’s great!