Scala-dev: Opaque types implementation

Created on 10 May 2018  Â·  15Comments  Â·  Source: scala/scala-dev

implementation of https://docs.scala-lang.org/sips/opaque-types.html

will be available during the 2.13.x cycle under -Xsource:2.14. If all goes well, on by default in 2.14.0

Most helpful comment

slipping this into a 2.13.x release

Pretty please ...

All 15 comments

Has the syntax been decided yet? The consensus to Martin's inline class idea wasn't clear to me

I expect we'll go with the syntax from the SIP: opaque type Name = String

Tentatively assigning to M5 to keep this on our radar. (I assume it won't be ready until after 2.13.0 final)

Implementation note: one complication I hadn't considered so far is that baseTypeSeq is pre-computed, and I don't think we already have a mechanism to make sure abstract types in a BTS are treated in a context-dependent way. BTS works very differently in dotty, and would be hard to backport to scalac

@adriaanm are you still thinking of slipping this into a 2.13.x release under -Xsource:2.14, or is the new plan to do it in 2.14 only?

slipping this into a 2.13.x release

Pretty please ...

I expect we'll go with the syntax from the SIP: opaque type Name = String

What do you think of using a value class with a private constructor instead?

~~~ scala
// Example taken from the current SIP
package object opaquetypes {
opaque type Logarithm = Double

object Logarithm {
// These are the ways to lift to the logarithm type
def apply(d: Double): Logarithm = math.log(d)

def safe(d: Double): Option[Logarithm] =
  if (d > 0.0) Some(math.log(d)) else None

// This is the first way to unlift the logarithm type
def exponent(l: Logarithm): Double = l

// Extension methods define opaque types' public APIs
implicit class LogarithmOps(val `this`: Logarithm) extends AnyVal {
  // This is the second way to unlift the logarithm type
  def toDouble: Double = math.exp(`this`)
  def +(that: Logarithm): Logarithm = Logarithm(math.exp(`this`) + math.exp(that))
  def *(that: Logarithm): Logarithm = Logarithm(`this` + that)
}

}
}
~~~

Alternate syntax, using private constructors of value classes:

~~~ scala
package object opaquetypes {
class Logarithm private (private val value: Double) extends AnyVal {
// This is the first way to unlift the logarithm type
def toDouble: Double = math.exp(value)
def +(that: Logarithm): Logarithm = Logarithm(math.exp(this.value) + math.exp(that.value))
def *(that: Logarithm): Logarithm = Logarithm(this.value + that.value)
}

object Logarithm {
// These are the ways to lift to the logarithm type
def apply(d: Double): Logarithm = new Logarithm(math.log(d))

def safe(d: Double): Option[Logarithm] =
  if (d > 0.0) Some(Logarithm(d)) else None

// This is the second way to unlift the logarithm type
def exponent(l: Logarithm): Double = l.value

}
}
~~~

The benefit is that we don’t need to introduce a new keyword, we can use plain old methods instead of extension methods, and also, it makes the syntax more similar to value classes, which is a good thing in my opinion because these features are similar.

On the other hand, the statu quo has the advantage of having a syntax similar to “transparent” type aliases: defining an opaque type is just a matter of adding an opaque modifier in front of a type alias definition. Also, I’m not sure what’s the future of value classes, but if we are going to deprecate or not promote them, then it makes no sense to try having a similar syntax for opaque types.

I don't know if it has been considered in the SIP, but would it make sense to allow for type bounds in the opaque type definition?

opaque type Logarithm <: Double = Double

That would allow to use Logarithm where Double is expected, but not Double where Logarithm is expected.

What do you think of using a value class with a private constructor instead?

This was the very first design. It was rejected because that syntax is completely at odds with the semantics. Value classes are classes: they have a classOf, one can faithfully pattern-match on them, they can implement interfaces, etc. Basically value classes have all the semantics of classes, with the exception of identity.

Opaque types do not have any of these things. Their semantics are literally the same as (transparent) type aliases, with one exception: they do not participate in subtying relationships outside the companion object. So using the type alias syntax with a modifier is a much better fit for the specific semantics that they carry.

Will this be backported to 2.12.x?

No, 2.12 is in maintenance mode.
On Sun, Dec 16, 2018 at 06:36 kerr notifications@github.com wrote:

Will this be backported to 2.12.x?

—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/scala/scala-dev/issues/504#issuecomment-447619172,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AAFjyw2GmwkRYkTGOP-NOHj7u0elKhMuks5u5dvSgaJpZM4T6Fdm
.

Will the opaque type keyword support type parameters? e.g. opaque type Timestamp[T <: TimeUnit] = Long?

Yes, see e.g. the immutable array example in the SIP: https://docs.scala-lang.org/sips/opaque-types.html#immutable-ie-write-once-arrays

Now that 2.13.0 has been released, what's the current thinking on how long it is likely to be until this ships?

I can see a way to support the self type encoding in 2.13 and support a subset of 3.0 opaque types in 2.14 (you’d have to be a bit more explicit inside the scope of the opaque type that your selecting the type member on this and not on the containing object). I can elaborate after scala days if you’re curious :) as soon as we’ve recovered from 2.13 and the conf, we’ll work on sharing and refining the 2.14 roadmap.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

SethTisue picture SethTisue  Â·  5Comments

adriaanm picture adriaanm  Â·  6Comments

SethTisue picture SethTisue  Â·  4Comments

adriaanm picture adriaanm  Â·  7Comments

lrytz picture lrytz  Â·  4Comments