Good way to reuse opaque type's transparentness?

I have been working with opaque types fair bit and using them as newtypes. Typically I tend to add a bunch of implicits and further extend it and use them like mixins like the following:

trait Newtype[Src](using codec: Codec[Src]):
  opaque type Type = Src
  given Codec[Type] = codec
  def apply(v: Src): Type = v
  extension (self: Type) inline def value: Src = self

transparent trait BigDecimalNewType extends Newtype[BigDecimal]:
  override opaque type Type = BigDecimal
  extension (self: Type)
    inline def +(inline y: Type): Type = self.value + y.value
    inline def -(inline y: Type): Type = self.value - y.value
    inline def *(inline y: Type): Type = self.value * y.value
    inline def /(inline y: Type): Type = self.value / y.value

type Amount = Amount.Type
object Amount extends BigDecimalNewType

type Price = Price.Type
object Price extends BigDecimalNewType

But the problem is that if the language disallows overriding of opaque type, it becomes littered with asInstanceOf calls. i.e

trait Newtype[Src](using codec: Codec[Src]):
  opaque type Type = Src
  given Codec[Type] = codec
  def apply(v: Src): Type = v
  extension (self: Type) inline def value: Src = self

transparent trait BigDecimalNewType extends Newtype[BigDecimal]:
  extension (self: Type)
    inline def +(inline y: Type): Type = (self.value + y.value).asInstanceOf[Type]
    inline def -(inline y: Type): Type = (self.value - y.value).asInstanceOf[Type]
    inline def *(inline y: Type): Type = (self.value * y.value).asInstanceOf[Type]
    inline def /(inline y: Type): Type = (self.value / y.value).asInstanceOf[Type]

type Amount = Amount.Type
object Amount extends BigDecimalNewType

type Price = Price.Type
object Price extends BigDecimalNewType

Any ideas to improve this, or shouldn’t the language allow overrides of opaque types?

1 Like

is there anything wrong with reusing apply? (or another inline method such as lift)

trait Newtype[Src]:
  opaque type Type = Src
  def apply(v: Src): Type = v
  protected inline def lift(v: Src): Type = v
  extension (self: Type) inline def value: Src = self

transparent trait BigDecimalNewType extends Newtype[BigDecimal]:
  extension (self: Type)
    inline def +(inline y: Type): Type = lift(self.value + y.value)
    inline def -(inline y: Type): Type = lift(self.value - y.value)
    inline def *(inline y: Type): Type = lift(self.value * y.value)
    inline def /(inline y: Type): Type = lift(self.value / y.value)

type Amount = Amount.Type
object Amount extends BigDecimalNewType

type Price = Price.Type
object Price extends BigDecimalNewType
2 Likes

That would work as well. Just slightly cumbersome to convert to and from if you have a lot of extensions in the mixin trait.