Paul
April 3, 2024, 7:05am
1
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?
Paul
April 3, 2024, 9:34am
3
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
Paul
April 4, 2024, 6:24am
5
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) ```
Paul
April 4, 2024, 8:10am
6
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
rjolly
April 4, 2024, 8:36am
7
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)
Paul
April 4, 2024, 9:24am
8
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.