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?