Constructor Interfaces
Red IO
redio.development at gmail.com
Sat Feb 18 05:35:48 UTC 2023
I took myself some time and reviewed all the points in this discussion.
I dropped the idea of injecting a hidden argument since on second thought
it would be messy and prone to leak the argument without extensive care
which would be much work and it would cause weird and unexpected
incompatibilities.
My conclusion is that mandating static method / constructors to be present
on a generic type opens many new concepts and coding strategies. The yield
of the feature would probably be high and could be applied in unexpected
places and in some cases move runtime checks/errors to compile time.
But; there is just no good way to resolve these restrictions at runtime in
the current implementation of java generics. The _only_ way to access
static data of a type variable at runtime is to have the class instance
available at runtime which requires the (planned) universal generics.
Since this feature is like I explained directly dependent on universal
generics now I will not spend any more time on this until/if universal
generics are near completion.
Great regards
RedIODev
On Tue, Jan 31, 2023, 04:18 Joseph D. Darcy <joe.darcy at oracle.com> wrote:
> To add to what Brian and others have said on this thread, annotation
> processors as first shipped in JDK 5.0 with apt and later standardized
> through JSR 269 in Java SE 6 were intentionally designed to *not* allow
> direct modification of ASTs to avoid language fragmentation, etc.
>
> However, it was acknowledged that at least some subset of the ability to
> modify sources was useful so annotation processors supported the ability to
> generate missing subclasses and *superclasses* during processing. Those
> capabilities are sufficient to implement a properties-like scheme, as
> sketched out in this blog entry:
>
>
> https://web.archive.org/web/20100410203408/http://blogs.sun.com/darcy/entry/properties_via_annotation_processing
>
> and later productized in projects like:
>
> http://immutables.github.io/immutable.html
>
> HTH,
>
> -Joe
> On 1/30/2023 6:36 AM, Brian Goetz wrote:
>
> Annotations are exactly as (not) powerful as they were designed to be.
> JSR 175 (https://www.jcp.org/en/jsr/detail?id=175) -- the effort that
> gave us annotations -- is called a _metadata facility for the Java
> Language_, not a _metaprogramming facility_. This choice was not made
> because the leaders of that effort were ignorant about metaprogramming; the
> goal was to leave interpretation of annotations to things like service
> containers, test frameworks, etc, because annotations are too weak a
> mechanism for providing language semantics.
>
> As Ron pointed out, it is a very common mistake when imagining possible
> language features to focus only on "if I had X, what cool code could I
> write?", and ignore the "if everyone had X, what would be the effect on
> readability, reliability, and maintainability of the global codebase?" But
> understanding the latter is where much of the challenge lies in responsibly
> evolving a language.
>
>
>
>
>
> On 1/30/2023 5:26 AM, Red IO wrote:
>
> 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/20230218/2f46a211/attachment-0001.htm>
More information about the amber-dev
mailing list