How Scala 3 Dotty compiles opaque types to Java code

As a brief experiment today I took a quick look at how Scala 3 (Dotty) opaque types are compiled. I started with this Scala/Dotty code in a file named OpaqueTypes.scala:

object OpaqueType:
 
    opaque type SSN = String
    object SSN:
        def apply(s: String): SSN = s

Then I compiled that code with dotc:

$ dotc OpaqueTypes.scala

Then I decompiled the resulting files with jad. I found this Java source code in a file named OpaqueType.jad:

import java.io.Serializable;
import scala.runtime.ModuleSerializationProxy;

public final class OpaqueType {

    public static final class SSN.
    implements Serializable {

        private Object writeReplace() {
            return new ModuleSerializationProxy(OpaqueType$SSN$);
        }

        public String apply(String s) {
            return s;
        }

        public static final SSN. MODULE$ = this;

        static {
            new SSN.();
        }

        public SSN.() {
        }
    }

}

I don’t know much yet about Dotty’s ModuleSerializationProxy, but it seems to be a key to using opaque types.

And I found this Java code in a file named OpaqueType$.jad:

import java.io.Serializable;
import scala.runtime.ModuleSerializationProxy;

public final class OpaqueType$
implements Serializable {

    private OpaqueType$() {}

    private Object writeReplace() {
        return new ModuleSerializationProxy(OpaqueType$);
    }

    public static final OpaqueType$ MODULE$ = this;
    public final OpaqueType.SSN. SSN;

    static {
        new OpaqueType$();
    }
}

I hope to write more about this in the future, but for today, if you’re interested in how Scala3/Dotty opaque types compile down to Java code, I hope this example is helpful.