Proposal for Opaque Type Aliases

I’m going over the original SIP again, and I’m wondering whether the original motivation can be satisfied with a simpler solution.

The SIP explains that the original motivation is to be able to make the compiler help differentiate between different type aliases of the same type – for instance, Id and Password, both are String. One solution to this are wrapper case classes, but since they may incur a performance penalty, a new solution was needed.

Opaques seem to me mostly of a better-performing emulation of wrapper case classes; but how about designing a solution that is tailored to the original motivation – differentiating between type aliases?

object aliases {
  // good old aliases
  type Id = String
  type Password = String
  // new "strict" aliases
  strict type StrictId = String
  strict type StrictPassword = String
}

val id: Id = "a"
val strictId: StrictId = StrictId("b")

// compiles
val s: String = id
val pwd: Password = id
val strictId2: StrictId = strictId
id.toLowerCase()
strictId.toLowerCase() // this is different than `opaque` behavior

// doesn't compile
val s2: String = strictId
val id2: Id = strictId
val strictPwd: StrictPassword = "x"
val strictPwd: StrictPassword = id
val strictPwd: StrictPassword = strictId

// potentially compiles?
val s2: String = strictId.asInstanceOf[String]
val id2: Id = strictId.asInstanceOf[Id]

I don’t have any strong preference to the strict keyword – it’s just the first thing that popped into mind – but I do believe that opaque is inadequate for such a feature; in fact, I think it could be useful to introduce both features, where’s opaque really serves more of a lightweight wrapper, and uses the more class-like syntax accordingly.