Extension methods in typeclasses are surprising

I have tried your workaround in Can We Wean Scala Off Implicit Conversions?

This still uses the concept of Conversion , but it’s no longer an implicit conversion . The conversion is applied explicitly whereever it is needed. The idea is that with the help of using clauses we can “push” the applications of conversions from user code to a few critical points in the libraries

, and it works - at the expense of some kind of algebraic purity in the type class definition:

import scala.language.implicitConversions

given id[T] as Conversion[T, T] = identity

trait SemiGroup[T]:
  extension [U](x: U)(using c: Conversion[U, T]) def combine (y: T): T

trait Monoid[T] extends SemiGroup[T]:
  def unit: T

class A

object A:
  given Conversion[Int, A]:
    def apply(n: Int) = ???

given Monoid[A]:
  extension [U](x: U)(using c: Conversion[U, A]) def combine (y: A) = ???
  def unit = ???

val a = new A

a.combine(a) // ok
a.combine(1) // ok
1.combine(a) // ok

There is still an issue if we introduce a second type B:

class B

object B:
  given Conversion[Int, B]:
    def apply(n: Int) = ???

given Monoid[B]:
  extension [U](x: U)(using c: Conversion[U, B]) def combine (y: B) = ???
  def unit = ???

val b = new B

b.combine(b) // ok
b.combine(1) // ok
1.combine(b)
^
Found:    (1 : Int)
Required: ?{ combine: ? }
Note that implicit extension methods cannot be applied because they are ambiguous;
both object given_Monoid_A and object given_Monoid_B provide an extension method `combine` on (1 : Int)

If someone has an idea how to solve this…