Allow 'empty' traits to be combined with an opaque type?

Would there be any downside or compiler complication to allow empty traits to be added to an opaque type, so they can be used as e.g. type bounds to resolve ambiguity looking op an implicit class that defines extra functionality for this opaque type?

empty trait Absolute {}

opaque type Position3 = Vector3 with Absolute

You can define extension methods for an opaque type. What exactly are you trying to do?

I want to use infix operators for these opaque types, and define these in mathematical constructs for reuse, like GroupInfix, VectorSpaceInfix… that will eventually call the extension methods on the opaque type, but these constructs may have identical infix operators, like + or *, resulting in ambiguity. Micromanaging the namespace is a possible solution, but it is not very user friendly. Adding trait ‘markers’ to my opaque type could be a better way to solve these ambiguities.

My use case and problems seem identical to what Ichoran is trying to do in this thread: Change shadowing mechanism of extension methods for on par implicit class behavior - #9 by Ichoran

There is another benefit:

To my knowledge, it is impossible to add erased fields to an opaque alias, which would be very handy

This is the sort of code I want to get working:

  trait MathStructure1[U] {
    def add(u:U, v:U):U
  }

  trait MathStructure2[U,V] {
    def add(u: U, v: V): U
  }

  object Scope {
    opaque type MathEntity1 = Double

    object MathEntity1 {

      def apply(u: Double): MathEntity1 = u

      extension (u: MathEntity1) {
        def toDouble: Double = u
      }
    }

    opaque type MathEntity2 = Double

    object MathEntity2 {

      def apply(u: Double): MathEntity2 = u

      extension (u: MathEntity2) {
        def toDouble: Double = u
      }
    }
  }

  import Scope.MathEntity1
  import Scope.MathEntity2

  given MathStructure1[MathEntity1] = new MathStructure1[MathEntity1] {

    override
    def add(u: MathEntity1, v: MathEntity1): MathEntity1 = MathEntity1(u.toDouble + v.toDouble)
  }

  given MathStructure2[MathEntity2, MathEntity1] = new MathStructure2[MathEntity2, MathEntity1] {

    override
    def add(u: MathEntity2, v: MathEntity1): MathEntity2 = MathEntity2(u.toDouble + v.toDouble)
  }


  implicit class MathStructure1Infix[U](u:U) {
    def +(v: U)(using q: MathStructure1[U]): U = q.add(u,v)
  }

  implicit class MathStructure2Infix[U,V](u: U) {
    def +(v: V)(using q: MathStructure2[U,V]): U = q.add(u, v)
  }

  /*
      I want to be able to do this in the same scope, but get

      Both method MathStructure1Infix ... and method MathStructure2Infix ... provide an extension method `+`
   */

  val p:MathEntity1 = MathEntity1(1.0) + MathEntity1(2.0)
  val q:MathEntity2 = MathEntity2(2.0) + MathEntity1(3.0) ```

Maybe we could only allow opaque types extending traits that are also marked as opaque, meaning they are only available at compile time?

opaque trait CompileTimeSemantic

opaque type CompileTimeType = AType with CompileTimeSemantic

Why not:

trait MathStructure1[U] {
  def add(u:U, v:U):U
  extension (u:U) def +(v: U): U = add(u,v)
}
trait MathStructure2[U,V] {
  def add(u: U, v: V): U
  extension (u:U) def +(v: V): U = add(u,v)
}
...
val p:MathEntity1 = MathEntity1(1.0) + MathEntity1(2.0)
val q:MathEntity2 = MathEntity2(2.0) + MathEntity1(3.0)

Well, it looks like a decent working alternative, but ideally, I would want to be able to decouple these so I can do things like

trait Group[U]
...
implicit class AdditiveGroupInfix[U](u:U)
...
implicit class MultiplicativeGroupInfix[U](u:U)
...

but maybe I can rewrite that by extending traits. I’ll try that.