SLC: Replace *Ops conversions by direct extension methods

PR Replace *Ops conversions by direct extension methods by halotukozak · Pull Request #26637 · scala/scala3 · GitHub

I propose to deprecate the implicit conversions, which was introduced to enable a’la extension method syntax. Currently, regular extension methods fit better. Similar to Replace runtime.Rich* implicits on primitives by direct extension methods. by sjrd · Pull Request #23872 · scala/scala3 · GitHub

e.g.

implicit def mkNumericOps(lhs: T): IntegralOps = new IntegralOps(lhs)

there is a one design question: should the extension methods be on the top-level or in the type class?

I’ve chosen the second one, but I don’t have a strong opinion.

Current draft doesn’t break binary compatibility, but it does break the source one (only if someone extends the TC and it’s not a problem in minor release, isn’t it?).
Inner extension methods in several instances bring the method name clash (they have to be marked with @targetName) but they allow to omit all the redundant bridges like this:

extension [T: Ops as ops](t: T)
  def meth = ops.meth(t)

However, with top-level extensions, there is a problem with overloading (e.g. math.max vs math.max for Ordering)

5 Likes

Can you give an example — the most plausible-to-actually-occur possible example — of the source incompatibility, to help us judge whether it’s acceptable breakage?

Can you lay out the pros and cons…?

If someone extends the TC, that’s why community build fails

//before
trait X:
  def meth(a: A): Unit

class XImpl extends X:
  def meth(a: A): Unit = ???

//after
trait X:
  extension(a: A) def meth: Unit

class XImpl extends X:
  def meth(a: A): Unit = ???

fails with method meth of type (a: A): Unit is a normal method, cannot override an extension method, but is still binary compatible

(only cons)

Top-level

  • requires bridges like
extension [T: Ops as ops](t: T)
  def meth = ops.meth(t)
  • problem with overloading (e.g. math.max vs math.max for Ordering)
  • extension methods divorced from a TC definition

In a type class

  • breaks source compatibility
  • sometimes requires @targetName

I may have missed sth and we can get around any of these problems