I have a use case where pattern matching on opaque types when done via the unapply method of the companion would make a lot of sense. This is a use case where we do not want to pattern match on the underlying opaque object directly though. We only want to give users of the library a way to have a view on the opaque object that can be used in pattern matching.
The general use case comes to light when writing libraries that abstract between many different implementations of a particular standard or Mathematical abstraction.
In our use case we have a large and evolving suite of standards from the W3C that have been implemented in Java in at least 4 ways, in JavaScript the same, in WASM in C and Rust (not to mention Ruby, and many other languages) over the past 20 years. When I worked at Sun Microsystems in 2005 I asked the Java communities if they wanted to agree on a common interface for their RDF libs so that their frameworks could be interoperable, but they felt that the time was not ripe. Later they felt there was too much invested in the frameworks. The usual Java Solution is to use the Adapter Pattern, (two java libs from Apache do that) but that means every object ends up needing to be wrapped, producing double the number of objects, which is a lot when you move from every relation consisting of 4 objects, to a situation where every relation consists of 8 objects.
In 2011 I discovered that Scala allows us to sidestep that whole problem and that one could write a library that would just abstract these differences, but without adding those indirection layers that end up being expensive in terms of memory and cpu.
The answer in Scala3 is to use Opaque Types, which amazingly ends up incurring no cost at all!
But there is a problem as we wish to design a developer friendly library, in that opaque types don’t allow pattern matching.
So if one looks at the test case:
bKt.asMatchable match
case Triple(sub,URI(rel),obj) => assertEquals(sub,bbl)
we see two problems:
- I need to use
asMatchable
for the Triple.unapply to work - The code won’t compile because it can’f find that URI is a matchable, i.e. it cannot find the URI.unapply
In both those cases I don’t want the unapply or pattern matching to give me the underlying structure of each of the libraries, the one hidden by the opaque keyword. Rather we want to be able to have a coherent view that is the same over all implementations. This would be given by the unapply
function in the companion objects.
So here we have an algebra of opaque data structures that fit together in a coherent way, and we want to abstract over the implementations so that people can write generic scala RDF code that will compile to use any of the 10 libraries mentioned and more. E.g. starting from a generic class MyProgram[Rdf<:RDF](...)
we should be able to produce one version for HP’s Jena object ProgHp extends MyProgram[Jena]
and one for IBM’s object ProgIBM extends MyProgram[Rdf4J]
, etc…
I have written projects using banana-rdf that work just like that, and it is a real pleasure not to have to worry about implementation issues. That was written in Scala 2, used type projections, which were leaky (one could easily come to end up being confused with methods from the underlying types), and also did not work well with pattern matching. Now we have to rewrite them for Scala 3 and having pattern matching work correctly would make for a very good library.
PS. I hope this is the right forum to post this idea to.