Migrating library classes to value classes
Remi Forax
forax at univ-mlv.fr
Mon Mar 4 22:44:22 UTC 2024
> From: "Kevin Bourrillion" <kevinb9n at gmail.com>
> To: valhalla-spec-experts at openjdk.org
> Sent: Monday, March 4, 2024 7:56:36 PM
> Subject: Migrating library classes to value classes
> Hey everyone,
> Most language changes affect only implementation code, but a few like record
> classes and now value classes are API-affecting: they permit new API shapes to
> be expressed, and thereby new ways for a client class to depend on a library
> class.
> These features are very carefully made backward-compatible, but there's a sort
> of second-level backward compatibility as well: can a library owner fully
> exploit the feature in a way that is *itself* backward-compatible for its
> clients? Looking at things this way brings in a whole host of new issues (since
> libraries often get stuck on old versions and often get run on a wide range of
> Java versions newer than the one they built and tested on, among other
> reasons).
> Record classes seem to come out well in this regard. From what I can tell, there
> should be many "record-like" classes today that can smoothly become records
> that all clients will still compile against and work with. Some `getFoo()`
> methods might have to be preserved as pass-throughs to `foo()` and maybe some
> details like that, but it looks pretty clean to me (?).
> But migrating to a value class is rescinding functionality and so inherently
> *not* backward-compatible. So, libraries can only do it via a multi-step
> process, over time. This thread is happening because I think this project
> probably wants to support that process, but I'm not clear yet on how it would
> do so.
> If we did nothing else, we'd be counting on something like [
> https://github.com/jspecify/jspecify/issues/488 | this JSpecify feature request
> ] to happen (please skim that, it helps explain what I'm even talking about in
> this thread). If we feel fine about hoping for the best on that front, then you
> can skip the rest of this message (I'd shift my argument to petitioning Oracle
> to support that effort!). That is, the remainder is motivated by the assumption
> that Java itself wants to actively support these migrations.
> Anyway: it looks very compelling to me to put that `@ValueClass` annotation,
> such as described, into Java itself, and have javac support it in the very same
> way it's going to support value class validation. (Even, of course, applying it
> to Integer and friends.)
> I'd even ask whether we could take it a step further. Once Java introduces the
> `value` keyword, there is still a lot of value (ugh) in having the annotation,
> and having it mean *exactly* the same thing, with all the same effects. This
> has a feeling of heresy to it, but it does something pretty powerful I think:
> it allows library owners to write forward -compatible code, that does the right
> thing whether its clients are using Java N-1 or Java N. Which makes the event
> of its users upgrading to Java N ... a little closer to a non- event.
> I suspect this of being reasonable. Since that Java N-1 client won't be able to
> use `non-atomic` or `!` or `?` or whatever we're calling the kind of
> constructor that makes the class a "never-null" type, I think the Java N-1 VM
> is within its rights to do almost nothing about the `value` keyword. (Perhaps
> prevent synchronization, and let's stick a pin in a discussion about equality
> semantics for the moment.) Which is good because it wouldn't know how to do
> those things.
> In fact, the earlier a version of Java we could backport this annotation and its
> javac support to, the better, AFAICT. With each version earlier we could push
> it, that much more code gets to become safely forward-compatible today.
> This is a much longer discussion but let me cut this message off here as a
> conversation-starter. Please start poking holes!
I do not think you need support of javac (i.e. change the spec) for that because we already have the multi-release jar, and a multi-release jar is basically a forward-compatible vehicle for libraries.
Using a multi-release jar that uses preview features has been used in the past by several opensource projects, the most popular is, i believe, Lucene that uses the preview versions of Panama to manage (unmap) mmap files.
For a class that you want to see both as an identity class and a value class, either you use Maven or Gradle (see [1]) but you have to hand manage the different versions or you use a special bytecode rewriter that look for annotations internal to your project and duplicate the classes (it seems a good use case for the classfile API) with one version being an identity class and one version being a value classes. And you can generalize that approach using JSpecify annotations for tagging fields/parameters that uses value classes with an implicit constructor.
I've used the bytecode rewriter approach in my project civilizer [2], which is based on a previous version of Valhalla (the last binary preview) and it works well.
Rémi
[1] [ https://maven.apache.org/plugins/maven-compiler-plugin/multirelease.html | https://maven.apache.org/plugins/maven-compiler-plugin/multirelease.html ]
[2] [ https://github.com/forax/civilizer | https://github.com/forax/civilizer ]
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/valhalla-spec-experts/attachments/20240304/05a07297/attachment.htm>
More information about the valhalla-spec-experts
mailing list