Constructor Interfaces

Red IO redio.development at gmail.com
Mon Jan 30 10:26:52 UTC 2023


After exploring the world of annotation processors trying to implement test
versions of the features, I discovered that annotation processors can't
officially edit the ast and Lombok uses undocumented hacks to achieve this.
This is a bit of a problem for implementing the test versions that way. My
question is why is this disallowed? It would be a really powerful tool for
compile time code execution/ generation.
Great regards
RedIODev

On Wed, Jan 25, 2023, 20:19 Red IO <redio.development at gmail.com> wrote:

> If the type information is an opt in feature it would not violate that
> rule. Adding an erasing parameter would still be a non breaking change. But
> if you opt in to request the type information and write logic depending on
> that information then this is a braking change to the code anyway since the
> logic wouldn't work with a raw type anyway. This could be another factor to
> push away from utilizing raw types. It could be a keyword you add to a
> parameterized thing to opt in (or implicitly opt in by utilizing the
> extended type information) and in the same act disable the raw version for
> it. Meaning that it would be a compile error to use the parameterized thing.
>
> Another option and preserve raw compatibility would be to exclude the
> hidden parameter in a raw instance. The downside to this approach would be
> that 2 signatures for every method/constructor that would otherwise include
> the hidden parameter would be required. Also for generic classes there
> would be 2 class layouts 1 with the hidden fields and 1 without.
>
> A completely different approach would be a static map of object instances
> to parameter lists. This would require 0 changes in the class itself. But
> in case of many generic objects loaded the performance of this approach is
> likely catastrophic. Should be tested if proving viable though. Another
> challenge is how a constructor would register the objects extended type
> information without an extra parameter. Also this would be difficult to
> implement for methods who are so short lived that the type parameter
> registration would likely take as long as the method itself needs to
> complete.
>
> Personally I would vote for braking with raw types (first approach) it
> wouldn't harm existing code (since opt in), would provide the simplest (and
> probably fastest) version and the only downside would be a harder and
> braking transition for api which still use raw types and want to utilize
> the extended type information (but as I already mentioned if an api wants
> to opt in to extended type information on a class; having raw instances of
> it would make absolutely no sense anyway).
>
> Great regards
> RedIODev
>
> On Wed, Jan 25, 2023, 19:29 <forax at univ-mlv.fr> wrote:
>
>> [private]
>>
>> ------------------------------
>>
>> *From: *"Red IO" <redio.development at gmail.com>
>> *To: *"Remi Forax" <forax at univ-mlv.fr>
>> *Cc: *"amber-dev" <amber-dev at openjdk.org>
>> *Sent: *Wednesday, January 25, 2023 3:17:34 PM
>> *Subject: *Re: Constructor Interfaces
>>
>> I proposed an idea (to Valhalla) to overcome type erasure that used the
>> same idea of desugering and a hidden argument that carries the erased types
>> class variable inside the generic context, but it was rejected as a to
>> naive approach or something and they where "already working on different
>> solutions for some time".
>>
>>
>> What you have proposed is very similar to what Kotlin does, pass
>> supplementary type arguments as parameter arguments/fields.
>> This does not work, the Java spec explicitly says that adding type
>> parameters, if there were previously none, is a backward compatible change
>> but adding a supplementary parameter is not a backward compatible change
>> (especially not a binary backward compatible change).
>>
>> That why the current proposed design pass the type arguments as a side
>> channel not encoded in the type descriptor, see
>>   https://cr.openjdk.java.net/~jrose/values/parametric-vm.pdf
>>
>> regards,
>> Rémi
>>
>>
>> Great regards RedIODev
>>
>> On Wed, Jan 25, 2023, 14:51 Remi Forax <forax at univ-mlv.fr> wrote:
>>
>>> We may need something like this for Valhalla, when we will revisit how
>>> to constraint the type arguments of universal generics.
>>>
>>> The kind of constraints you describe on type parameters already exist in
>>> C# or TypeScript and was more recently introduced in Go, and there is the
>>> type class of Haskell too.
>>>
>>> regards,
>>> Rémi
>>>
>>> ------------------------------
>>>
>>> *From: *"Red IO" <redio.development at gmail.com>
>>> *To: *"amber-dev" <amber-dev at openjdk.org>
>>> *Sent: *Wednesday, January 25, 2023 8:03:14 AM
>>> *Subject: *Constructor Interfaces
>>>
>>> Summary
>>> -------
>>>
>>> Enable a parameterized class to constrain the parameterized type to be
>>> constructible with a given list of parameters.
>>>
>>>
>>>
>>> Motivation
>>> ----------
>>>
>>> It is possible since JDK 8 to get a constructor (method) reference of an
>>> object. This allowed for the creation of an unknown class with a known
>>> constructor reference. But currently the only way to obtain such reference
>>> is at call site like this:
>>> Box<String> stringBox = new Box<>(String::new);
>>>
>>> It is inconvenient for the user to supply the the reference themselves
>>> and can confuse them as the type of the parameter is something like
>>> Supplier<String> which doesn't require the pased reference to be a
>>> constructor.
>>> It also clutters api's like "toArray" which requires an IntFunction to
>>> be type safe.
>>>
>>> Description
>>> -----------
>>>
>>> ConstructorInterface
>>> A ConstructorInterface is a special kind of interface similar to a
>>> FunctionalInterface. It also has similar constraints. It only allows
>>> abstract constructors and no other abstract methods. It can declare
>>> multiple constructors though. The definition of such interface would look
>>> similar to this:
>>>
>>> @ConstructorInterface //optional validation like FunctionalInterfaces
>>> public interface DefaultConstructible {
>>> new();
>>> new(char[] chars);
>>> }
>>>
>>> A parameterized type could declare this interface as a type bound for
>>> its parameter and therefore enabling it to be constructed safely. Like this:
>>> public class Box<E extends DefaultConstructible> {
>>> public Box() {
>>> E newElement = new E();
>>> }
>>> }
>>> The containing type is not forced to implement the ContructorInterface
>>> explicitly. It is implicitly implemented if the required constructor(s)
>>> is(are) present.
>>> public static void main(String[] args) {
>>> Box<String> stringBox = new Box<>(); //compiles because String has the
>>> required constructors.
>>> Box<java.sql.Date> dateBox new Box<>(); error: java.sql.Data does not
>>> satisfy the type bound DefaultConstructible
>>> }
>>> The interface might not be implemented by any class, since it doesn't
>>> follow the inheritance rule that extending classes of those who implement
>>> it also implement it. This requirement comes from the fact that extending
>>> classes do not necessarily need to have the same constructor signature and
>>> therefore don't qualify the requirements for the interface. Another option
>>> would be that extending classes of classes that implement a constructor
>>> interface explicitly are also required to supply the necessary constructors.
>>>
>>> class Foo implements DefaultConstructable {
>>> //both required by the interface
>>> public Foo() {}
>>> public Foo(char[] chars) {}
>>> }
>>>
>>> class Bar extends Foo {
>>> //the requirement for the required constructors is passed down.
>>> public Bar() {}
>>> public Bar(char[] chars) {}
>>> }
>>>
>>>
>>>
>>> public static <T extends Foo> T createT() {
>>> return new T();
>>> }
>>>
>>> public <T extends Foo> T wrapper() {
>>> return createT();
>>> }
>>> This would technically work but would require a lot of static analysis
>>> to find the real type of T to call its constructor.
>>> Restricting the use of "new T()" to type parameters that specify a
>>> constructor interface directly and only allow those to be resolved with a
>>> concrete type rather than another type parameter.
>>>
>>> Alternatives
>>> ------------
>>> An alternative would be to introduce new syntax to restrict the ability
>>> of certain constructors on a parameter type. Like c# does (but only for the
>>> default constructor) :
>>> public static T foo<T>() where T: new() {
>>> return new T();
>>> }
>>> In java:
>>> public static <T extends new()> T foo() {
>>> return new T();
>>> }
>>> The downside of this approach is obviously the introduction of new
>>> syntax rather than reusing the interface/inheritance syntax.
>>>
>>> Another alternative to this approach could be to implement static
>>> abstract methods. This would allow an interface to mandate a static Factory
>>> Method. The downside of this approach is that it requires the parameter
>>> class to actually implement the interface and the concept of type erasure
>>> would need to be addressed for static abstract methods to work. In contrast
>>> the ConstructorInterface enables every class that matches its contract to
>>> pass the type bound.
>>>
>>>
>>>
>>> Risks and Assumptions
>>> ---------------------
>>>
>>> As mentioned before the restriction the interface is giving on a type
>>> bound is different to normal interfaces, it restricts by its containing
>>> abstract constructors not by the type itself. It also makes use of the new
>>> operator on type variables.
>>>
>>>
>>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/amber-dev/attachments/20230130/58ff8849/attachment-0001.htm>


More information about the amber-dev mailing list