From vicente.romero at oracle.com Mon Jul 3 16:06:40 2023 From: vicente.romero at oracle.com (Vicente Romero) Date: Mon, 3 Jul 2023 12:06:40 -0400 Subject: Question about circular references In-Reply-To: References: Message-ID: <2485f939-bdd0-d2d2-00c8-063e7cb0f308@oracle.com> Hi David, The question about record's mutability has been asked several times actually in this same list there are some previous ones asking for this same issue. As a reference there is a very interesting one covering this one an other issues related to records [1] and please see Brian's answer [2] which as mentioned covers not only mutability but other topics too. Thanks, Vicente [1] https://mail.openjdk.org/pipermail/amber-dev/2020-April/005900.html [2] https://mail.openjdk.org/pipermail/amber-dev/2020-May/005972.html On 6/30/23 18:28, David Alayachew wrote: > Hello all, > > First off, please let me know if I have CC'd the wrong groups. I've > CC'd the Amber Dev Team since this involves records, but it's not > specifically about records. > > --- > > For the past couple of weeks, I have been building a program that uses > a State Transition Diagram (aka State Machine, Finite Automata, etc. > -- https://en.wikipedia.org/wiki/Finite-state_machine) to model part > of its control flow. I've been using some Amber features to facilitate > this (and having a wonderful time of it), but then I hit a snag. > > Here is a(n EXTREMELY) simplified version of my actual problem. > Imagine I have code like the following. > > ```java > > sealed interface Node permits StartNode, BranchingNode, EndNode > {...unrelated stuff here...} > > record StartNode (Node a, Node b, Node c) implements > Node {} > record BranchingNode (Node a, Node b, Node c, ...fields > unrelated to transitioning...) implements Node {} > record EndNode (...fields unrelated to transitioning...) implements > Node {} > > ``` > > This type hierarchy is meant to represent a control flow of sorts. > Control flow is (imo) best modeled using a State Transition Diagram, > so I instinctively reached for that. And since my API needed to be > nothing but the data (each Node needed to be tightly coupled to my > internal state representation), I realized that this is an ideal use > case for records. > > Things worked out well enough until I tried to model a circular > relationship. > > Through chance, all of my control flows up to this point were > tree-like, so I could model them by starting from the "leaves," then > climbing up until I got to the "roots". To use State Transition > Diagram terminology, I started from my exit states and modeled my way > up to my entry states. > > For example, assume that my State Transition Diagram is as so. > > S ---a---> T > S ---b---> U > S ---c---> V > T ---a---> U > T ---b---> V > T ---c---> E > U ---a---> V > U --b|c--> E > V -a|b|c-> E > > S is my StartNode, and E is my ExitNode. > > In this case, modeling with records is easy. It would look like so. > > ```java > > ExitNode e = new ExitNode<>(...unrelated...); > BranchingNode v = new BranchingNode<>(e, e, e, > ...unrelated...); > BranchingNode u = new BranchingNode<>(v, e, e, > ...unrelated...); > BranchingNode t = new BranchingNode<>(u, v, e, > ...unrelated...); > StartNode s = new StartNode<>(t, u, v); > > return s; > > ``` > > But once I hit a circular reference, I could not figure out how to > model the code using the same format. > > For example, what if I say the following instead? > > V ---a---> T > > How do I model that using my current representation? > > Obviously, I could change my representation, but all of them required > me to "taint" my representation in incorrect ways. > > For example, I could swap out my records for simple classes where the > references to Node's were mutable. But I strongly disapprove of this > strategy because these nodes do NOT have a mutable relationship. > Obviously, I could put something in the Javadoc, but I want to fix the > incorrect representation, not put a warning sign in front of it. > > Also, I could use indirection, like having a separate Map whose values > are the actual Node references and the keys would be a record > Pair(String nodeId, Branch branch) {} where Branch is enum Branch { > a, b, c, ; } and then give each Node an id, changing my record to now > be record BranchingNode (String id, ...the same as above...) {}. > But ignoring the fact that I now have to deal with managing an id, > I've also added a lot of unnecessary bloat and indirection just to get > the circular reference I wanted. What should be a direct relationship > now requires a Map lookup. > > In that same vein, someone suggested that I use pattern-matching for > switch, but that would require me to create a new switch expression > for every single state. That's even more verbose and indirect than the > Map. At least with the map, I can put them all in one expression. This > strategy has an expression for each state! > > I've been told that there is another pathway involving reflection, but > it basically amounts to breaking the rules of Java. Apparently, you > can turn off finality to insert in fields you want, and then turn it > back on? I liked this idea the least compared to all of the others, so > I didn't pursue it any further. > > In the end, I decided to go down the Map lookup route. But I just > wanted to point out my experience with this because it was a > surprising and annoying speed bump along an otherwise smooth road. I > didn't think that something as small as a circular reference would > require me to uproot my entire solution. > > And finally, I want to emphasize that the same blockers above apply no > matter what pathway I go down. I had actually tried implementing this > first as an enum before I tried a record, since an enum would more > accurately represent my state. > > ```java > > enum State > { > > V(T, EXIT, EXIT), //FAILURE -- T cannot be referenced yet > U(V, EXIT, EXIT), > T(U, V, EXIT), > ; > > ...public final fields and constructor... > > } > > ``` > > But even then, the same problem occurred -- I can't reference an enum > value until it has been declared. I thought going down the path of > records would give me the flexibility I wanted, but no dice. > > It reminded me of that one programming meme. > > > * High Quality > > * Quickly Built > > * Low Cost > > > > You can only pick 2 > > But instead, it's > > * Circular Relationship > * Immutability > * Direct References > > What are your thoughts? Is this a problem in your eyes too? Or simply > a non-issue? > > Thank you for your time and insight! > David Alayachew -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Mon Jul 3 16:59:44 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Mon, 3 Jul 2023 12:59:44 -0400 Subject: Question about circular references In-Reply-To: <2485f939-bdd0-d2d2-00c8-063e7cb0f308@oracle.com> References: <2485f939-bdd0-d2d2-00c8-063e7cb0f308@oracle.com> Message-ID: Hello Vicente, Thank you for your response! > The question about record's mutability has been asked > several times actually in this same list there are some > previous ones asking for this same issue. I think you may have misread my post Vicente. Let me be clear here -- I do not want records to be mutable. That is not my goal at all with my post. Mutability would make my situation much WORSE because mutability would result in my model inaccurately representing how the control flow works. The purpose of my post was to see if anyone felt the same about how circular references are forced to work in Java. As is, I see no way to have circular references while maintaining immutability and direct references. You must either do it through reflection, or sacrifice either immutability or direct references. I wanted to know if anyone else was bothered by that fact and felt it was a problem that should be solved. > As a reference there is a very interesting one covering > this one an other issues related to records [1] and > please see Brian's answer [2] which as mentioned covers > not only mutability but other topics too. > > Thanks, > Vicente > > [1] https://mail.openjdk.org/pipermail/amber-dev/2020-April/005900.html > [2] https://mail.openjdk.org/pipermail/amber-dev/2020-May/005972.html Indeed, both links are very interesting, but they are entirely orthogonal to my post. My post is discussing my pain points with how circular references are in Java, and "querying the crowd" to see everyone else's thoughts on whether or not this is a problem worth solving. They are talking about a list of want-to-haves for records, followed by a detailed explanation from Brian on why those are not (currently) viable. The reason I added the amber list is because in this case, I found a dictionary-definition use case for records, but I am unable to use them there because of this circular reference problem. I felt that the amber team might find value in discussing this too, since this prevents records from being used for their defined purpose. Thank you for your time! David Alayachew -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Mon Jul 3 17:03:17 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Mon, 3 Jul 2023 13:03:17 -0400 Subject: Question about circular references In-Reply-To: References: <2485f939-bdd0-d2d2-00c8-063e7cb0f308@oracle.com> Message-ID: As a side note, here is a related post I made that captures a very similar problem, but for enums and lambdas instead of records. https://stackoverflow.com/questions/75072937/why-does-my-lambda-get-illegal-forward-reference-but-my-anonymous-class-does-no -------------- next part -------------- An HTML attachment was scrubbed... URL: From ron.pressler at oracle.com Mon Jul 3 21:10:51 2023 From: ron.pressler at oracle.com (Ron Pressler) Date: Mon, 3 Jul 2023 21:10:51 +0000 Subject: Question about circular references In-Reply-To: References: Message-ID: <6A1AD073-ABCC-4D1B-AC35-7978556D2D8E@oracle.com> Hi. While records represent simple data, not all simple data can and should be directly represented as records. Records are specifically tuples (i.e. product types), and cyclic data structures are not tuples. The issue is not immutability, but that tuples do not represent cyclic data, at least not directly (a set of tuples ? pairs, actually ? could, however, represent the set of all edges in a graph, even a cyclic graph). ? Ron > On 30 Jun 2023, at 23:28, David Alayachew wrote: > > Hello all, > > First off, please let me know if I have CC'd the wrong groups. I've CC'd the Amber Dev Team since this involves records, but it's not specifically about records. > > --- > > For the past couple of weeks, I have been building a program that uses a State Transition Diagram (aka State Machine, Finite Automata, etc. -- https://en.wikipedia.org/wiki/Finite-state_machine) to model part of its control flow. I've been using some Amber features to facilitate this (and having a wonderful time of it), but then I hit a snag. > > Here is a(n EXTREMELY) simplified version of my actual problem. Imagine I have code like the following. > > ```java > > sealed interface Node permits StartNode, BranchingNode, EndNode {...unrelated stuff here...} > > record StartNode (Node a, Node b, Node c) implements Node {} > record BranchingNode (Node a, Node b, Node c, ...fields unrelated to transitioning...) implements Node {} > record EndNode (...fields unrelated to transitioning...) implements Node {} > > ``` > > This type hierarchy is meant to represent a control flow of sorts. Control flow is (imo) best modeled using a State Transition Diagram, so I instinctively reached for that. And since my API needed to be nothing but the data (each Node needed to be tightly coupled to my internal state representation), I realized that this is an ideal use case for records. > > Things worked out well enough until I tried to model a circular relationship. > > Through chance, all of my control flows up to this point were tree-like, so I could model them by starting from the "leaves," then climbing up until I got to the "roots". To use State Transition Diagram terminology, I started from my exit states and modeled my way up to my entry states. > > For example, assume that my State Transition Diagram is as so. > > S ---a---> T > S ---b---> U > S ---c---> V > T ---a---> U > T ---b---> V > T ---c---> E > U ---a---> V > U --b|c--> E > V -a|b|c-> E > > S is my StartNode, and E is my ExitNode. > > In this case, modeling with records is easy. It would look like so. > > ```java > > ExitNode e = new ExitNode<>(...unrelated...); > BranchingNode v = new BranchingNode<>(e, e, e, ...unrelated...); > BranchingNode u = new BranchingNode<>(v, e, e, ...unrelated...); > BranchingNode t = new BranchingNode<>(u, v, e, ...unrelated...); > StartNode s = new StartNode<>(t, u, v); > > return s; > > ``` > > But once I hit a circular reference, I could not figure out how to model the code using the same format. > > For example, what if I say the following instead? > > V ---a---> T > > How do I model that using my current representation? > > Obviously, I could change my representation, but all of them required me to "taint" my representation in incorrect ways. > > For example, I could swap out my records for simple classes where the references to Node's were mutable. But I strongly disapprove of this strategy because these nodes do NOT have a mutable relationship. Obviously, I could put something in the Javadoc, but I want to fix the incorrect representation, not put a warning sign in front of it. > > Also, I could use indirection, like having a separate Map whose values are the actual Node references and the keys would be a record Pair(String nodeId, Branch branch) {} where Branch is enum Branch { a, b, c, ; } and then give each Node an id, changing my record to now be record BranchingNode (String id, ...the same as above...) {}. But ignoring the fact that I now have to deal with managing an id, I've also added a lot of unnecessary bloat and indirection just to get the circular reference I wanted. What should be a direct relationship now requires a Map lookup. > > In that same vein, someone suggested that I use pattern-matching for switch, but that would require me to create a new switch expression for every single state. That's even more verbose and indirect than the Map. At least with the map, I can put them all in one expression. This strategy has an expression for each state! > > I've been told that there is another pathway involving reflection, but it basically amounts to breaking the rules of Java. Apparently, you can turn off finality to insert in fields you want, and then turn it back on? I liked this idea the least compared to all of the others, so I didn't pursue it any further. > > In the end, I decided to go down the Map lookup route. But I just wanted to point out my experience with this because it was a surprising and annoying speed bump along an otherwise smooth road. I didn't think that something as small as a circular reference would require me to uproot my entire solution. > > And finally, I want to emphasize that the same blockers above apply no matter what pathway I go down. I had actually tried implementing this first as an enum before I tried a record, since an enum would more accurately represent my state. > > ```java > > enum State > { > > V(T, EXIT, EXIT), //FAILURE -- T cannot be referenced yet > U(V, EXIT, EXIT), > T(U, V, EXIT), > ; > > ...public final fields and constructor... > > } > > ``` > > But even then, the same problem occurred -- I can't reference an enum value until it has been declared. I thought going down the path of records would give me the flexibility I wanted, but no dice. > > It reminded me of that one programming meme. > > > * High Quality > > * Quickly Built > > * Low Cost > > > > You can only pick 2 > > But instead, it's > > * Circular Relationship > * Immutability > * Direct References > > What are your thoughts? Is this a problem in your eyes too? Or simply a non-issue? > > Thank you for your time and insight! > David Alayachew From davidalayachew at gmail.com Mon Jul 3 22:35:48 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Mon, 3 Jul 2023 18:35:48 -0400 Subject: Question about circular references In-Reply-To: <6A1AD073-ABCC-4D1B-AC35-7978556D2D8E@oracle.com> References: <6A1AD073-ABCC-4D1B-AC35-7978556D2D8E@oracle.com> Message-ID: Hello Ron, Thank you for your response! > The issue is not immutability So let me reiterate what I said to Vicente -- I did not and do not take issue with records being immutable. Immutability is a good thing, and I did not and do not want that to go away. > While records represent simple data, not all simple data > can and should be directly represented as records. > Records are specifically tuples (i.e. product types), and > cyclic data structures are not tuples. ??? The dictionary definition of "product type" disagrees with you, correct? Let me post a snippet of the definition here. "In programming languages and type theory, a product of types is another, compounded, type in a structure. The "operands" of the product are types, and the structure of a product type is determined by the fixed order of the operands in the product. An instance of a product type retains the fixed order, but otherwise MAY CONTAIN ALL POSSIBLE INSTANCES OF ITS PRIMITIVE DATA TYPES." "If there are only two component types, it can be called a "pair type". For example, if two component types A and B are THE SET OF ALL POSSIBLE VALUES OF THAT TYPE, the product type written A ? B contains elements that are pairs (a,b), where "a" and "b" are instances of A and B respectively." Am I misreading this? Because even after multiple readings of it, the definition seems clear, obvious, and in direct contradiction to what you are saying. What am I missing here? > The issue is[...]that tuples do not > represent cyclic data, at least not directly Again, I am struggling to see where you pulling this logic and definition from. It seems counter to what everyone else calls a product type or a tuple. Here is another snippet. "In type theory a product type of two types A and B is the type whose terms are ordered pairs (a,b) with a:A and b:B." "In a model of the type theory in categorical semantics, this is a product. In set theory, it is a CARTESIAN PRODUCT. In dependent type theory, it is a special case of a dependent sum." Help me out here. Thank you for your time! David Alayachew -------------- next part -------------- An HTML attachment was scrubbed... URL: From ron.pressler at oracle.com Mon Jul 3 22:59:13 2023 From: ron.pressler at oracle.com (Ron Pressler) Date: Mon, 3 Jul 2023 22:59:13 +0000 Subject: [External] : Re: Question about circular references In-Reply-To: References: <6A1AD073-ABCC-4D1B-AC35-7978556D2D8E@oracle.com> Message-ID: When I said that immutability is not the issue I meant that there can be immutable cyclic graphs ? so immutability isn?t the thing preventing them ? but you can?t have tuples (and tuples are also immutable) directly represent a cyclic graph (in the reference-following sense). Again, tuples/records can represent arbitrary graphs in the standard mathematical way by representing edges, but not in the direct ?OOP? reference-following way. Put another way, you can have an immutable reference-based representation of a cyclic graph, but not one that?s directly made of records (which are also obviously immutable). Records are simple immutable data, but not every simple immutable data structure is directly representable by records. Because there can be immutable cyclic graphs I said that it?s not immutability that?s the issue. (Whether or not we can talk of mutable tuples is a separate matter; I would say no, at least not in the most rigorous sense) > On 3 Jul 2023, at 23:35, David Alayachew wrote: > > > Hello Ron, > > Thank you for your response! > > > The issue is not immutability > > So let me reiterate what I said to Vicente -- I did not and do not take issue with records being immutable. Immutability is a good thing, and I did not and do not want that to go away. > > > While records represent simple data, not all simple data > > can and should be directly represented as records. > > Records are specifically tuples (i.e. product types), and > > cyclic data structures are not tuples. > > ??? > > The dictionary definition of "product type" disagrees with you, correct? Let me post a snippet of the definition here. > > "In programming languages and type theory, a product of types is another, compounded, type in a structure. The "operands" of the product are types, and the structure of a product type is determined by the fixed order of the operands in the product. An instance of a product type retains the fixed order, but otherwise MAY CONTAIN ALL POSSIBLE INSTANCES OF ITS PRIMITIVE DATA TYPES." > > "If there are only two component types, it can be called a "pair type". For example, if two component types A and B are THE SET OF ALL POSSIBLE VALUES OF THAT TYPE, the product type written A ? B contains elements that are pairs (a,b), where "a" and "b" are instances of A and B respectively." > > Am I misreading this? Because even after multiple readings of it, the definition seems clear, obvious, and in direct contradiction to what you are saying. What am I missing here? > > > The issue is[...]that tuples do not > > represent cyclic data, at least not directly > > Again, I am struggling to see where you pulling this logic and definition from. It seems counter to what everyone else calls a product type or a tuple. > > Here is another snippet. > > "In type theory a product type of two types A and B is the type whose terms are ordered pairs (a,b) with a:A and b:B." > > "In a model of the type theory in categorical semantics, this is a product. In set theory, it is a CARTESIAN PRODUCT. In dependent type theory, it is a special case of a dependent sum." > > Help me out here. > > Thank you for your time! > David Alayachew > From davidalayachew at gmail.com Mon Jul 3 23:22:09 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Mon, 3 Jul 2023 19:22:09 -0400 Subject: [External] : Re: Question about circular references In-Reply-To: References: <6A1AD073-ABCC-4D1B-AC35-7978556D2D8E@oracle.com> Message-ID: Hello Ron, Thank you for your response! > When I said that immutability is not the issue I meant > that there can be immutable cyclic graphs ? so > immutability isn?t the thing preventing them ? but you > can?t have tuples (and tuples are also immutable) > directly represent a cyclic graph (in the > reference-following sense). Again, tuples/records can > represent arbitrary graphs in the standard mathematical > way by representing edges, but not in the direct ?OOP? > reference-following way. And thank you for the clarification. This makes much more sense. In that case, this inability in the reference-following way is actually the part that I am taking issue. I am saying that this inability is a problem. The purpose of my original post was to say that I think this ia a problem, and I want to see if anyone else thinks it is too. The fact that it is not representable, imo, limits us from being able to more accurately write programs that correctly model the domain we're building for. > Put another way, you can have an immutable > reference-based representation of a cyclic graph, but not > one that?s directly made of records (which are also > obviously immutable). Records are simple immutable data, > but not every simple immutable data structure is directly > representable by records. I understand you now. You are essentially saying that in Java, this is simply not representable because of the way OOP and references work in Java. And this is the part that I am taking issue with and querying the crowd for. > Because there can be immutable cyclic graphs I said that > it?s not immutability that?s the issue. (Whether or not > we can talk of mutable tuples is a separate matter; I > would say no, at least not in the most rigorous sense) I would agree. Mutable tuples have a use, but I don't see enough benefit for them atm. And either way, it does not solve my problem since immutability is what I need in order for my model to truly be accurate. But what are your thoughts on my post? Do you think this lack of representability is a problem too? Thank you for your time and help! David Alayachew -------------- next part -------------- An HTML attachment was scrubbed... URL: From wesleyegberto at gmail.com Tue Jul 4 00:54:27 2023 From: wesleyegberto at gmail.com (Wesley Egberto) Date: Mon, 3 Jul 2023 21:54:27 -0300 Subject: Unnamed Classes with Initializers Message-ID: Hi all First off, please let me know if I have sent this to the wrong group. I'm exploring the JEPs in JDK 21, with the JEP 445 Unnamed Classes and Instance Main Methods I'm getting a compiler error when using instance and static initializers. I would like to know if I misunderstood the JEP or if I'm missing something. The JEP says: "An unnamed class is almost exactly like an explicitly declared class. Its > members can have the same modifiers (e.g., private and static) and the > modifiers have the same defaults (e.g., package access and instance > membership). *The class can have static initializers as well as instance > initializers.* One key difference is that while an unnamed class has a > default zero-parameter constructor, it can have no other constructor." When I try to compile the following code: static { System.out.println("static initializer"); } { System.out.println("instance initializer"); } void main() { System.out.println("instance main method"); } I got the following error: UnnamedClassWithInitializers.java:1: error: class, interface, enum, or record expected static { ^ UnnamedClassWithInitializers.java:3: error: class, interface, enum, or record expected } ^ UnnamedClassWithInitializers.java:7: error: class, interface, enum, or record expected } ^ UnnamedClassWithInitializers.java:11: error: class, interface, enum, or record expected } ^ 4 errors error: compilation failed Thank you for your time and work! Wesley Egberto -------------- next part -------------- An HTML attachment was scrubbed... URL: From ron.pressler at oracle.com Tue Jul 4 01:03:54 2023 From: ron.pressler at oracle.com (Ron Pressler) Date: Tue, 4 Jul 2023 01:03:54 +0000 Subject: [External] : Re: Question about circular references In-Reply-To: References: <6A1AD073-ABCC-4D1B-AC35-7978556D2D8E@oracle.com> Message-ID: > On 4 Jul 2023, at 00:22, David Alayachew wrote: > > > But what are your thoughts on my post? Do you think this lack of representability is a problem too? > I don?t see a lack of representability. Java?s records can represent tuples ? the thing they are meant to represent. To represent a potentially cyclic graph with tuples you can use the ordinary representation of edges, and if you want to represent a graph in a more OOPy/pointery way, i.e. not with tuples, use classes that aren?t tuples, which are also representable in Java (and you can also make them immutable if you like). ? Ron From davidalayachew at gmail.com Tue Jul 4 01:04:17 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Mon, 3 Jul 2023 21:04:17 -0400 Subject: Unnamed Classes with Initializers In-Reply-To: References: Message-ID: Hello Wesley, Thank you for reaching out! I can reproduce your error. Here are my error logs for context. ~~~ $ java --enable-preview --source 21 abc.java abc.java:5: error: class, interface, enum, or record expected static { ^ abc.java:7: error: class, interface, enum, or record expected } ^ abc.java:11: error: class, interface, enum, or record expected } ^ Note: abc.java uses preview features of Java SE 21. Note: Recompile with -Xlint:preview for details. 3 errors error: compilation failed $ cat abc.java void main() { System.out.println("Main"); } static { System.out.println("Static"); } { System.out.println("Instance"); } $ java --version openjdk 21-ea 2023-09-19 OpenJDK Runtime Environment (build 21-ea+29-2411) OpenJDK 64-Bit Server VM (build 21-ea+29-2411, mixed mode, sharing) $ javac --version javac 21-ea Thank you for your time and help! David Alayachew -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Tue Jul 4 01:08:21 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Mon, 3 Jul 2023 21:08:21 -0400 Subject: Unnamed Classes with Initializers In-Reply-To: References: Message-ID: If I had to guess, they simply implemented part of the feature thus far. The rest will probably come some point soon. -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Tue Jul 4 01:42:55 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Mon, 3 Jul 2023 21:42:55 -0400 Subject: [External] : Re: Question about circular references In-Reply-To: References: <6A1AD073-ABCC-4D1B-AC35-7978556D2D8E@oracle.com> Message-ID: Hello Ron, Thank you for your response! > I don?t see a lack of representability. Java?s records > can represent tuples ? the thing they are meant to > represent. Apologies, I was not clear here. My question to you is actually, do you see a problem with the fact that records cannot represent a circular reference to themselves? According to the definitions I cited earlier, tuples/product types have circular references in their domain of possible values, and therefore, we do have a lack of representability if I model my data as nodes, and not edges. The fact that you can sidestep that by modelling it as edges does not change the fact that you do indeed have a gap of representability. And to be clear, this question rises above State Transition Diagrams and just talks about circular references alone. > and if you want to represent a graph in a more > OOPy/pointery way, i.e. not with tuples, use classes that > aren?t tuples, which are also representable in Java (and > you can also make them immutable if you like). Are you saying you can accomplish this without indirection, as in without a Map lookup or something equivalent? If so, please demonstrate. > To represent a potentially cyclic graph with tuples you > can use the ordinary representation of edges I understand your point, but this requires me to apply an ill fitting model because the language is unable to map the one I actually need. I am modeling control flow. The question that I am constantly asking is "What are my next possible steps from this current node?" Rather than having the node store that information directly, you are telling me to create a set of edges and simply perform a lookup on this set, filtering down to the ones that start/end (assuming bidirectional) with my current node so that I can figure out what my next option is. Do you see what I mean when I say that this ia a bad fit? Yes, technically, that works, but this is even worse in the department of indirection. I didn't want to avoid indirection because indirection is in and of itself bad. I wanted to avoid indirection so that my model would have as little intellectual and data overhead as possible. I wanted to be able to answer my question as directly as possible so that I can reason about my problem as easily as possible. Your solution goes completely in the opposite direction of that and adds so much overhead that I would sooner choose the workarounds I mentioned originally. And I'm sure that your strategy would be more useful in a weighted graph or something, but hopefully you see where I am coming from here? Thank you for your time and insight! David Alayachew -------------- next part -------------- An HTML attachment was scrubbed... URL: From redio.development at gmail.com Tue Jul 4 07:39:46 2023 From: redio.development at gmail.com (redio.development at gmail.com) Date: Tue, 4 Jul 2023 09:39:46 +0200 Subject: AW: [External] : Re: Question about circular references In-Reply-To: References: <6A1AD073-ABCC-4D1B-AC35-7978556D2D8E@oracle.com> Message-ID: <0e1b01d9ae4a$b481d200$1d857600$@gmail.com> I think the main problem as I understand it is, that you cannot forward declare the identity of an object. So it?s technically not a record problem but a class problem. If you could forward declare an uninitialized Object reference like this pseudo code does: EndNode e = new EndNode(); BranchNode v = uninit; BranchNode u = new BranchNode(v, e, e); BranchNode t = new BranchNode(u, v, e); v = new BranchNode(t, e, e); StartNode s = new StartNode(t, u, v); You wouldn?t have that problem. The problem here is that you would need the notion of references that are neither null nor an object but in some sort of null with identity that is later initialized. An easy solution would be to go for an ID instead of java references. IDs have the benefit that you can forward declare them: public class Test { public static void main(String[] args) { EndNode e = new EndNode(UUID.randomUUID()); UUID vUUID = UUID.randomUUID(); BranchNode u = new BranchNode(UUID.randomUUID(), vUUID, e.self(), e.self()); BranchNode t = new BranchNode(UUID.randomUUID(), u.self(), vUUID, e.self()); BranchNode v = new BranchNode(vUUID, t.self(), e.self(), e.self()); StartNode s = new StartNode(UUID.randomUUID(), t.self(), u.self(), v.self()); } } class NodeStore { public static final Map map = new HashMap<>(); } sealed interface Node { UUID self(); } record StartNode(UUID self, UUID a, UUID b, UUID c) implements Node { public StartNode { NodeStore.map.put(self, this); } } record BranchNode(UUID self, UUID a, UUID b, UUID c) implements Node { public BranchNode { NodeStore.map.put(self, this); } } record EndNode(UUID self) implements Node { public EndNode { NodeStore.map.put(self, this); } } The downside is that you are responsible to manage the IDs instead of the runtime. Hope that helps Great Regards RedIODev Von: amber-dev Im Auftrag von David Alayachew Gesendet: Dienstag, 4. Juli 2023 03:43 An: Ron Pressler Cc: amber-dev Betreff: Re: [External] : Re: Question about circular references Hello Ron, Thank you for your response! > I don?t see a lack of representability. Java?s records > can represent tuples ? the thing they are meant to > represent. Apologies, I was not clear here. My question to you is actually, do you see a problem with the fact that records cannot represent a circular reference to themselves? According to the definitions I cited earlier, tuples/product types have circular references in their domain of possible values, and therefore, we do have a lack of representability if I model my data as nodes, and not edges. The fact that you can sidestep that by modelling it as edges does not change the fact that you do indeed have a gap of representability. And to be clear, this question rises above State Transition Diagrams and just talks about circular references alone. > and if you want to represent a graph in a more > OOPy/pointery way, i.e. not with tuples, use classes that > aren?t tuples, which are also representable in Java (and > you can also make them immutable if you like). Are you saying you can accomplish this without indirection, as in without a Map lookup or something equivalent? If so, please demonstrate. > To represent a potentially cyclic graph with tuples you > can use the ordinary representation of edges I understand your point, but this requires me to apply an ill fitting model because the language is unable to map the one I actually need. I am modeling control flow. The question that I am constantly asking is "What are my next possible steps from this current node?" Rather than having the node store that information directly, you are telling me to create a set of edges and simply perform a lookup on this set, filtering down to the ones that start/end (assuming bidirectional) with my current node so that I can figure out what my next option is. Do you see what I mean when I say that this ia a bad fit? Yes, technically, that works, but this is even worse in the department of indirection. I didn't want to avoid indirection because indirection is in and of itself bad. I wanted to avoid indirection so that my model would have as little intellectual and data overhead as possible. I wanted to be able to answer my question as directly as possible so that I can reason about my problem as easily as possible. Your solution goes completely in the opposite direction of that and adds so much overhead that I would sooner choose the workarounds I mentioned originally. And I'm sure that your strategy would be more useful in a weighted graph or something, but hopefully you see where I am coming from here? Thank you for your time and insight! David Alayachew -------------- next part -------------- An HTML attachment was scrubbed... URL: From gavin.bierman at oracle.com Tue Jul 4 10:13:19 2023 From: gavin.bierman at oracle.com (Gavin Bierman) Date: Tue, 4 Jul 2023 10:13:19 +0000 Subject: Question about circular references In-Reply-To: References: Message-ID: <1C8BFBCD-4DB6-4AD2-9BD3-604FD61F3957@oracle.com> Hi David, I think what you are asking for is ?why can?t I build cyclic record values?? This is a reasonable question - it?s a very common question in languages that are build to be immutable from the ground-up, e.g. functional languages. For these languages the solution is either to just add pointers (e.g Standard ML took this approach), or to support something more first class. If you look at F#, you?ll see that they support this - although it?s pretty hidden in the spec! They allow (i) let expressions to be recursive (with a REC keyword) and (ii) they provide the AND operator to define mutually recursive value bindings and type declarations. This leads to code like this (taken from https://learn.microsoft.com/en-us/dotnet/fsharp/language-reference/records ) // Create a Person type and use the Address type that is not defined type Person = { Name: string Age: int Address: Address } // Define the Address type which is used in the Person record and Address = { Line1: string Line2: string PostCode: string Occupant: Person } // Create a Person type and use the Address type that is not defined let rec person = { Name = "Person name" Age = 12 Address = { Line1 = "line 1" Line2 = "line 2" PostCode = "abc123" Occupant = person } } I think this is the sort of thing you are after, right? The issue is that this would create **all sorts of problems**, e.g. around DA/DU, initialisation semantics, pattern matching semantics, etc. etc. As Ron has suggested - if you want to build a cyclic structure then we already have fantastic technology for that - use classes. You want all the other fields to be immutable? Use final! Once we support patterns in classes, then you?ll get many of the advantages of using records as a client. With records we have carved out a subset of classes that fit the design, for which we can give a great experience without massive changes to the language and its semantics. We could have gone further but we would have to have made compromises that weren?t worth the price. Hope this helps, Gavin PS We also provide another option: Want to be a hacker? Make your record with array components and mutate the array contents to your heart's content! record Loop(String head, Loop[] tail){}; Loop[] tl = new Loop[]{null}; var l = new Loop("hello", tl); // A loop of hellos tl[0]=l; var tmp = l; for(int i=0; i<100; i++){ System.out.println(i+": "+tmp.head()); tmp=tmp.tail()[0]; } // Make l a loop of hello worlds tl[0]=new Loop("world", new Loop[]{l}); for(int i=0; i<100; i++){ System.out.println(i+": "+tmp.head()); tmp=tmp.tail()[0]; } (Don?t tell anyone I showed you this code :-) ) On 30 Jun 2023, at 23:28, David Alayachew wrote: Hello all, First off, please let me know if I have CC'd the wrong groups. I've CC'd the Amber Dev Team since this involves records, but it's not specifically about records. --- For the past couple of weeks, I have been building a program that uses a State Transition Diagram (aka State Machine, Finite Automata, etc. -- https://en.wikipedia.org/wiki/Finite-state_machine) to model part of its control flow. I've been using some Amber features to facilitate this (and having a wonderful time of it), but then I hit a snag. Here is a(n EXTREMELY) simplified version of my actual problem. Imagine I have code like the following. ```java sealed interface Node permits StartNode, BranchingNode, EndNode {...unrelated stuff here...} record StartNode (Node a, Node b, Node c) implements Node {} record BranchingNode (Node a, Node b, Node c, ...fields unrelated to transitioning...) implements Node {} record EndNode (...fields unrelated to transitioning...) implements Node {} ``` This type hierarchy is meant to represent a control flow of sorts. Control flow is (imo) best modeled using a State Transition Diagram, so I instinctively reached for that. And since my API needed to be nothing but the data (each Node needed to be tightly coupled to my internal state representation), I realized that this is an ideal use case for records. Things worked out well enough until I tried to model a circular relationship. Through chance, all of my control flows up to this point were tree-like, so I could model them by starting from the "leaves," then climbing up until I got to the "roots". To use State Transition Diagram terminology, I started from my exit states and modeled my way up to my entry states. For example, assume that my State Transition Diagram is as so. S ---a---> T S ---b---> U S ---c---> V T ---a---> U T ---b---> V T ---c---> E U ---a---> V U --b|c--> E V -a|b|c-> E S is my StartNode, and E is my ExitNode. In this case, modeling with records is easy. It would look like so. ```java ExitNode e = new ExitNode<>(...unrelated...); BranchingNode v = new BranchingNode<>(e, e, e, ...unrelated...); BranchingNode u = new BranchingNode<>(v, e, e, ...unrelated...); BranchingNode t = new BranchingNode<>(u, v, e, ...unrelated...); StartNode s = new StartNode<>(t, u, v); return s; ``` But once I hit a circular reference, I could not figure out how to model the code using the same format. For example, what if I say the following instead? V ---a---> T How do I model that using my current representation? Obviously, I could change my representation, but all of them required me to "taint" my representation in incorrect ways. For example, I could swap out my records for simple classes where the references to Node's were mutable. But I strongly disapprove of this strategy because these nodes do NOT have a mutable relationship. Obviously, I could put something in the Javadoc, but I want to fix the incorrect representation, not put a warning sign in front of it. Also, I could use indirection, like having a separate Map whose values are the actual Node references and the keys would be a record Pair(String nodeId, Branch branch) {} where Branch is enum Branch { a, b, c, ; } and then give each Node an id, changing my record to now be record BranchingNode (String id, ...the same as above...) {}. But ignoring the fact that I now have to deal with managing an id, I've also added a lot of unnecessary bloat and indirection just to get the circular reference I wanted. What should be a direct relationship now requires a Map lookup. In that same vein, someone suggested that I use pattern-matching for switch, but that would require me to create a new switch expression for every single state. That's even more verbose and indirect than the Map. At least with the map, I can put them all in one expression. This strategy has an expression for each state! I've been told that there is another pathway involving reflection, but it basically amounts to breaking the rules of Java. Apparently, you can turn off finality to insert in fields you want, and then turn it back on? I liked this idea the least compared to all of the others, so I didn't pursue it any further. In the end, I decided to go down the Map lookup route. But I just wanted to point out my experience with this because it was a surprising and annoying speed bump along an otherwise smooth road. I didn't think that something as small as a circular reference would require me to uproot my entire solution. And finally, I want to emphasize that the same blockers above apply no matter what pathway I go down. I had actually tried implementing this first as an enum before I tried a record, since an enum would more accurately represent my state. ```java enum State { V(T, EXIT, EXIT), //FAILURE -- T cannot be referenced yet U(V, EXIT, EXIT), T(U, V, EXIT), ; ...public final fields and constructor... } ``` But even then, the same problem occurred -- I can't reference an enum value until it has been declared. I thought going down the path of records would give me the flexibility I wanted, but no dice. It reminded me of that one programming meme. > * High Quality > * Quickly Built > * Low Cost > > You can only pick 2 But instead, it's * Circular Relationship * Immutability * Direct References What are your thoughts? Is this a problem in your eyes too? Or simply a non-issue? Thank you for your time and insight! David Alayachew -------------- next part -------------- An HTML attachment was scrubbed... URL: From ron.pressler at oracle.com Tue Jul 4 10:39:10 2023 From: ron.pressler at oracle.com (Ron Pressler) Date: Tue, 4 Jul 2023 10:39:10 +0000 Subject: [External] : Re: Question about circular references In-Reply-To: References: <6A1AD073-ABCC-4D1B-AC35-7978556D2D8E@oracle.com> Message-ID: <9ACE2710-F912-4297-873F-61139413B031@oracle.com> > On 4 Jul 2023, at 02:42, David Alayachew wrote: > > > Apologies, I was not clear here. My question to you is actually, do you see a problem with the fact that records cannot represent a circular reference to themselves? No, because records are meant to represent tuples, and tuples don?t self-reference. > > According to the definitions I cited earlier, tuples/product types have circular references in their domain of possible values They don?t. Let?s say that the product type T is defined as T := Integer x T. Then a tuple of type T can self-reference if and only if T (the second component) can self-reference. It?s perfectly consistent, therefore, for tuples to not self reference, and Java, like most languages with algebraic data types, says tuples don?t self-reference. You can?t construct a self-referencing instance of T because you?d need a component value that is the instance you?re constructing. It also ensures that recursions over values always terminate. > > Are you saying you can accomplish this without indirection, as in without a Map lookup or something equivalent? If so, please demonstrate. A set of pairs *is* a map (actually, a ?multi map", i.e. a map from elements to sets). You could implement a map from just tuples (as students learn in an introductory FP course: you represent the map as a list, and the list is a linked list of item-tail pairs, i.e. a cons list) but you?re better off just using an existing map implementation. > > I understand your point, but this requires me to apply an ill fitting model because the language is unable to map the one I actually need. But why did you decide that you need to do it *with records*? It?s not that the language isn?t able to represent the graph you want, but that you picked a language feature that is meant to represent tuples and you?re trying to use it to represent something that isn?t a tuple. Maybe you did it because you want to use pattern-matching and records have pattern-matching, but we would like to offer pattern-matching for other classes, too. ? Ron From duncan.macgregor at servicenow.com Tue Jul 4 11:16:51 2023 From: duncan.macgregor at servicenow.com (Duncan MacGregor) Date: Tue, 4 Jul 2023 11:16:51 +0000 Subject: Question about circular references In-Reply-To: <1C8BFBCD-4DB6-4AD2-9BD3-604FD61F3957@oracle.com> References: <1C8BFBCD-4DB6-4AD2-9BD3-604FD61F3957@oracle.com> Message-ID: As Gavin says, you can create what you want with non-record classes, and you can even do it with immutable objects! jshell> class A { ...> final B b; ...> A () { ...> b = new B(this); ...> } ...> } | created class A, however, it cannot be referenced until class B is declared jshell> class B { ...> final A a; ...> B (A a) { ...> this.a = a; ...> } ...> } | created class B Using a combination of lambdas or similar to capture that work that needs to be done and leaking `this` out of the constructor you can produce bigger cycles, but for more complex graphs you?d need to do some work to create everything in the right order (or maybe complete abuse Loom?s threads to yield in the middle of a constructor and pass references around in a queue), but it?s completely evil and probably not worth the effort. You can even pull the above trick with records, but you?ll need an extra level of cunning because you must delegate to the canonical constructor, so you?d need to find somewhere else to stash all the state you?re passing around (private thread local maybe?) and pass entirely dummy values into that constructor, and it?s even more not worth it. You?ll also find that this sort of thing break lots of stuff, such as printing values in jshell because there is an implicit assumption that nobody would be quite this evil. If you were going to be this evil then I think it would be easier to just indirect everything through a single array list or something and use indexes rather than refs. Duncan. From: amber-dev on behalf of Gavin Bierman Date: Tuesday, 4 July 2023 at 11:14 To: David Alayachew Cc: amber-dev Subject: Re: Question about circular references [External Email] ________________________________ Hi David, I think what you are asking for is ?why can?t I build cyclic record values?? This is a reasonable question - it?s a very common question in languages that are build to be immutable from the ground-up, e.g. functional languages. For these languages the solution is either to just add pointers (e.g Standard ML took this approach), or to support something more first class. If you look at F#, you?ll see that they support this - although it?s pretty hidden in the spec! They allow (i) let expressions to be recursive (with a REC keyword) and (ii) they provide the AND operator to define mutually recursive value bindings and type declarations. This leads to code like this (taken from https://learn.microsoft.com/en-us/dotnet/fsharp/language-reference/records ) // Create a Person type and use the Address type that is not defined type Person = { Name: string Age: int Address: Address } // Define the Address type which is used in the Person record and Address = { Line1: string Line2: string PostCode: string Occupant: Person } // Create a Person type and use the Address type that is not defined let rec person = { Name = "Person name" Age = 12 Address = { Line1 = "line 1" Line2 = "line 2" PostCode = "abc123" Occupant = person } } I think this is the sort of thing you are after, right? The issue is that this would create **all sorts of problems**, e.g. around DA/DU, initialisation semantics, pattern matching semantics, etc. etc. As Ron has suggested - if you want to build a cyclic structure then we already have fantastic technology for that - use classes. You want all the other fields to be immutable? Use final! Once we support patterns in classes, then you?ll get many of the advantages of using records as a client. With records we have carved out a subset of classes that fit the design, for which we can give a great experience without massive changes to the language and its semantics. We could have gone further but we would have to have made compromises that weren?t worth the price. Hope this helps, Gavin PS We also provide another option: Want to be a hacker? Make your record with array components and mutate the array contents to your heart's content! record Loop(String head, Loop[] tail){}; Loop[] tl = new Loop[]{null}; var l = new Loop("hello", tl); // A loop of hellos tl[0]=l; var tmp = l; for(int i=0; i<100; i++){ System.out.println(i+": "+tmp.head()); tmp=tmp.tail()[0]; } // Make l a loop of hello worlds tl[0]=new Loop("world", new Loop[]{l}); for(int i=0; i<100; i++){ System.out.println(i+": "+tmp.head()); tmp=tmp.tail()[0]; } (Don?t tell anyone I showed you this code :-) ) On 30 Jun 2023, at 23:28, David Alayachew wrote: Hello all, First off, please let me know if I have CC'd the wrong groups. I've CC'd the Amber Dev Team since this involves records, but it's not specifically about records. --- For the past couple of weeks, I have been building a program that uses a State Transition Diagram (aka State Machine, Finite Automata, etc. -- https://en.wikipedia.org/wiki/Finite-state_machine) to model part of its control flow. I've been using some Amber features to facilitate this (and having a wonderful time of it), but then I hit a snag. Here is a(n EXTREMELY) simplified version of my actual problem. Imagine I have code like the following. ```java sealed interface Node permits StartNode, BranchingNode, EndNode {...unrelated stuff here...} record StartNode (Node a, Node b, Node c) implements Node {} record BranchingNode (Node a, Node b, Node c, ...fields unrelated to transitioning...) implements Node {} record EndNode (...fields unrelated to transitioning...) implements Node {} ``` This type hierarchy is meant to represent a control flow of sorts. Control flow is (imo) best modeled using a State Transition Diagram, so I instinctively reached for that. And since my API needed to be nothing but the data (each Node needed to be tightly coupled to my internal state representation), I realized that this is an ideal use case for records. Things worked out well enough until I tried to model a circular relationship. Through chance, all of my control flows up to this point were tree-like, so I could model them by starting from the "leaves," then climbing up until I got to the "roots". To use State Transition Diagram terminology, I started from my exit states and modeled my way up to my entry states. For example, assume that my State Transition Diagram is as so. S ---a---> T S ---b---> U S ---c---> V T ---a---> U T ---b---> V T ---c---> E U ---a---> V U --b|c--> E V -a|b|c-> E S is my StartNode, and E is my ExitNode. In this case, modeling with records is easy. It would look like so. ```java ExitNode e = new ExitNode<>(...unrelated...); BranchingNode v = new BranchingNode<>(e, e, e, ...unrelated...); BranchingNode u = new BranchingNode<>(v, e, e, ...unrelated...); BranchingNode t = new BranchingNode<>(u, v, e, ...unrelated...); StartNode s = new StartNode<>(t, u, v); return s; ``` But once I hit a circular reference, I could not figure out how to model the code using the same format. For example, what if I say the following instead? V ---a---> T How do I model that using my current representation? Obviously, I could change my representation, but all of them required me to "taint" my representation in incorrect ways. For example, I could swap out my records for simple classes where the references to Node's were mutable. But I strongly disapprove of this strategy because these nodes do NOT have a mutable relationship. Obviously, I could put something in the Javadoc, but I want to fix the incorrect representation, not put a warning sign in front of it. Also, I could use indirection, like having a separate Map whose values are the actual Node references and the keys would be a record Pair(String nodeId, Branch branch) {} where Branch is enum Branch { a, b, c, ; } and then give each Node an id, changing my record to now be record BranchingNode (String id, ...the same as above...) {}. But ignoring the fact that I now have to deal with managing an id, I've also added a lot of unnecessary bloat and indirection just to get the circular reference I wanted. What should be a direct relationship now requires a Map lookup. In that same vein, someone suggested that I use pattern-matching for switch, but that would require me to create a new switch expression for every single state. That's even more verbose and indirect than the Map. At least with the map, I can put them all in one expression. This strategy has an expression for each state! I've been told that there is another pathway involving reflection, but it basically amounts to breaking the rules of Java. Apparently, you can turn off finality to insert in fields you want, and then turn it back on? I liked this idea the least compared to all of the others, so I didn't pursue it any further. In the end, I decided to go down the Map lookup route. But I just wanted to point out my experience with this because it was a surprising and annoying speed bump along an otherwise smooth road. I didn't think that something as small as a circular reference would require me to uproot my entire solution. And finally, I want to emphasize that the same blockers above apply no matter what pathway I go down. I had actually tried implementing this first as an enum before I tried a record, since an enum would more accurately represent my state. ```java enum State { V(T, EXIT, EXIT), //FAILURE -- T cannot be referenced yet U(V, EXIT, EXIT), T(U, V, EXIT), ; ...public final fields and constructor... } ``` But even then, the same problem occurred -- I can't reference an enum value until it has been declared. I thought going down the path of records would give me the flexibility I wanted, but no dice. It reminded me of that one programming meme. > * High Quality > * Quickly Built > * Low Cost > > You can only pick 2 But instead, it's * Circular Relationship * Immutability * Direct References What are your thoughts? Is this a problem in your eyes too? Or simply a non-issue? Thank you for your time and insight! David Alayachew -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Tue Jul 4 11:46:44 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Tue, 4 Jul 2023 07:46:44 -0400 Subject: [External] : Re: Question about circular references In-Reply-To: <0e1b01d9ae4a$b481d200$1d857600$@gmail.com> References: <6A1AD073-ABCC-4D1B-AC35-7978556D2D8E@oracle.com> <0e1b01d9ae4a$b481d200$1d857600$@gmail.com> Message-ID: Hello REDIODev, Thank you for your response! So, your solution definitely works, but this falls under the indirection I was talking about in my original post. You can go back to the June archives to see what I mean. Indirection itself isn't bad, but it adds overhead in ways that wouldn't need to be there if it was direct references to records like we have for tree examples. Thank you for your input! David Alayachew On Tue, Jul 4, 2023 at 3:39?AM wrote: > I think the main problem as I understand it is, that you cannot forward > declare the identity of an object. So it?s technically not a record problem > but a class problem. > > If you could forward declare an uninitialized Object reference like this > pseudo code does: > > EndNode e = new EndNode(); > > BranchNode v = uninit; > > BranchNode u = new BranchNode(v, e, e); > > BranchNode t = new BranchNode(u, v, e); > > v = new BranchNode(t, e, e); > > StartNode s = new StartNode(t, u, v); > > > > You wouldn?t have that problem. The problem here is that you would need > the notion of references that are neither null nor an object but in some > sort of null with identity that is later initialized. > > An easy solution would be to go for an ID instead of java references. IDs > have the benefit that you can forward declare them: > > > > public class Test { > > public static void main(String[] args) { > > EndNode e = new EndNode(UUID.randomUUID()); > > UUID vUUID = UUID.randomUUID(); > > BranchNode u = new BranchNode(UUID.randomUUID(), vUUID, e.self(), > e.self()); > > BranchNode t = new BranchNode(UUID.randomUUID(), u.self(), vUUID, > e.self()); > > BranchNode v = new BranchNode(vUUID, t.self(), e.self(), e.self > ()); > > StartNode s = new StartNode(UUID.randomUUID(), t.self(), u.self(), > v.self()); > > } > > } > > > > class NodeStore { > > public static final Map map = new HashMap<>(); > > } > > > > sealed interface Node { > > UUID self(); > > } > > > > record StartNode(UUID self, UUID a, UUID b, UUID c) implements Node { > > > > public StartNode { > > NodeStore.map.put(self, this); > > } > > } > > > > record BranchNode(UUID self, UUID a, UUID b, UUID c) implements Node { > > public BranchNode { > > NodeStore.map.put(self, this); > > } > > } > > > > record EndNode(UUID self) implements Node { > > public EndNode { > > NodeStore.map.put(self, this); > > } > > } > > > > The downside is that you are responsible to manage the IDs instead of the > runtime. > > > > Hope that helps > > > > Great Regards > > RedIODev > > > > *Von:* amber-dev *Im Auftrag von *David > Alayachew > *Gesendet:* Dienstag, 4. Juli 2023 03:43 > *An:* Ron Pressler > *Cc:* amber-dev > *Betreff:* Re: [External] : Re: Question about circular references > > > > > > Hello Ron, > > > > Thank you for your response! > > > > I don?t see a lack of representability. Java?s records > > can represent tuples ? the thing they are meant to > > represent. > > Apologies, I was not clear here. My question to you is actually, do you > see a problem with the fact that records cannot represent a circular > reference to themselves? > > According to the definitions I cited earlier, tuples/product types have > circular references in their domain of possible values, and therefore, we > do have a lack of representability if I model my data as nodes, and not > edges. The fact that you can sidestep that by modelling it as edges does > not change the fact that you do indeed have a gap of representability. > > And to be clear, this question rises above State Transition Diagrams and > just talks about circular references alone. > > > and if you want to represent a graph in a more > > OOPy/pointery way, i.e. not with tuples, use classes that > > aren?t tuples, which are also representable in Java (and > > you can also make them immutable if you like). > > Are you saying you can accomplish this without indirection, as in without > a Map lookup or something equivalent? If so, please demonstrate. > > > To represent a potentially cyclic graph with tuples you > > can use the ordinary representation of edges > > I understand your point, but this requires me to apply an ill fitting > model because the language is unable to map the one I actually need. > > I am modeling control flow. The question that I am constantly asking is > "What are my next possible steps from this current node?" Rather than > having the node store that information directly, you are telling me to > create a set of edges and simply perform a lookup on this set, filtering > down to the ones that start/end (assuming bidirectional) with my current > node so that I can figure out what my next option is. Do you see what I > mean when I say that this ia a bad fit? > > Yes, technically, that works, but this is even worse in the department of > indirection. I didn't want to avoid indirection because indirection is in > and of itself bad. I wanted to avoid indirection so that my model would > have as little intellectual and data overhead as possible. I wanted to be > able to answer my question as directly as possible so that I can reason > about my problem as easily as possible. Your solution goes completely in > the opposite direction of that and adds so much overhead that I would > sooner choose the workarounds I mentioned originally. > > And I'm sure that your strategy would be more useful in a weighted graph > or something, but hopefully you see where I am coming from here? > > > > Thank you for your time and insight! > > David Alayachew > -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Tue Jul 4 12:32:00 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Tue, 4 Jul 2023 08:32:00 -0400 Subject: [External] : Re: Question about circular references In-Reply-To: <9ACE2710-F912-4297-873F-61139413B031@oracle.com> References: <6A1AD073-ABCC-4D1B-AC35-7978556D2D8E@oracle.com> <9ACE2710-F912-4297-873F-61139413B031@oracle.com> Message-ID: Hello Ron, Thank you for your response! > > I understand your point, but this requires me to apply > > an ill fitting model because the language is unable to > > map the one I actually need. > > But why did you decide that you need to do it *with > records*? It?s not that the language isn?t able to > represent the graph you want, but that you picked a > language feature that is meant to represent tuples and > you?re trying to use it to represent something that isn?t > a tuple. > > Maybe you did it because you want to use pattern-matching > and records have pattern-matching, but we would like to > offer pattern-matching for other classes, too. I actually have no issue going outside of records to do this. A final class with final public fields would meet my needs just as well. But neither that final class nor a record can get past this circular reference problem because Java does not allow a circular reference while maintaining immutability and direct reference. By all means, if you can show me a well-fitting solution that accomplishes this with final classes WITHOUT dipping into indirection I've mentioned many times above or mutability (or reflection stuff), I'll take my metaphorical hat and go. And I understand if that feels like an unreasonable request. I am actually of the opinion that it is an impossible request. But please, prove me wrong. And as an aside, please take a look at Gavin's response. He points to another language that absolutely solved this problem. His F# example captures an example that meets all my needs for my representation. > > Are you saying you can accomplish this without > > indirection, as in without a Map lookup or something > > equivalent? If so, please demonstrate. > > A set of pairs *is* a map (actually, a ?multi map", i.e. > a map from elements to sets). You could implement a map > from just tuples (as students learn in an introductory FP > course: you represent the map as a list, and the list is > a linked list of item-tail pairs, i.e. a cons list) but > you?re better off just using an existing map > implementation. And as I mentioned in a previous response, once you turn this from a set of nodes to a set of edges with a map lookup, you shoot clarity in the foot to get something that is possible. As I said, that solution works, but it 100% does not meet the indirection need I have been describing this entire time. And if you recall from the last post, this Map lookup strategy that you are suggesting is the workaround I actually landed on, since it is the most effective of the ill-fitting solutions. But most effective does not make it a good fit. > > Apologies, I was not clear here. My question to you is > > actually, do you see a problem with the fact that > > records cannot represent a circular reference to > > themselves? > > No, because records are meant to represent tuples, and > tuples don?t self-reference. > > > According to the definitions I cited earlier, > > tuples/product types have circular references in their > > domain of possible values > > They don?t. Let?s say that the product type T is defined > as T := Integer x T. Then a tuple of type T can > self-reference if and only if T (the second component) > can self-reference. It?s perfectly consistent, therefore, > for tuples to not self reference, and Java, like most > languages with algebraic data types, says tuples don?t > self-reference. You can?t construct a self-referencing > instance of T because you?d need a component value that > is the instance you?re constructing. It also ensures that > recursions over values always terminate. Ron, you're using circular logic to avoid the question. My question was -- records currently cannot self-reference, do you think that is a problem? You respond saying -- No, because records are tuples, and tuples cannot self-reference. But when I challenge your definition of tuples, saying that they in fact CAN self-reference, you respond saying -- well, technically they can, but tuples in Java cannot because the requirement for self referencing is to contain a component that self-references, that component being the tuple type itself. My entire question is asking, do you think it is a problem that records cannot self-reference, and you say no because records cannot self-reference, even though tuples can. Please tell me you see how this is circular logic? And finally, please look at Gavin's post here -- https://mail.openjdk.org/pipermail/amber-dev/2023-July/008142.html He has successfully provided an example of circular references that meets all the points I was talking about. Thank you for your time and response! David Alayachew -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Tue Jul 4 13:06:25 2023 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 4 Jul 2023 13:06:25 +0000 Subject: Unnamed Classes with Initializers In-Reply-To: References: Message-ID: <0D59D3A5-5B21-41E5-9A23-B190D78E4501@oracle.com> What part of the feature do you think is missing? The compiler is behaving correctly in that it is rejecting an invalid program; static and instance initializers are not allowed in unnamed classes. However, the error messages could be more helpful. Sent from my iPad > On Jul 3, 2023, at 9:08 PM, David Alayachew wrote: > > ? > If I had to guess, they simply implemented part of the feature thus far. The rest will probably come some point soon. From davidalayachew at gmail.com Tue Jul 4 13:17:13 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Tue, 4 Jul 2023 09:17:13 -0400 Subject: Unnamed Classes with Initializers In-Reply-To: <0D59D3A5-5B21-41E5-9A23-B190D78E4501@oracle.com> References: <0D59D3A5-5B21-41E5-9A23-B190D78E4501@oracle.com> Message-ID: Hello Brian, Thank you for your response! Maybe me and Wesley are somehow wildly misreading the JEP, but here is a copy-and-paste. -- https://openjdk.org/jeps/445 "An unnamed class is almost exactly like an explicitly declared class. Its members can have the same modifiers (e.g., private and static) and the modifiers have the same defaults (e.g., package access and instance membership). **The class can have static initializers as well as instance initializers.** One key difference is that while an unnamed class has a default zero-parameter constructor, it can have no other constructor." Are we misreading this JEP? Thank you for your time and help! David Alayachew -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Tue Jul 4 13:22:10 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Tue, 4 Jul 2023 09:22:10 -0400 Subject: Question about circular references In-Reply-To: <1C8BFBCD-4DB6-4AD2-9BD3-604FD61F3957@oracle.com> References: <1C8BFBCD-4DB6-4AD2-9BD3-604FD61F3957@oracle.com> Message-ID: Hello Gavin, Thank you for your response! Let me start by saying your F# example meets all the needs I had for my original problem. Thank you for seeing my point. I will address your F# example momentarily. > As Ron has suggested - if you want to build a cyclic > structure then we already have fantastic technology for > that - use classes. You want all the other fields to be > immutable? Use final! Once we support patterns in > classes, then you?ll get many of the advantages of using > records as a client. Maybe you can help me out with that. I've had a very extensive back and forth with Ron, and I'm not seeing how to accomplish this with classes while maintaing directness and immutability. If I were to model this as a class, I would want this class to be nothing but the most relevant bits of data, transparent for all to see (you can see why I instictively reached for records here). That way, I can maintain as direct an abstraction as possible without any of the fluff. So, my first thought would simply be to have a class something along the lines of this. ```java public final class Node { //Unmodifiable public final Node t, u, v; public Node(Node t, Node u, Node v) { this.t = t; this.u = u; this.v = v; } //equals, hash, etc. } ``` But this still hits the same problem of not being able to reference a variable that hasn't been assigned yet. How would I get past this while using normal classes? Ron suggested modeling my data as edges, but once I try and switch from 8 Nodes with references to other Nodes and instead use (up to) 64 edges, I've blown up my example into something significantly more complex in order to work around the fact that the language does not permit immutable, direct self-references. The best workaround that I have found thus far is just to create a set of Nodes, then construct a Map lookup where I can find all edges that touch the given key. But this is the very workaround that I suggested in my original post, and it doesn't meet my needs because that strategy loses clarity almost immediately after you get any sort of scale. I want to be able to look at my code, and have the direct relationships between nodes be tightly coupled to the Node. I want it to be as easily as with a non-cyclic representation. Brian Goetz's Data-Oriented Programming has a good example of a tree like structure that captures what my goal is. And my original code example with T, U, and V shows exactly what I am talking about. There, I have minimized overhead so that the relationships can be as visible as possible. > With records we have carved out a subset of classes that > fit the design, for which we can give a great experience > without massive changes to the language and its > semantics. We could have gone further but we would have > to have made compromises that weren?t worth the price. Yes, I don't think that this is a problem with records per se. Rather, I think that this is a more general problem in Java itself that limits the realm of possible representations, and it just so happens that some of those off-limits representations are applicable to records too. Does that make sense? > PS We also provide another option: Want to be a hacker? Make your record with array components and mutate the array contents to your heart's content! > > record Loop(String head, Loop[] tail){}; > > Loop[] tl = new Loop[]{null}; > > var l = new Loop("hello", tl); > > // A loop of hellos > tl[0]=l; > > var tmp = l; > for(int i=0; i<100; i++){ > System.out.println(i+": "+tmp.head()); > tmp=tmp.tail()[0]; > } > > // Make l a loop of hello worlds > tl[0]=new Loop("world", new Loop[]{l}); > > for(int i=0; i<100; i++){ > System.out.println(i+": "+tmp.head()); > tmp=tmp.tail()[0]; > } > > (Don?t tell anyone I showed you this code :-) ) Hmmmmmmmmm, now that I think about, I remember reading somewhere else that there is a proposal for being able to "freeze" arrays. Freezing an array would make it unmodifiable, in the same way that Collections can be. If/when that JEP comes out, I could swap out my t, u, v for numeric transitions instead, then once I have finished, I can freeze my array to make it unmodifiable. I'll concede that, if frozen arrays ever come out, then this strategy meets all the needs I specified. However, I would far rather we get something like your F# example, which I will now address. > I think what you are asking for is ?why can?t I build > cyclic record values?? > > This is a reasonable question - it?s a very common > question in languages that are build to be immutable from > the ground-up, e.g. functional languages. For these > languages the solution is either to just add pointers > (e.g Standard ML took this approach), or to support > something more first class. If you look at F#, you?ll see > that they support this - although it?s pretty hidden in > the spec! They allow (i) let expressions to be recursive > (with a REC keyword) and (ii) they provide the AND > operator to define mutually recursive value bindings and > type declarations. This leads to code like this (taken > from https://learn.microsoft.com/en-us/dotnet/fsharp/language-reference/records ) > > // Create a Person type and use the Address type that is > // not defined > type Person = > { Name: string > Age: int > Address: Address } > // Define the Address type which is used in the Person > // record > and Address = > { Line1: string > Line2: string > PostCode: string > Occupant: Person } > > // Create a Person type and use the Address type that is > // not defined > let rec person = > { > Name = "Person name" > Age = 12 > Address = > { > Line1 = "line 1" > Line2 = "line 2" > PostCode = "abc123" > Occupant = person > } > } > > I think this is the sort of thing you are after, right? Lol. This would absolutely meet my needs if this were possible. > The issue is that this would create **all sorts of > problems**, e.g. around DA/DU, initialisation semantics, > pattern matching semantics, etc. etc. I see what you mean. It sounds like what you are saying here is that, the benefits this would provide would not be worth the costs it would incur? If so, I am willing to accept that this particular strategy would not be an effective fit for Java. But since you broached the subject (:P), RedIODev raised a point that I think might be the best solution out of this. Here is a link to the response in question -- https://mail.openjdk.org/pipermail/amber-dev/2023-July/008141.html RedIODev says "I think the main problem as I understand it is, that you cannot forward declare the identity of an object. So it?s technically not a record problem but a class problem." The F# example you described makes sense to me and the costs also make sense to me. But how do you feel about the ability to forward declare the identity of an object, and then use it before it's declared, with the compiler-enforced promise that I will declare and populate that object before ever attempting to dereference its pointer? I would even be fine with forcing the object to be declared/populated/initialized (what's the right word here?) in the same block that it's variable was declared at. Would this solution instead be a viable strategy? Thank you for your help and insight! David Alayachew -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Tue Jul 4 13:28:35 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Tue, 4 Jul 2023 09:28:35 -0400 Subject: Question about circular references In-Reply-To: References: <1C8BFBCD-4DB6-4AD2-9BD3-604FD61F3957@oracle.com> Message-ID: Hello Duncan, Thank you for your response! I'll address your point as a whole and say, I think we both agree, that right now, Java makes it so that indirection is the only real, viable way to represent this type of model. And even then, I am of the opinion that it doesn't scale nearly as easily as the direct variant would (unless you try to do in the edges format like Ron suggested here -- https://mail.openjdk.org/pipermail/amber-dev/2023-July/008137.html). But for a better example of what I was looking for, take a look at Brian Goetz's Data-Oriented Programming article. In it, he draws out a Binary Tree using Nodes. The representation is direct, just like in a linked list. I was hoping for a similar level of directness, but permitting circular references. Also, please see my last 2-3 paragraphs on this link ( https://mail.openjdk.org/pipermail/amber-dev/2023-July/008149.html) for what I think a possible future solution might look like. Thank you for your time and help! David Alayachew -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Tue Jul 4 13:32:19 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Tue, 4 Jul 2023 09:32:19 -0400 Subject: Question about circular references In-Reply-To: References: <1C8BFBCD-4DB6-4AD2-9BD3-604FD61F3957@oracle.com> Message-ID: Hello Gavin, Actually, in regards to my question about the viable strategy, let me make that a new thread. I'd rather keep the 2 discussions separate to facilitate searchability. -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Tue Jul 4 13:44:30 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Tue, 4 Jul 2023 09:44:30 -0400 Subject: Using variables before initializing them (was: Forward declaring the identity of an object) In-Reply-To: References: Message-ID: In retrospect, forward declaring may not have been the right title for this. Maybe, "Using variables before initializing, as long as they aren't dereferenced until after initialization?" -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Tue Jul 4 13:46:02 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Tue, 4 Jul 2023 09:46:02 -0400 Subject: Using variables before initializing them (was: Forward declaring the identity of an object) In-Reply-To: References: Message-ID: Looks like the first email never made it through, so let me copy and paste it here. Hello Amber Dev Team, I am starting this thread to start a spin off discussion from the "Question about circular references" thread above. In short, RedIODev mentioned something in passing that piqued my interest. The idea was the ability to reference a declared, but uninitialized variable (a reference), and allowing that reference to be passed to, let's say, another constructor, but not permitting derereference until the variable in question has fully been initialized. I wanted to get people's perspectives on this. Is this a good idea? A bad one? Is this too difficult to do? Not that difficult? Please let me know your thoughts. Thank you for your time and insight! David Alayachew -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Tue Jul 4 13:42:02 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Tue, 4 Jul 2023 09:42:02 -0400 Subject: Forward declaring the identity of an object Message-ID: Hello Amber Dev Team, I am starting this thread to start a spin off discussion from the "Question about circular references" thread above. In short, RedIODev mentioned something in passing that piqued my interest. The idea was the ability to reference a declared, but uninitialized variable (a reference), and allowing that reference to be passed to, let's say, another constructor, but not permitting derereference until the variable in question has fully been initialized. I wanted to get people's perspectives on this. Is this a good idea? A bad one? Is this too difficult to do? Not that difficult? Please let me know your thoughts. Thank you for your time and insight! David Alayachew -------------- next part -------------- An HTML attachment was scrubbed... URL: From james.laskey at oracle.com Tue Jul 4 13:53:04 2023 From: james.laskey at oracle.com (Jim Laskey) Date: Tue, 4 Jul 2023 13:53:04 +0000 Subject: Unnamed Classes with Initializers In-Reply-To: References: <0D59D3A5-5B21-41E5-9A23-B190D78E4501@oracle.com> Message-ID: That statement was left in error and has now been removed, On Jul 4, 2023, at 10:17 AM, David Alayachew wrote: Hello Brian, Thank you for your response! Maybe me and Wesley are somehow wildly misreading the JEP, but here is a copy-and-paste. -- https://openjdk.org/jeps/445 "An unnamed class is almost exactly like an explicitly declared class. Its members can have the same modifiers (e.g., private and static) and the modifiers have the same defaults (e.g., package access and instance membership). **The class can have static initializers as well as instance initializers.** One key difference is that while an unnamed class has a default zero-parameter constructor, it can have no other constructor." Are we misreading this JEP? Thank you for your time and help! David Alayachew -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Tue Jul 4 13:54:27 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Tue, 4 Jul 2023 09:54:27 -0400 Subject: Unnamed Classes with Initializers In-Reply-To: References: <0D59D3A5-5B21-41E5-9A23-B190D78E4501@oracle.com> Message-ID: Hello Jim, Thank you for your response! Ok, that clarifies everything. Thank you for your help! David Alayachew On Tue, Jul 4, 2023 at 9:53?AM Jim Laskey wrote: > That statement was left in error and has now been removed, > > On Jul 4, 2023, at 10:17 AM, David Alayachew > wrote: > > Hello Brian, > > Thank you for your response! > > Maybe me and Wesley are somehow wildly misreading the JEP, but here is a > copy-and-paste. -- https://openjdk.org/jeps/445 > > "An unnamed class is almost exactly like an explicitly declared class. Its > members can have the same modifiers (e.g., private and static) and the > modifiers have the same defaults (e.g., package access and instance > membership). > > **The class can have static initializers as well as instance > initializers.** > > One key difference is that while an unnamed class has a default > zero-parameter constructor, it can have no other constructor." > > Are we misreading this JEP? > > Thank you for your time and help! > David Alayachew > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From ron.pressler at oracle.com Tue Jul 4 14:50:08 2023 From: ron.pressler at oracle.com (Ron Pressler) Date: Tue, 4 Jul 2023 14:50:08 +0000 Subject: [External] : Re: Question about circular references In-Reply-To: References: <6A1AD073-ABCC-4D1B-AC35-7978556D2D8E@oracle.com> <9ACE2710-F912-4297-873F-61139413B031@oracle.com> Message-ID: > On 4 Jul 2023, at 13:32, David Alayachew wrote: > > > I actually have no issue going outside of records to do this. A final class with final public fields would meet my needs just as well. But neither that final class nor a record can get past this circular reference problem because Java does not allow a circular reference while maintaining immutability and direct reference. You?re repeating the same thing of reaching for a feature designed not for your use case. Records are immutable, but not everything that?s immutable is a record. Similarly, final fields do describe immutable data in Java, but not everything that?s immutable can or should be final. Final fields are a Java feature that guarantees some things about initialisation order and other initialisation-related guarantees, but you can, in this case, think of it as the feature you use when you need to ensure there are no cycles. So what you?re asking is, how do I use Java?s feature for excluding cycles to admit cycles? The answer is: you don?t. A common OOP pattern is to have a mutable builder than then uses private access to create immutable objects (don?t expose mutating methods). They can still be simple data carriers, and if and when we have deconstruction patterns for non-record classes they can make use of pattern matching, too. The language supports this just fine. It just doesn?t support it with the feature designed for tuples, because what you want isn?t a tuple, and it doesn?t support it with a feature designed to enforce a particular initialisation behaviour when it?s not the behaviour you want. Yes, you can rely on the fact that final fields are tied to constructors and so do some clever initialisation in the constructor, but my general point is that you?re reaching for features that were designed for other uses. ? Ron From davidalayachew at gmail.com Tue Jul 4 15:25:55 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Tue, 4 Jul 2023 11:25:55 -0400 Subject: [External] : Re: Question about circular references In-Reply-To: References: <6A1AD073-ABCC-4D1B-AC35-7978556D2D8E@oracle.com> <9ACE2710-F912-4297-873F-61139413B031@oracle.com> Message-ID: Hello Ron, Thank you for your response! > You?re repeating the same thing of reaching for a feature > designed not for your use case. Records are immutable, > but not everything that?s immutable is a record. > Similarly, final fields do describe immutable data in > Java, but not everything that?s immutable can or should > be final. Admittedly, I should have been more clear. But my point was, I don't particularly care if I use final fields either. Again, the only constraints I had were these. * Immutable * Nodes DIRECTLY referencing other Nodes -- no indirection, no id or map lookup -- pure direct references * Permit circular references Through all the responses I have been given, none of them have been able to hit all 3. I only started with records because the definition provided seemed to be a good fit for my problem. You seem to disagree, and I addressed your disagreements, but your response here doesn't attempt to retort on that. It only focuses on me talking about final fields, since I am fine using them instead, if need be. > Final fields are a Java feature that guarantees some > things about initialisation order and other > initialisation-related guarantees, but you can, in this > case, think of it as the feature you use when you need to > ensure there are no cycles. So what you?re asking is, how > do I use Java?s feature for excluding cycles to admit > cycles? The answer is: you don?t. To be honest, I actually question this logic too -- about thinking of final as not permitting cycles here. And even if that holds, I'm pretty sure that further discussion will lead to me criticizing that too, eventually. But regardless, you are declaring this out of the blue. Where are you coming from when you say this? What backs this statement up? I have come to you with widely accepted definitions. If you are going to say that final fields, in this instance, don't permit cycles, I need you to explain how and why, not just claim that they do. > A common OOP pattern is to have a mutable builder than > then uses private access to create immutable objects > (don?t expose mutating methods). They can still be simple > data carriers, and if and when we have deconstruction > patterns for non-record classes they can make use of > pattern matching, too. By all means, I am not picky. Can you show me what this would look like though? I can understand how you would achieve immutability doing this, but not the directness constraint I am talking about above. > The language supports this just fine. It just doesn?t > support it with the feature designed for tuples, because > what you want isn?t a tuple, and it doesn?t support it > with a feature designed to enforce a particular > initialisation behaviour when it?s not the behaviour you > want. Yes, you can rely on the fact that final fields are > tied to constructors and so do some clever initialisation > in the constructor, but my general point is that you?re > reaching for features that were designed for other uses. And I addressed why I thought your logic is flawed, but you have not responded to that. Instead, you are talking about final fields. By all means, I'd like to see how we can address this with something that isn't final fields or records, but I've yet to see that. Thank you for your time and insight! David Alayachew -------------- next part -------------- An HTML attachment was scrubbed... URL: From ron.pressler at oracle.com Tue Jul 4 16:06:31 2023 From: ron.pressler at oracle.com (Ron Pressler) Date: Tue, 4 Jul 2023 16:06:31 +0000 Subject: [External] : Re: Question about circular references In-Reply-To: References: <6A1AD073-ABCC-4D1B-AC35-7978556D2D8E@oracle.com> <9ACE2710-F912-4297-873F-61139413B031@oracle.com> Message-ID: <6D975886-0A0C-4912-B4D6-67E45A947AD4@oracle.com> > On 4 Jul 2023, at 16:25, David Alayachew wrote: > > But regardless, you are declaring this out of the blue. Where are you coming from when you say this? What backs this statement up? I have come to you with widely accepted definitions. If you are going to say that final fields, in this instance, don't permit cycles, I need you to explain how and why, not just claim that they do. For the purpose of this discussion, final is a feature that requires a field to be assigned (only once) in the constructor. If it?s assigned a value arriving to the constructor from the outside, then that value cannot the object currently being constructed. (Yes, you can get around that by passing `this` from a constructor to other constructors). This is in contrast to non-final fields that can be assigned outside the constructor, and so can be assigned in methods that receive the current object itself as an argument, something that the constructor cannot do. > By all means, I am not picky. Can you show me what this would look like though? I can understand how you would achieve immutability doing this, but not the directness constraint I am talking about above. You create your nodes and then you set their references (say, from a builder class), but you don?t expose a public API that allows mutating your nodes. ? Ron From redio.development at gmail.com Tue Jul 4 17:10:48 2023 From: redio.development at gmail.com (Red IO) Date: Tue, 4 Jul 2023 19:10:48 +0200 Subject: Forward declaring the identity of an object In-Reply-To: References: Message-ID: I think the biggest problems would be 1. How do you mark a memory location as allocated (has identity aka memory address) but should still be treated as null which changes later without any notice. It's like a reverse WeakReference 2. How would you represent it syntactically it's neither null nor a valid object it's a in between state previously unknown. 3. There would be many changes in the language necessary to make this feature work reliably and the use beyond niece things like circular references in immutable datastructures are probably low. 4. This would also bring up many questions in which cases the memory behaves like a object and in which case null. Like synchronization, null checks, instanceof, etc. It's likely not worth the complexity and the cost. Great regards RedIODev On Tue, Jul 4, 2023, 15:47 David Alayachew wrote: > > Hello Amber Dev Team, > > I am starting this thread to start a spin off discussion from the > "Question about circular references" thread above. > > In short, RedIODev mentioned something in passing that piqued my interest. > The idea was the ability to reference a declared, but uninitialized > variable (a reference), and allowing that reference to be passed to, let's > say, another constructor, but not permitting derereference until the > variable in question has fully been initialized. > > I wanted to get people's perspectives on this. Is this a good idea? A bad > one? Is this too difficult to do? Not that difficult? Please let me know > your thoughts. > > Thank you for your time and insight! > David Alayachew > -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Tue Jul 4 17:52:21 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Tue, 4 Jul 2023 13:52:21 -0400 Subject: [External] : Re: Question about circular references In-Reply-To: <6D975886-0A0C-4912-B4D6-67E45A947AD4@oracle.com> References: <6A1AD073-ABCC-4D1B-AC35-7978556D2D8E@oracle.com> <9ACE2710-F912-4297-873F-61139413B031@oracle.com> <6D975886-0A0C-4912-B4D6-67E45A947AD4@oracle.com> Message-ID: Hello Ron, Thank you for your response! > > But regardless, you are declaring this out of the blue. > > Where are you coming from when you say this? What backs > > this statement up? I have come to you with widely > > accepted definitions. If you are going to say that final > > fields, in this instance, don't permit cycles, I need you > > to explain how and why, not just claim that they do. > > For the purpose of this discussion, final is a feature > that requires a field to be assigned (only once) in the > constructor. If it?s assigned a value arriving to the > constructor from the outside, then that value cannot the > object currently being constructed. (Yes, you can get > around that by passing `this` from a constructor to other > constructors). This is in contrast to non-final fields > that can be assigned outside the constructor, and so can > be assigned in methods that receive the current object > itself as an argument, something that the constructor > cannot do. Ok, I think I see where the problem is. When you say "final fields do not PERMIT cycles", I am interpreting that to mean "whether or not cycles are representable, they are not permitted, and this is by intention". Whereas I think you are saying "final fields are unable to have cycles because pre-existing mechanics prevent it, even though there is nothing explicitly forbidding it with intention". If that is the case, then we have been talking past each other. My entire point thus far has been acknowledging that, yes, these things are not representable, but then saying that they in fact should be. You have been retorting the entire time saying they are not representable and that's not a problem because of (what I perceive to be) circular logic. You have still not responded to those points I've made by the way. But to actually address this quote, yes, as I have said multiple times, whether it is through records or final fields or something else entirely, we are currently unable to represent cycles. But that is a byproduct of the particular set of mechanics Java provided to create records and finality. At no point along the line was it said that final SHOULD NOT be able to represent circular references. And it is THAT that I am asking for evidence for. And the reason why is because, if there is not an explicit, intentional reason why they should not, then I am of the firm opinion that they should be able to. > > By all means, I am not picky. Can you show me what this > > would look like though? I can understand how you would > > achieve immutability doing this, but not the directness > > constraint I am talking about above. > > You create your nodes and then you set their references > (say, from a builder class), but you don?t expose a > public API that allows mutating your nodes. So, in short, you are saying that I should be creating a builder class, or some privately available mechanism, to be able to insert in the necessary references while still keeping my public API with no mutation options? Here's a compilable example. ```java public class ImmutableAndDirectCircularReferences { public static class GraphBuilder { public static Node graph1() { Node start = new Node(); Node t = new Node(); Node u = new Node(); Node v = new Node(); Node end = new Node(); start.setter(t, u, v); t.setter(u, v, end); u.setter(v, end, end); v.setter(end, end, end); end.setter(end, end, end); //or something like this return start; } } public static class Node { private Node t, u, v; private Node() {} private void setter(Node t, Node u, Node v) { this.t = t; this.u = u; this.v = v; } public Node t() { return this.t; } public Node u() { return this.u; } public Node v() { return this.v; } //equals, hash, tostring } } ``` Fair. I'll concede that it checks all the boxes. More specifically, I'll concede that it is not accurate for me to say that Java can't model circular references while maintaining immutability and direct references. But man, it does it in the worst way possible. We have more boilerplate than multiple other solutions combined, and even then, we have to go deep into indirection to EVENTUALLY construct a direct solution. It's the worst of both worlds while still checking all the boxes. Knowing that this is the actual solution to my problem makes me wish for another solution that much more. But regardless. Point conceded. Thank you for your time and help! David Alayachew -------------- next part -------------- An HTML attachment was scrubbed... URL: From ron.pressler at oracle.com Tue Jul 4 18:09:50 2023 From: ron.pressler at oracle.com (Ron Pressler) Date: Tue, 4 Jul 2023 18:09:50 +0000 Subject: [External] : Re: Question about circular references In-Reply-To: References: <6A1AD073-ABCC-4D1B-AC35-7978556D2D8E@oracle.com> <9ACE2710-F912-4297-873F-61139413B031@oracle.com> <6D975886-0A0C-4912-B4D6-67E45A947AD4@oracle.com> Message-ID: <5894D1E3-70BF-4DB1-9E1D-94EBCA006CB8@oracle.com> > On 4 Jul 2023, at 18:52, David Alayachew wrote: > > > My entire point thus far has been acknowledging that, yes, these things are not representable, but then saying that they in fact should be. You have been retorting the entire time saying they are not representable and that's not a problem because of (what I perceive to be) circular logic. You have still not responded to those points I've made by the way. I?m saying that sometimes you want to allow cycles ? and Java has your back ? and sometimes you want to know things about initialisation, which is why we have final. > > whether it is through records or final fields or something else entirely, we are currently unable to represent cycles. This is clearly not the case. Java classes easily represent cyclic data structures. The features to do that have been part of Java since day one. > But that is a byproduct of the particular set of mechanics Java provided to create records and finality. At no point along the line was it said that final SHOULD NOT be able to represent circular references. And it is THAT that I am asking for evidence for. That?s not how this works. If you want to add a feature to make *final* cyclic references possible it is you who need to show how that would solve a big problem for Java users, who can currently already represent cyclic data structuresl. > We have more boilerplate than multiple other solutions combined, and even then, we have to go deep into indirection to EVENTUALLY construct a direct solution. It's the worst of both worlds while still checking all the boxes. Knowing that this is the actual solution to my problem makes me wish for another solution that much more. But adding another Java feature to address every use case has downsides, too. What we care about isn?t the effort in a *particular* case but the effort integrated over *all* cases. We trade some slightly cumbersome code in few situations for a simpler language overall. However, if your primary concern is the need to write accessor methods, there is a ?concise method declaration? feature on the table, but it?s low priority because there are bigger problems we need to tackle, first. As I?ve said to others before, the best way to motivate a change in the language is to identify a BIG problem, i.e. one that is either very common or one that takes significant effort to get right with existing constructs. We all run into problems that the language could help with, but unless it?s the same problem every time or it?s a very difficult problem, changing the language to address each of those problems creates its own problem. ? Ron From davidalayachew at gmail.com Tue Jul 4 19:06:17 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Tue, 4 Jul 2023 15:06:17 -0400 Subject: [External] : Re: Question about circular references In-Reply-To: <5894D1E3-70BF-4DB1-9E1D-94EBCA006CB8@oracle.com> References: <6A1AD073-ABCC-4D1B-AC35-7978556D2D8E@oracle.com> <9ACE2710-F912-4297-873F-61139413B031@oracle.com> <6D975886-0A0C-4912-B4D6-67E45A947AD4@oracle.com> <5894D1E3-70BF-4DB1-9E1D-94EBCA006CB8@oracle.com> Message-ID: Hello Ron, Thank you for your response! I have to run for holiday festivities, so this will be my last response for several hours. I'll address any future points tomorrow when I get back. > Cyclic is in fact possible Since I conceded that cyclic is in fact possible (regardless of the trouble I have to go through to get it), there's not much I have to say other than that you are correct, cyclic is in fact possible. > BIG problem I spend a lot of my time writing front end code, path finding algorithms, and natural language processing. These are basically the 3 realms I spend 90% of my time in. All 3 of these treat State Transition Diagrams as the holy grail for representing control flow and decision making. Literally ALL of the business logic I write for these initiatives involves me emulating a state transition diagram directly into Java code. For front end code, after I click a button, knowing what the next buttons should be activated or deactivated is an ideal use case for a state transition diagram. If I write a calculator, pressing the start button should activate the number buttons and the operator buttons, but not the negation button or the clear buttons. By using a state transition diagrams, you clearly define all possible path in control flow execution, which is GOLDEN for eliminating bugs in front end code. For path finding algorithms, I don't think that requires any explanation lol. State transition diagrams were literally built for path finding algorithms lol. You need a very good reason to NOT use a state transition diagram. And for natural language processing, you follow almost the exact same logic as the front end code example above. By entering a word, you have filtered the domain of possible follow up words considerably, depending on the type of the word. It is this control flow wrangling that allows something as prohibitively expansive as the English language to be effectively wrangled down to Earth with State Transition Diagrams and Pattern matching. It's no secret that the first thing I ever used Amber for was natural language processing () All of that is to say this. This is a problem that I pay a tax for several times a day, every day. So in my eyes, this is a very large thorn in my side. I treat it similarly to how we treated enums. Sure, they were representable as static final fields, but introducing them as an enum opened up an entire world of possibilities that I could use them for. Enums are my literal favorite feature in all of Java because, like pattern-matching and state transition diagrams, it allows you to be able to wrestle a prohibitively complex domain (serialization and deserialization of a small set of values) into an ergonomic feature that's easy to wield. I'll respond to any further questions when I get back. Thank you for your time and help! David Alayachew -------------- next part -------------- An HTML attachment was scrubbed... URL: From ron.pressler at oracle.com Tue Jul 4 19:45:03 2023 From: ron.pressler at oracle.com (Ron Pressler) Date: Tue, 4 Jul 2023 19:45:03 +0000 Subject: [External] : Re: Question about circular references In-Reply-To: References: <6A1AD073-ABCC-4D1B-AC35-7978556D2D8E@oracle.com> <9ACE2710-F912-4297-873F-61139413B031@oracle.com> <6D975886-0A0C-4912-B4D6-67E45A947AD4@oracle.com> <5894D1E3-70BF-4DB1-9E1D-94EBCA006CB8@oracle.com> Message-ID: <2AF9900D-8B2B-46DF-A085-7280EDCF3ADF@oracle.com> > On 4 Jul 2023, at 20:06, David Alayachew wrote: > > All of that is to say this. This is a problem that I pay a tax for several times a day, every day. So in my eyes, this is a very large thorn in my side. What is the tax, though? Is it the lack of access to pattern matching? Is it having to manually write accessor methods? Is it just boilerplate or is there something that?s difficult to do correctly without language support? From archie.cobbs at gmail.com Tue Jul 4 20:43:12 2023 From: archie.cobbs at gmail.com (Archie Cobbs) Date: Tue, 4 Jul 2023 15:43:12 -0500 Subject: [External] : Re: Question about circular references In-Reply-To: References: <6A1AD073-ABCC-4D1B-AC35-7978556D2D8E@oracle.com> <9ACE2710-F912-4297-873F-61139413B031@oracle.com> <6D975886-0A0C-4912-B4D6-67E45A947AD4@oracle.com> <5894D1E3-70BF-4DB1-9E1D-94EBCA006CB8@oracle.com> Message-ID: On Tue, Jul 4, 2023 at 2:06?PM David Alayachew wrote: > I spend a lot of my time writing front end code, path finding algorithms, > and natural language processing. These are basically the 3 realms I spend > 90% of my time in. All 3 of these treat State Transition Diagrams as the > holy grail for representing control flow and decision making. Literally ALL > of the business logic I write for these initiatives involves me emulating a > state transition diagram directly into Java code. > This sounds like a scenario where a domain-specific language might be appropriate. Have you considered taking that approach? An quick & easy way to experiment with this idea is to use XML+XSLT to generate whatever (e.g., Java source files). Once I used XML+XSLT to generate RPM spec files, which were then compiled into RPMs, as a way to do some DevOps automation. That's a random example but it worked well in terms of the cost/benefit trade-off. Obviously, Java is a general purpose language, so for any particular subset of the programming universe, it's probably going to be sub-optimal. That shouldn't be a surprise, right? -Archie -- Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From holo3146 at gmail.com Wed Jul 5 21:09:10 2023 From: holo3146 at gmail.com (Holo The Sage Wolf) Date: Thu, 6 Jul 2023 00:09:10 +0300 Subject: [External] : Re: Question about circular references In-Reply-To: References: <6A1AD073-ABCC-4D1B-AC35-7978556D2D8E@oracle.com> <9ACE2710-F912-4297-873F-61139413B031@oracle.com> <6D975886-0A0C-4912-B4D6-67E45A947AD4@oracle.com> <5894D1E3-70BF-4DB1-9E1D-94EBCA006CB8@oracle.com> Message-ID: While I can't comment about Frontend, and the paths finding case is clearly correct, but from my experience in NLP, having a "frozen repository" of lookup map is more convenient. Specifically, the ability to copy the repository in an efficient way (as it is just a fixed number of maps) is very useful for hot-commits ("frozen repo" is active, while there is a non-frozen repo in the background, changing all the time, every X amount of time/changes I copy the non-frozen repo into a frozen repo and replace the old active with the new frozen repo) You can also do it using cyclic GADT but I don't see how you both enable fast copying and fast freezing. You can also sidestep these problems adding util method to add nodes/edges (as in, create new graph with the extra nodes/edges), but this becomes very hard to reason without a reference to every node (or without identifiers, the Poor's-man-ref), in which case you basically have a backing repository with "get frozen copy" method that return your GADT On Tue, Jul 4, 2023, 22:06 David Alayachew wrote: > Hello Ron, > > Thank you for your response! > > I have to run for holiday festivities, so this will be my last response > for several hours. I'll address any future points tomorrow when I get back. > > > Cyclic is in fact possible > > Since I conceded that cyclic is in fact possible (regardless of the > trouble I have to go through to get it), there's not much I have to say > other than that you are correct, cyclic is in fact possible. > > > BIG problem > > I spend a lot of my time writing front end code, path finding algorithms, > and natural language processing. These are basically the 3 realms I spend > 90% of my time in. All 3 of these treat State Transition Diagrams as the > holy grail for representing control flow and decision making. Literally ALL > of the business logic I write for these initiatives involves me emulating a > state transition diagram directly into Java code. > > For front end code, after I click a button, knowing what the next buttons > should be activated or deactivated is an ideal use case for a state > transition diagram. If I write a calculator, pressing the start button > should activate the number buttons and the operator buttons, but not the > negation button or the clear buttons. By using a state transition diagrams, > you clearly define all possible path in control flow execution, which is > GOLDEN for eliminating bugs in front end code. > > For path finding algorithms, I don't think that requires any explanation > lol. State transition diagrams were literally built for path finding > algorithms lol. You need a very good reason to NOT use a state transition > diagram. > > And for natural language processing, you follow almost the exact same > logic as the front end code example above. By entering a word, you have > filtered the domain of possible follow up words considerably, depending on > the type of the word. It is this control flow wrangling that allows > something as prohibitively expansive as the English language to be > effectively wrangled down to Earth with State Transition Diagrams and > Pattern matching. It's no secret that the first thing I ever used Amber for > was natural language processing () > > All of that is to say this. This is a problem that I pay a tax for several > times a day, every day. So in my eyes, this is a very large thorn in my > side. > I treat it similarly to how we treated enums. Sure, they were > representable as static final fields, but introducing them as an enum > opened up an entire world of possibilities that I could use them for. Enums > are my literal favorite feature in all of Java because, like > pattern-matching and state transition diagrams, it allows you to be able to > wrestle a prohibitively complex domain (serialization and deserialization > of a small set of values) into an ergonomic feature that's easy to wield. > > I'll respond to any further questions when I get back. > > Thank you for your time and help! > David Alayachew > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Thu Jul 6 17:30:26 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Thu, 6 Jul 2023 13:30:26 -0400 Subject: [External] : Re: Question about circular references In-Reply-To: References: <6A1AD073-ABCC-4D1B-AC35-7978556D2D8E@oracle.com> <9ACE2710-F912-4297-873F-61139413B031@oracle.com> <6D975886-0A0C-4912-B4D6-67E45A947AD4@oracle.com> <5894D1E3-70BF-4DB1-9E1D-94EBCA006CB8@oracle.com> Message-ID: Hello all, Thank you for your patience. I am back and will now continue where I left off on responses. One thing I would like to correct before I start, in my scramble to get away from the computer, I accidentally typed the following. " It's no secret that the first thing I ever used Amber for was natural language processing () " Brain fart. What I had meant to type in was instead this. "It's no surprise that the first thing I ever used Amber for was Natural Language Processing ( https://mail.openjdk.org/pipermail/amber-dev/2022-September/007456.html)" But that's all I wanted to mention. Thank you all again for your time and patience! David Alayachew On Tue, Jul 4, 2023 at 3:06?PM David Alayachew wrote: > Hello Ron, > > Thank you for your response! > > I have to run for holiday festivities, so this will be my last response > for several hours. I'll address any future points tomorrow when I get back. > > > Cyclic is in fact possible > > Since I conceded that cyclic is in fact possible (regardless of the > trouble I have to go through to get it), there's not much I have to say > other than that you are correct, cyclic is in fact possible. > > > BIG problem > > I spend a lot of my time writing front end code, path finding algorithms, > and natural language processing. These are basically the 3 realms I spend > 90% of my time in. All 3 of these treat State Transition Diagrams as the > holy grail for representing control flow and decision making. Literally ALL > of the business logic I write for these initiatives involves me emulating a > state transition diagram directly into Java code. > > For front end code, after I click a button, knowing what the next buttons > should be activated or deactivated is an ideal use case for a state > transition diagram. If I write a calculator, pressing the start button > should activate the number buttons and the operator buttons, but not the > negation button or the clear buttons. By using a state transition diagrams, > you clearly define all possible path in control flow execution, which is > GOLDEN for eliminating bugs in front end code. > > For path finding algorithms, I don't think that requires any explanation > lol. State transition diagrams were literally built for path finding > algorithms lol. You need a very good reason to NOT use a state transition > diagram. > > And for natural language processing, you follow almost the exact same > logic as the front end code example above. By entering a word, you have > filtered the domain of possible follow up words considerably, depending on > the type of the word. It is this control flow wrangling that allows > something as prohibitively expansive as the English language to be > effectively wrangled down to Earth with State Transition Diagrams and > Pattern matching. It's no secret that the first thing I ever used Amber for > was natural language processing () > > All of that is to say this. This is a problem that I pay a tax for several > times a day, every day. So in my eyes, this is a very large thorn in my > side. > I treat it similarly to how we treated enums. Sure, they were > representable as static final fields, but introducing them as an enum > opened up an entire world of possibilities that I could use them for. Enums > are my literal favorite feature in all of Java because, like > pattern-matching and state transition diagrams, it allows you to be able to > wrestle a prohibitively complex domain (serialization and deserialization > of a small set of values) into an ergonomic feature that's easy to wield. > > I'll respond to any further questions when I get back. > > Thank you for your time and help! > David Alayachew > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Thu Jul 6 18:27:36 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Thu, 6 Jul 2023 14:27:36 -0400 Subject: Forward declaring the identity of an object In-Reply-To: References: Message-ID: Hello RedIODev, Thank you for your response! And thank you for your patience, the holidays took up a lot of time. Let me preface this by saying that I won't have many (if any) satisfying answers for you. A lot of these details seem to go deep into the guts about how the JVM work, and while I am happy to learn if you can point me to some resources, the fact is, I just don't have that information in my head at the moment. Regardless, I'll attempt each of your bullets. > I think the biggest problems would be > > 1. How do you mark a memory location as allocated (has > identity aka memory address) but should still be > treated as null which changes later without any > notice? It's like a reverse WeakReference Not sure. To explain the (very nebulous/shapeless) concept I had in my head, here it goes. I believe that the concept of a circular reference is an inherently identity-based operation. Therefore, we must exclude primitives and value classes from the equation. Next, I am of the assumption that there exists a point in time where memory has been allocated, but the data has not been written to memory yet. My hope and prayer was that we could allow actions to occur between those specific 2 actions safely by allowing the compiler to "prove" that the allocation AND memory write will eventually happen before any dereferencing occurs. As a result of that, you have an address that you can pass around to other variables, but they are not permitted to dereference it until it is actually initialized with data -- aka, the memory write. Based on this ethereal concept, I don't know that the data needs to be marked in any way at all. We were taking 2 actions that were essentially contiguous, and breaking them up so that subsequent "safe" actions could occur in between the 2. Please let me know if I am wildly off of the mark here. > 2. How would you represent it syntactically it's neither > null nor a valid object it's a in between state > previously unknown. Truthfully, I don't really care much about what the syntactic representation looks like as long as it is immediately clear and obvious. The semantics, however, are a little different. I would treat whatever syntactic choice we use as a sort of "hall pass" for definite assignment. Basically, we have the variable uninitialized as usual, but then areas where we want to do an illegal forward reference, we put our "hall pass" in front of the variable to signify that it is "safe to use uninitialized". For example. class Node { final Node next; Node(Node next) { this.next = next; //no dereferencing has occurred for the entirety of this constructor, and thus, it is "safe". } } final Node rock, paper, scissors; rock = new Node(__uninit scissors); //the "hall pass", which triggers the scissors memory location to be allocated paper = new Node(rock); scissors = new Node(paper); //the scissors memory allocation is now being written to, completing initialization > 3. There would be many changes in the language necessary > to make this feature work reliably and the use beyond > niece things like circular references in immutable > datastructures are probably low. Frankly, the primary reason why I feel this feature should exist is for clarity's sake. We are allowing an object to represent a relationship more directly. If a references b and b references a, then just bootstrap them into each other and be done with it! Introducing indirection, mutability, reflection, or piles of builder code boilerplate when what is needed is a direct relationship sounds to me like sidestepping a problem by using Java's other strengths and points of flexibility. And while that strength and flexibility is both useful and powerful, it adds overhead that makes problems harder and harder to reason about. I think removing all of that is making coding simpler, and that is a goal worth pursuing imo. > 4. This would also bring up many questions in which cases > the memory behaves like a object and in which case > null. Like synchronization, null checks, instanceof, > etc. > > It's likely not worth the complexity and the cost. Ideally, this should not add any new runtime features, but instead, should be separating pre-existing features into separate actions. My hope is that this can be an "unsafe" operation that can be proven "safe" by the compiler. And yes, there is a limit to what the compiler can prove. For example, if the "constructor" in the example was actually a method on an interface, well we don't know what the implementing method does. Maybe the implementing method dereferences the variable, which then begs the question of what failure should look like. I am of the mindset that we should treat failure the same way that we do in the nonatomic concept from Valhalla and mark that as a cost developers can opt into to get the tradeoffs they want. As for what should literally happen during failure, well based on what I described, you can end up with an object containing garbage data. Of course, the compiler can protect you from a vast majority of these problems, but you opt into getting garbage data back if you use this feature incorrectly. Please let me know your thoughts. Thank you for your time and questions! David Alayachew -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Thu Jul 6 19:34:34 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Thu, 6 Jul 2023 15:34:34 -0400 Subject: [External] : Re: Question about circular references In-Reply-To: <2AF9900D-8B2B-46DF-A085-7280EDCF3ADF@oracle.com> References: <6A1AD073-ABCC-4D1B-AC35-7978556D2D8E@oracle.com> <9ACE2710-F912-4297-873F-61139413B031@oracle.com> <6D975886-0A0C-4912-B4D6-67E45A947AD4@oracle.com> <5894D1E3-70BF-4DB1-9E1D-94EBCA006CB8@oracle.com> <2AF9900D-8B2B-46DF-A085-7280EDCF3ADF@oracle.com> Message-ID: Hello Ron, Thank you for your response! > > All of that is to say this. This is a problem that I > > pay a tax for several times a day, every day. So in my > > eyes, this is a very large thorn in my side. > > What is the tax, though? Is it the lack of access to > pattern matching? Is it having to manually write accessor > methods? Is it just boilerplate or is there something > that?s difficult to do correctly without language support? In short, it's the boilerplate and indirection combined. Pattern-matching is not a problem at all. Pattern-matching actually tends to pair well with STD's. But back to the problem -- it seems small at first glance, but it adds up quickly. Let me show you what I mean. Starting from literal ground zero, let's pick the simplest State Transition Diagram that has a circular reference -- Rock, Paper, Scissors. Since my it's literally just 3 states that have a reference to the other state that they "defeat", then an enum sounds like a perfect fit for this type of problem. And btw, here is a stackoverflow post I made about a very similar situation. I encourage reading it and the accepted answer, since it goes into way more depth about the problem I am describing. https://stackoverflow.com/questions/75072937/why-does-my-lambda-get-illegal-forward-reference-but-my-anonymous-class-does-no enum RPS0 { Rock(Paper), Paper(Scissors), Scissors(Rock), ; public final losesTo; RPS0(RPS0 losesTo) { this.losesTo = losesTo; } } Plain, simple, obvious. No confusion or difficulty whatsoever. But this doesn't compile -- I am referencing a value that does not yet exist. The specific error I get is illegal forward reference. So, I am forced to dig into a couple of other pots. Following your suggestion from earlier Ron, I can do a builder-esque pattern of sorts and go from there. So here we go. enum RPS1 { Rock, Paper, Scissors, ; private RPS1 losesTo; static //a static initialization block is the simplest idea imo. { Rock.losesTo = Paper; Paper.losesTo = Scissors; Scissors.losesTo = Rock; } public RPS1 losesTo() { return this.losesTo; } } Now, one could argue that this is actually even slightly simpler to understand than what we saw before. I am willing to concede that. But what happens if we add a new option to the game, let's say Shotgun? We need to make sure not to forget to add the Shotgun branch to our static initialization block. That's a problem. Let's try and fix that with a switch expression. enum RPS2 { Rock, Paper, Scissors, ; public static RPS2 losesTo(RPS2 choice) { return switch (choice) { case Rock -> Paper; case Paper -> Scissors; case Scissors -> Rock; }; } } Ignore the fact that I didn't include a shotgun, I'm just making a point. By using a switch expression, not only have we gained exhaustiveness, which solves our previous problem, but we have actually made our solution a little simpler! I'd go so far as to say that this strategy is better than the hypothetical I proposed. Cool, switch expressions are (from what I can see) the best solution for representing single transition STD's. But what about multiple transitions? Let's try that with switch expressions. enum RPS3 { Rock, Paper, Scissors, ; public static RPS3 losesTo(RPS3 choice) { return switch (choice) { case Rock -> Paper; case Paper -> Scissors; case Scissors -> Rock; }; } public static RPS3 winsAgainst(RPS3 choice) { return switch (choice) { case Rock -> Scissors; case Paper -> Rock; case Scissors -> Paper; }; } } So, since switch expressions only can handle one transition, we create another method to handle the other transition. But from there, things start to get a little unideal. For starters, now the transitions are separate from each other. This means that if a state has to change significantly, then we need to make sure that we modify all of its transitions. Currently, that is easy enough -- there's only 2 transitions. But you see my point here? If there are 4 different transitions are possible for each state, then this means that maintainability is being damaged by this strategy. The more transitions you add, the closer we get to an ugly mess. Let's address that by combining our 2 solutions above. enum RPS4 { Rock, Paper, Scissors, ; public static final Map lookup; record Transitions(RPS4 losesTo, RPS4 winsAgainst) {} static { Map tempLookup = new HashMap<>(); for (RPS4 each : RPS4.values()) { tempLookup .put ( each, switch (each) { case Rock -> new Transitions(Paper, Scissors); case Paper -> new Transitions(Scissors, Rock); case Scissors -> new Transitions(Rock, Paper); } ); } lookup = Map.copyOf(tempLookup); } } So, now the cracks are starting to form. Already, our business logic is getting drowned out by a lot of boilerplate and excess. But all of that boilerplate is "necessary", since it gives us guarantees that we depend upon in order to write correct code. And to add insult to injury, the business logic looks very similar to what my original, hypothetical solution looks like. But there's room for improvement -- get rid of the map entirely, and just make the switch expression the entirety of the method body. enum RPS4_1 { Rock, Paper, Scissors, ; record Transitions(RPS4_1 losesTo, RPS4_1 winsAgainst) {} public static Transitions transitions(RPS4_1 choice) { return switch (choice) { case Rock -> new Transitions(Paper, Scissors); case Paper -> new Transitions(Scissors, Rock); case Scissors -> new Transitions(Rock, Paper); }; } } Now, I am a believer that premature optimization causes more problems than it solves. That said, I am a bit uncomfortable with the idea of making a new Transitions object each time this method is called. I will go ahead and assume that the JVM can optimize most, if not all of that cost, but the concern remains. But either way, worst case scenario, we could make private static final fields that house the Transitions. Regardless, by using a switch expression, we have essentially created our own little lookup map, but as a method. But notice, this Transitions object is returning a lot more than what we want. Which is not a problem at all at this scale. But what if we want our Rock Paper Scissors example to have more details than just transitions? Imagine that each enum had other attributes like weight, size, etc. That puts us back into the Catch 21 from before. If we stick the extra metadata into Transitions, then we are constructing (maybe, maybe JVM saves us) all of this excess data when we only need 1 or 2 of the fields. And even if the JVM saves us, it doesn't change the fact that we still have to do more hops and unboxing just to get to what I want - the data. There's that indirection creeping back in. If we don't stick the extra metadata into Transitions, then we are back to making 2 or more methods to capture what we need, which has the same problems as above. And might I remind you, State Transition Diagrams were originally built for (and currently, most frequently used with) algorithms, specifically path-finding algorithms. So performance and memory usage mean a lot to STD's most common domains. But, still, there are ways around this too. Java is an incredibly flexible language, as this response has clearly proven. We can get direct relationships and exhaustiveness another way in java -- abstract methods. So let's try that. enum RPS5 { Rock{ public Transitions transitions() { return new Transitions(Paper, Scissors); } public String metadata() { return "whatever"; } }, Paper{ public Transitions transitions() { return new Transitions(Scissors, Rock); } public String metadata() { return "whatever"; } }, Scissors{ public Transitions transitions() { return new Transitions(Rock, Paper); } public String metadata() { return "whatever"; } }, ; record Transitions(RPS5 losesTo, RPS5 winsAgainst) {} public abstract Transitions transitions(); public abstract String metadata(); } For simplicity's sake, I made everything one line. But whether it's one line or multiple, it's pretty clear here that we still have a lot of overhead for what boils down to just simple relationships, right? But there's actually another hidden undesirable. In order to appease the abstract method constraint, I had to use an anonymous class for each one of my enum values. Each anonymous class literally creates a new .class file upon compilation. For proof, here is my directory of .class files for this example. 'ScratchPad$RPS4$1.class' 'ScratchPad$RPS4$2.class' 'ScratchPad$RPS4$3.class' 'ScratchPad$RPS4.class' Now, this may not seem so bad. But what happens if my STD has 50 states? And what if I have multiple STD's like this? This balloons up very quickly, and can create hundreds or thousands of classes for every single state that I create. And please recall, I spend a lot of my time working with State Transition Diagrams. So, still plenty of boilerplate, and now I have a .class file per state. If we're going to make a literal .class file per state, why don't we just go all the way and make it a record and a sealed type? At this point, we'd be adding only a little extra overhead, but getting back some simplicity in return and a lot more directness in return. The big reason why going to records makes sense is because we can escape the Catch 21. Now, I'll go ahead and skip to the end and say that records and sealed expressions work well enough for my Rock Paper Scissors problem, but we end up recreating a lot of enum functionality. When I had enum's, I had the values() method, EnumSet's, EnumMap's, and more. I have to replace that with a static final field on the sealed interface, IdentityHashSet, and IdentityHashMap, respectively. My entire point in showing all of this is to demonstrate all the work we had to do each time our requirements change. Rather than adding another attribute to my enum value's state, I had to rearchitect my solution in yet another way. And this is because the sensical, obvious solution, kept bumping into this circular reference thing, forcing me to uproot my solution and replace it with something different. Whereas if we go back to my hypothetical solution, each time the problem changes, the answer becomes obvious, with little to no rearchitecting. We add a field to our enum, we get to keep our exhaustiveness and directness for free, while only charging us a fair tax in terms of conciseness. A clear, obvious solution. And might I add, once we achieve any sort of scale (5+ values), my solution becomes the most concise and easy to read solution. But the part that I want to highlight here is this -- each of the solutions that we highlighted are "simple enough" to read. So, their legibility and ease of comprehension is really not my problem here. What bothers me is how maintaining and modifying them is annoying and non-obvious anytime a functionality change request comes in. As we travel further and further down the rabbit hole, we find ourselves recreating the things that we used to have for free. Because I didn't have exhaustiveness, I needed a switch expression. Because I didn't have directness, I switched from enums to records and sealed types. And for those who think we are at the end of the line, thinking that this is all annoying, but manageable if that is all there is to keep in mind, please note that I have taken you down exactly one path. We went to that path's logical conclusion, but only one path. For example, what if my states are different types because each state needs different fields or a differing number of transitions? There's a whole laundry list of other paths that I didn't even touch, and spoiler alert -- there's surprisingly little overlap between them. Hopefully this illustrates my pain points better? Thank you for your time and insight! David Alayachew -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Thu Jul 6 19:57:12 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Thu, 6 Jul 2023 15:57:12 -0400 Subject: [External] : Re: Question about circular references In-Reply-To: References: <6A1AD073-ABCC-4D1B-AC35-7978556D2D8E@oracle.com> <9ACE2710-F912-4297-873F-61139413B031@oracle.com> <6D975886-0A0C-4912-B4D6-67E45A947AD4@oracle.com> <5894D1E3-70BF-4DB1-9E1D-94EBCA006CB8@oracle.com> Message-ID: Hello Archie, Thank you for your response! > This sounds like a scenario where a domain-specific > language might be appropriate. Have you considered taking > that approach? So, I have considered it, but the mental overhead in learning a new language/refamiliarizing myself with one always pushed me away. All my problems here are significant enough that it is worth blowing up the email thread, but I fear they might be worse if I tried a new domain language. Willing to give it a shot though. The biggest reason why I am pushing for this feature is because I keep running into a very similar situation -- I attempt a project idea, I try and model it the simplest way possible to limit indirection and mutability, I either fail to do so or fail to do it to a level that I can mentally contain, and then my brain just turns to jelly trying to hold all of the indirection in my head. It's like when you work out at the gym, and then you reach a point to where you push your muscles to literal exhaustion, and they quickly start dying on you. It's the same feeling when I deal with too much indirection for the scale of problems I am trying to solve. > An quick & easy way to experiment with this idea is to > use XML+XSLT to generate whatever (e.g., Java source > files). Once I used XML+XSLT to generate RPM spec files, > which were then compiled into RPMs, as a way to do some > DevOps automation. That's a random example but it worked > well in terms of the cost/benefit trade-off. Lol, sounds like fun. Added to the to-do list, and I'll let you know how it went once I try it out. > Obviously, Java is a general purpose language, so for any > particular subset of the programming universe, it's > probably going to be sub-optimal. That shouldn't be a > surprise, right? I mean, I'd be more willing to accept that if there was literally only one obstacle stopping from doing everything else I wanted to. It is literally this one problem stopping me from making further progress. Thank you for your time and help! David Alayachew On Tue, Jul 4, 2023 at 4:43?PM Archie Cobbs wrote: > On Tue, Jul 4, 2023 at 2:06?PM David Alayachew > wrote: > >> I spend a lot of my time writing front end code, path finding algorithms, >> and natural language processing. These are basically the 3 realms I spend >> 90% of my time in. All 3 of these treat State Transition Diagrams as the >> holy grail for representing control flow and decision making. Literally ALL >> of the business logic I write for these initiatives involves me emulating a >> state transition diagram directly into Java code. >> > > This sounds like a scenario where a domain-specific language might be > appropriate. Have you considered taking that approach? > > An quick & easy way to experiment with this idea is to use XML+XSLT to > generate whatever (e.g., Java source files). Once I used XML+XSLT to > generate RPM spec files, which were then compiled into RPMs, as a way to do > some DevOps automation. That's a random example but it worked well in terms > of the cost/benefit trade-off. > > Obviously, Java is a general purpose language, so for any particular > subset of the programming universe, it's probably going to be sub-optimal. > That shouldn't be a surprise, right? > > -Archie > > -- > Archie L. Cobbs > -------------- next part -------------- An HTML attachment was scrubbed... URL: From ron.pressler at oracle.com Thu Jul 6 20:21:09 2023 From: ron.pressler at oracle.com (Ron Pressler) Date: Thu, 6 Jul 2023 20:21:09 +0000 Subject: [External] : Re: Question about circular references In-Reply-To: References: <6A1AD073-ABCC-4D1B-AC35-7978556D2D8E@oracle.com> <9ACE2710-F912-4297-873F-61139413B031@oracle.com> <6D975886-0A0C-4912-B4D6-67E45A947AD4@oracle.com> <5894D1E3-70BF-4DB1-9E1D-94EBCA006CB8@oracle.com> <2AF9900D-8B2B-46DF-A085-7280EDCF3ADF@oracle.com> Message-ID: <92D035F7-0B73-487D-A0B4-B5BB0DA1EFA0@oracle.com> > On 6 Jul 2023, at 20:34, David Alayachew wrote: > > We need to make sure not to forget to add the Shotgun branch to our static initialization block. That's a problem. Yes, but is it a big one? How often is it encountered and how hard is it to detect and solve? Having the language solve every problem that the language can solve is one approach to language design, but it rarely yields popular languages. Such languages tend to grow quickly. The challenge of language design is exercising judgment in picking which problems are worth addressing. Every feature needs to ?carry its weight?: the magnitude of the problem it solves needs to be bigger than the complexity it adds to the language. Much if not most of the time we spend thinking about features is spent on answering the question: is the problem big enough to justify complicating the language (or even spending time thinking about a solution)? That?s also where most ideas, including those that are raised internally, are dropped; yes, they solve a problem, but not one that?s big enough to justify a language change. Maybe there?s a way to extend definite assignment in a way that covers sufficiently many more interesting cases that aren?t currently covered without adding too much complexity, but my gut instinct is that the cost/benefit here is more questionable than other features we?re working on, and so isn?t a high priority. In other words, given the likely-non-trivial nature of a feature that would cover this use case, the magnitude of the problem, as presented, is not big enough to merit immediate attention (although sometimes we need to address a smaller problem when we discover it stands in the way of addressing a bigger problem). But that?s just my opinion. Maybe when others return from their vacation they would want to offer a different perspective. ? Ron From davidalayachew at gmail.com Thu Jul 6 21:33:46 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Thu, 6 Jul 2023 17:33:46 -0400 Subject: [External] : Re: Question about circular references In-Reply-To: References: <6A1AD073-ABCC-4D1B-AC35-7978556D2D8E@oracle.com> <9ACE2710-F912-4297-873F-61139413B031@oracle.com> <6D975886-0A0C-4912-B4D6-67E45A947AD4@oracle.com> <5894D1E3-70BF-4DB1-9E1D-94EBCA006CB8@oracle.com> Message-ID: Hello, Thank you for your response! > While I can't comment about Frontend, and the paths > finding case is clearly correct While STD's were built with path-finding in mind, I have to say that front end code is where STD's truly shine imo. Frontend code struggles greatly from separating business logic from rendering logic from data transmission logic. Having a STD allows you to group all of your decision making into a set of paths, and STD's compose so you can latch one on to the other like a train cart, put a few inside of a larger one, nest them like Russian nesting dolls, etc. > but from my experience in NLP, having a "frozen > repository" of lookup map is more convenient. > > Specifically, the ability to copy the repository in an > efficient way (as it is just a fixed number of maps) is > very useful for hot-commits ("frozen repo" is active, > while there is a non-frozen repo in the background, > changing all the time, every X amount of time/changes I > copy the non-frozen repo into a frozen repo and replace > the old active with the new frozen repo) Lol, that hot swapping is really clever! I've actually been working on a path-finding problem using dynamic programming. I have a map that is oriented almost exactly like you put it, but I update on the fly, and that always carries overhead that I wasn't really sure how to shake. I'm going to try that myself. Thank you very much! But to address your point (which flows nicely from the previous sentence), I don't think one or the other is necessarily the best option at all times. Ultimately, there are times when a lookup map is definitely the best solution forward. A perfect example is when you have a massive, unknown number of transitions from each state. In situations like that, you COULD try and do the direct approach, but that degrades fast. There's a threshold, decided strictly by the number of branches, where directness becomes a poor fit. I've only hit that boundary once before though, and even then, it only meant uprooting one of my STD's, not all of them. > You can also do it using cyclic GADT but I don't see how > you both enable fast copying and fast freezing. Lol, you don't. Long story short, that hasn't really been a need for me. Most of my NLP is working within the grammar, parsing, discoverability side of things. Take a raw english sentence, determine the language parts from it (verb? noun? adjective?), then use that information to quantify, negate, or compare to a known set of entities that are pulled at startup. Then, I use all of that info to answer the users questions. My algorithm starts fresh on each session, as opposed to accumulating data to be used for the next request. I'm sure once I get further along, I'll run into the problem that you are describing (persisting the results to be loaded later). > You can also sidestep these problems adding util method to add nodes/edges (as in, create new graph with the extra nodes/edges), but this becomes very hard to reason without a reference to every node (or without identifiers, the Poor's-man-ref), in which case you basically have a backing repository with "get frozen copy" method that return your GADT I see what you mean. When I get the chance, I am going to try this out and see how it turns out. Not sure how I would approach it. Thank you for your time and help! David Alayachew On Wed, Jul 5, 2023 at 5:09?PM Holo The Sage Wolf wrote: > While I can't comment about Frontend, and the paths finding case is > clearly correct, but from my experience in NLP, having a "frozen > repository" of lookup map is more convenient. > > Specifically, the ability to copy the repository in an efficient way (as > it is just a fixed number of maps) is very useful for hot-commits ("frozen > repo" is active, while there is a non-frozen repo in the background, > changing all the time, every X amount of time/changes I copy the non-frozen > repo into a frozen repo and replace the old active with the new frozen repo) > > You can also do it using cyclic GADT but I don't see how you both enable > fast copying and fast freezing. You can also sidestep these problems adding > util method to add nodes/edges (as in, create new graph with the extra > nodes/edges), but this becomes very hard to reason without a reference to > every node (or without identifiers, the Poor's-man-ref), in which case you > basically have a backing repository with "get frozen copy" method that > return your GADT > > On Tue, Jul 4, 2023, 22:06 David Alayachew > wrote: > >> Hello Ron, >> >> Thank you for your response! >> >> I have to run for holiday festivities, so this will be my last response >> for several hours. I'll address any future points tomorrow when I get back. >> >> > Cyclic is in fact possible >> >> Since I conceded that cyclic is in fact possible (regardless of the >> trouble I have to go through to get it), there's not much I have to say >> other than that you are correct, cyclic is in fact possible. >> >> > BIG problem >> >> I spend a lot of my time writing front end code, path finding algorithms, >> and natural language processing. These are basically the 3 realms I spend >> 90% of my time in. All 3 of these treat State Transition Diagrams as the >> holy grail for representing control flow and decision making. Literally ALL >> of the business logic I write for these initiatives involves me emulating a >> state transition diagram directly into Java code. >> >> For front end code, after I click a button, knowing what the next buttons >> should be activated or deactivated is an ideal use case for a state >> transition diagram. If I write a calculator, pressing the start button >> should activate the number buttons and the operator buttons, but not the >> negation button or the clear buttons. By using a state transition diagrams, >> you clearly define all possible path in control flow execution, which is >> GOLDEN for eliminating bugs in front end code. >> >> For path finding algorithms, I don't think that requires any explanation >> lol. State transition diagrams were literally built for path finding >> algorithms lol. You need a very good reason to NOT use a state transition >> diagram. >> >> And for natural language processing, you follow almost the exact same >> logic as the front end code example above. By entering a word, you have >> filtered the domain of possible follow up words considerably, depending on >> the type of the word. It is this control flow wrangling that allows >> something as prohibitively expansive as the English language to be >> effectively wrangled down to Earth with State Transition Diagrams and >> Pattern matching. It's no secret that the first thing I ever used Amber for >> was natural language processing () >> >> All of that is to say this. This is a problem that I pay a tax for >> several times a day, every day. So in my eyes, this is a very large thorn >> in my side. >> I treat it similarly to how we treated enums. Sure, they were >> representable as static final fields, but introducing them as an enum >> opened up an entire world of possibilities that I could use them for. Enums >> are my literal favorite feature in all of Java because, like >> pattern-matching and state transition diagrams, it allows you to be able to >> wrestle a prohibitively complex domain (serialization and deserialization >> of a small set of values) into an ergonomic feature that's easy to wield. >> >> I'll respond to any further questions when I get back. >> >> Thank you for your time and help! >> David Alayachew >> >> -------------- next part -------------- An HTML attachment was scrubbed... URL: From steve at ethx.net Thu Jul 6 21:42:17 2023 From: steve at ethx.net (Steve Barham) Date: Thu, 6 Jul 2023 22:42:17 +0100 Subject: [External] : Re: Question about circular references In-Reply-To: References: <6A1AD073-ABCC-4D1B-AC35-7978556D2D8E@oracle.com> <9ACE2710-F912-4297-873F-61139413B031@oracle.com> <6D975886-0A0C-4912-B4D6-67E45A947AD4@oracle.com> <5894D1E3-70BF-4DB1-9E1D-94EBCA006CB8@oracle.com> Message-ID: Politely, I have a similar feeling of exhaustion when I see yet another email on this subject - which has very little to do with Amber. Stable languages evolve slowly. Were you to convince people of the merits of your arguments (which so far has not been the case) you would be waiting through many release cycles for that feature to become available. I suspect your time and energy would be better spent confronting the reality in which you find yourself (which may not be perfect in your estimation), than your current approach. Thank you, Steve > On 6 Jul 2023, at 20:57, David Alayachew wrote: > > > Hello Archie, > > Thank you for your response! > > > This sounds like a scenario where a domain-specific > > language might be appropriate. Have you considered taking > > that approach? > > So, I have considered it, but the mental overhead in learning a new language/refamiliarizing myself with one always pushed me away. All my problems here are significant enough that it is worth blowing up the email thread, but I fear they might be worse if I tried a new domain language. Willing to give it a shot though. > > The biggest reason why I am pushing for this feature is because I keep running into a very similar situation -- I attempt a project idea, I try and model it the simplest way possible to limit indirection and mutability, I either fail to do so or fail to do it to a level that I can mentally contain, and then my brain just turns to jelly trying to hold all of the indirection in my head. It's like when you work out at the gym, and then you reach a point to where you push your muscles to literal exhaustion, and they quickly start dying on you. It's the same feeling when I deal with too much indirection for the scale of problems I am trying to solve. > > > An quick & easy way to experiment with this idea is to > > use XML+XSLT to generate whatever (e.g., Java source > > files). Once I used XML+XSLT to generate RPM spec files, > > which were then compiled into RPMs, as a way to do some > > DevOps automation. That's a random example but it worked > > well in terms of the cost/benefit trade-off. > > Lol, sounds like fun. Added to the to-do list, and I'll let you know how it went once I try it out. > > > Obviously, Java is a general purpose language, so for any > > particular subset of the programming universe, it's > > probably going to be sub-optimal. That shouldn't be a > > surprise, right? > > I mean, I'd be more willing to accept that if there was literally only one obstacle stopping from doing everything else I wanted to. It is literally this one problem stopping me from making further progress. > > Thank you for your time and help! > David Alayachew > > On Tue, Jul 4, 2023 at 4:43?PM Archie Cobbs > wrote: > On Tue, Jul 4, 2023 at 2:06?PM David Alayachew > wrote: > I spend a lot of my time writing front end code, path finding algorithms, and natural language processing. These are basically the 3 realms I spend 90% of my time in. All 3 of these treat State Transition Diagrams as the holy grail for representing control flow and decision making. Literally ALL of the business logic I write for these initiatives involves me emulating a state transition diagram directly into Java code. > > This sounds like a scenario where a domain-specific language might be appropriate. Have you considered taking that approach? > > An quick & easy way to experiment with this idea is to use XML+XSLT to generate whatever (e.g., Java source files). Once I used XML+XSLT to generate RPM spec files, which were then compiled into RPMs, as a way to do some DevOps automation. That's a random example but it worked well in terms of the cost/benefit trade-off. > > Obviously, Java is a general purpose language, so for any particular subset of the programming universe, it's probably going to be sub-optimal. That shouldn't be a surprise, right? > > -Archie > > -- > Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Thu Jul 6 22:02:06 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Thu, 6 Jul 2023 18:02:06 -0400 Subject: [External] : Re: Question about circular references In-Reply-To: <92D035F7-0B73-487D-A0B4-B5BB0DA1EFA0@oracle.com> References: <6A1AD073-ABCC-4D1B-AC35-7978556D2D8E@oracle.com> <9ACE2710-F912-4297-873F-61139413B031@oracle.com> <6D975886-0A0C-4912-B4D6-67E45A947AD4@oracle.com> <5894D1E3-70BF-4DB1-9E1D-94EBCA006CB8@oracle.com> <2AF9900D-8B2B-46DF-A085-7280EDCF3ADF@oracle.com> <92D035F7-0B73-487D-A0B4-B5BB0DA1EFA0@oracle.com> Message-ID: Hello Ron, Thank you for your response! > > We need to make sure not to forget to add the Shotgun > > branch to our static initialization block. That's a > > problem. > > Yes, but is it a big one? How often is it encountered and > how hard is it to detect and solve? Lol, let me link you again to my post about Data-Oriented Programming. https://mail.openjdk.org/pipermail/amber-dev/2022-September/007456.html My biggest pain point from there by far was not being able to keep track of all of my states attributes as they morphed due to changing requirements. I went into great detail talking about exactly how big of a pain point it was in my side. Several people were able to sympathize with me as well. > Having the language solve every problem that the language > can solve is one approach to language design, but it > rarely yields popular languages. Such languages tend to > grow quickly. > > The challenge of language design is exercising judgment > in picking which problems are worth addressing. Every > feature needs to ?carry its weight?: the magnitude of the > problem it solves needs to be bigger than the complexity > it adds to the language. Much if not most of the time we > spend thinking about features is spent on answering the > question: is the problem big enough to justify > complicating the language (or even spending time thinking > about a solution)? That?s also where most ideas, > including those that are raised internally, are dropped; > yes, they solve a problem, but not one that?s big enough > to justify a language change. Agreed. That's why I started this entire thing off by talking about what was bothering me and seeing if anyone else could relate. My claim was based on a faulty premise, but once the foundation of that premise was corrected and reoriented, I think I have pointed out a significant pain point in the language. > Maybe there?s a way to extend definite assignment in a > way that covers sufficiently many more interesting cases > that aren?t currently covered without adding too much > complexity, but my gut instinct is that the cost/benefit > here is more questionable than other features we?re > working on, and so isn?t a high priority. In other words, > given the likely-non-trivial nature of a feature that > would cover this use case, the magnitude of the problem, > as presented, is not big enough to merit immediate > attention I'm definitely willing to accept that this feature just isn't a top priority compared to deconstruction patterns or something similar. But I feel like once an accurate picture of the level of effort has been painted, that'll be when this question can be answered. And I should emphasize -- any successful attempt to improve definite assignment will have a massive reach because definite assignment is EVERYWHERE in Java. It bleeds into practically every line of code we ever write in Java. Even a small improvement, that only moves the ball forward a few inches, will have a huge benefit. It becomes a question of the costs. > (although sometimes we need to address a > smaller problem when we discover it stands in the way of > addressing a bigger problem). I see what you mean. I am curious if the world of lazy loading would benefit from this. > But that?s just my opinion. Maybe when others return from > their vacation they would want to offer a different > perspective. Yes, would love to hear more perspectives on this topic. You and I have had plenty to say, but my primary goal was to see if anyone felt similarly. Thank you for your time and help! David Alayachew -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Thu Jul 6 22:21:12 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Thu, 6 Jul 2023 18:21:12 -0400 Subject: [External] : Re: Question about circular references In-Reply-To: References: <6A1AD073-ABCC-4D1B-AC35-7978556D2D8E@oracle.com> <9ACE2710-F912-4297-873F-61139413B031@oracle.com> <6D975886-0A0C-4912-B4D6-67E45A947AD4@oracle.com> <5894D1E3-70BF-4DB1-9E1D-94EBCA006CB8@oracle.com> Message-ID: Hello Steve, Thank you for your response! > Politely, I have a similar feeling of exhaustion when I > see yet another email on this subject - which has very > little to do with Amber. Fair enough. There have been a lot of emails, and I am verbose by nature. Add to that the big roundabout over whether or not Java had circular references that ended up being false. > Stable languages evolve slowly. Were you to convince > people of the merits of your arguments (which so far has > not been the case) you would be waiting through many > release cycles for that feature to become available. > > I suspect your time and energy would be better spent > confronting the reality in which you find yourself (which > may not be perfect in your estimation), than your current > approach. Lol, I am a patient person, so a few years is just fine with me. And to be clear, I learned the ins and outs of this problem. I know which solutions to go for and which to avoid. My point was that this was a tax I don't think I should have to pay, and I wanted to see everyone else's thoughts. Thank you for your time and patience! David Alayachew -------------- next part -------------- An HTML attachment was scrubbed... URL: From nlisker at gmail.com Fri Jul 7 00:17:15 2023 From: nlisker at gmail.com (Nir Lisker) Date: Fri, 7 Jul 2023 03:17:15 +0300 Subject: Assignment of final fields outside of constructors Message-ID: Hi, I believe that the following is a known request. Consider the class class Boat { final int sails; Boat(Type type) { switch (type) { case ENGINE -> sails = 0; case SAIL -> sails = 2; default -> sails = 1; } } } This is fine (and yes, can be written better). However, if I try to extract the constructor body into a private method, I get a compilation error that the final field has not been assigned. Understandable at this point. In some classes that are more complex than my contrived example, the constructor's body is divided into steps ('createSails', 'createAnchor'...) that assign final fields. The current situation forces all of the code to appear in one big chunk in the constructor, instead of being able to divide the work into methods, as we usually like to do with methods. There is a known clear readability and maintenance benefit here. I'm aware that there is a school of thought saying that the constructor should only do simple things, and all the preparation should be done before instantiating the class. Basically, calling createSails() etc. first and then sending the results to the constructor to just do checks and assignments. I don't always subscribe to this approach, especially since it exposes implementation details (what if the return type of createSails() should be internal to Boat only?). I remember some discussion for "nested methods": allowing methods (and constructors) to contain other methods; I assume that that would be one way to solve it. One can also use simple scopes (`{ ... }`) to divide the method/constructor. However, I would like you to consider the following solution. If the method satisfies these conditions: 1. It is private (and thus final) 2. It is called only from the/a constructor 3. It is called only once then it may assign final fields. The last condition might not be strictly necessary because there is already a check if the final field has been assigned already. These conditions can be applied recursively, checking if the call originates in the constructor and allowing to further divide the constructors into sub-steps, but I'm willing to wait with this addition. I think that this can be checked rather easily by static analysis. I CC'd Archie who has done similar work in JEP 447 (static analysis to remove unnecessary restrictions in the constructor) to weigh in. I say "rather easily" because these conditions don't require looking at the class hierarchy - they are limited to the inspected class only. Thoughts? - Nir -------------- next part -------------- An HTML attachment was scrubbed... URL: From archie.cobbs at gmail.com Fri Jul 7 01:59:06 2023 From: archie.cobbs at gmail.com (Archie Cobbs) Date: Thu, 6 Jul 2023 20:59:06 -0500 Subject: Assignment of final fields outside of constructors In-Reply-To: References: Message-ID: On Thu, Jul 6, 2023 at 7:17?PM Nir Lisker wrote: > However, I would like you to consider the following solution. > If the method satisfies these conditions: > 1. It is private (and thus final) > 2. It is called only from the/a constructor > 3. It is called only once > then it may assign final fields. The last condition might not be strictly > necessary because there is already a check if the final field has been > assigned already. These conditions can be applied recursively, checking if > the call originates in the constructor and allowing to further divide > the constructors into sub-steps, but I'm willing to wait with this addition. > Re #2 you would need to say "called only from a constructor that explicitly or implicitly invokes super()" Playing devil's advocate for a moment - it's not really the field assignment that is the bulky problem, it's the calculation that computes the field's value. In other words, wouldn't doing the following be a pretty close approximation to what you're asking for? class Boat { final int sails; Boat(Type type) { this.sails = computeSails(type); } private static int computeSails(Type type) { return switch (type) { case ENGINE -> 0; case SAIL -> 2; default -> 1; }; } } The only difference is where the "this.sails = " part lives. Of course this approach doesn't work quite as well when you want to compute multiple final field values in one private method - then you'd need to return them in some carrier object, etc. The static analysis would indeed be relatively easy; the "this escape" analyzer does this kind of thing (see ThisEscapeAnalyzer.java). Of course there's also the JLS specification work, which can rival the coding work in its complexity... -Archie -- Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From redio.development at gmail.com Fri Jul 7 10:45:48 2023 From: redio.development at gmail.com (Red IO) Date: Fri, 7 Jul 2023 12:45:48 +0200 Subject: Forward declaring the identity of an object In-Reply-To: References: Message-ID: I see you though of it as a compile time thing. But like you also mentioned it would be difficult for the compiler to prove that no access occurs before initialization severely limiting the usefulness. Also the things with for example instanceof and synchronization are real questions as they primary operate on identity and don't use the (garbage) data of a half initialized object. Like: Foo foo = __uninitialized; boolean b = foo instanceof Foo //true or false syncronized(foo) { //works or not? } foo = new Foo(); Also in terms of value objects and primitives it doesn't really work at all since they by definition do not have identity (in Java) so since they cannot be referenced at all they can't ever be in this uninitialized state. Overall cyclical REFERENCE structures don't make sense or work in datatypes without identity. The only big obstacle I see is that it's probably not worth the cost of implementating. The private field or id approach both work and solve the problem hence this is merely a convenience feature, even though the concept might allow unforeseen possibilities not obvious right now. If other problems are found that would be solved with it it might become viable to implement. Great regards RedIODev On Thu, Jul 6, 2023, 20:27 David Alayachew wrote: > > Hello RedIODev, > > Thank you for your response! And thank you for your patience, the holidays > took up a lot of time. > > Let me preface this by saying that I won't have many (if any) satisfying > answers for you. A lot of these details seem to go deep into the guts about > how the JVM work, and while I am happy to learn if you can point me to some > resources, the fact is, I just don't have that information in my head at > the moment. > > Regardless, I'll attempt each of your bullets. > > > I think the biggest problems would be > > > > 1. How do you mark a memory location as allocated (has > > identity aka memory address) but should still be > > treated as null which changes later without any > > notice? It's like a reverse WeakReference > > Not sure. > > To explain the (very nebulous/shapeless) concept I had in my head, here it > goes. > > I believe that the concept of a circular reference is an inherently > identity-based operation. Therefore, we must exclude primitives and value > classes from the equation. > > Next, I am of the assumption that there exists a point in time where > memory has been allocated, but the data has not been written to memory yet. > My hope and prayer was that we could allow actions to occur between those > specific 2 actions safely by allowing the compiler to "prove" that the > allocation AND memory write will eventually happen before any dereferencing > occurs. > > As a result of that, you have an address that you can pass around to other > variables, but they are not permitted to dereference it until it is > actually initialized with data -- aka, the memory write. > > Based on this ethereal concept, I don't know that the data needs to be > marked in any way at all. We were taking 2 actions that were essentially > contiguous, and breaking them up so that subsequent "safe" actions could > occur in between the 2. > > Please let me know if I am wildly off of the mark here. > > > 2. How would you represent it syntactically it's neither > > null nor a valid object it's a in between state > > previously unknown. > > Truthfully, I don't really care much about what the syntactic > representation looks like as long as it is immediately clear and obvious. > > The semantics, however, are a little different. > > I would treat whatever syntactic choice we use as a sort of "hall pass" > for definite assignment. Basically, we have the variable uninitialized as > usual, but then areas where we want to do an illegal forward reference, we > put our "hall pass" in front of the variable to signify that it is "safe to > use uninitialized". > > For example. > > class Node > { > > final Node next; > > Node(Node next) > { > this.next = next; //no dereferencing has occurred for the entirety > of this constructor, and thus, it is "safe". > } > > } > > final Node rock, paper, scissors; > > rock = new Node(__uninit scissors); //the "hall pass", which triggers the > scissors memory location to be allocated > paper = new Node(rock); > scissors = new Node(paper); //the scissors memory allocation is now being > written to, completing initialization > > > 3. There would be many changes in the language necessary > > to make this feature work reliably and the use beyond > > niece things like circular references in immutable > > datastructures are probably low. > > Frankly, the primary reason why I feel this feature should exist is for > clarity's sake. We are allowing an object to represent a relationship more > directly. If a references b and b references a, then just bootstrap them > into each other and be done with it! Introducing indirection, mutability, > reflection, or piles of builder code boilerplate when what is needed is a > direct relationship sounds to me like sidestepping a problem by using > Java's other strengths and points of flexibility. And while that strength > and flexibility is both useful and powerful, it adds overhead that makes > problems harder and harder to reason about. I think removing all of that is > making coding simpler, and that is a goal worth pursuing imo. > > > 4. This would also bring up many questions in which cases > > the memory behaves like a object and in which case > > null. Like synchronization, null checks, instanceof, > > etc. > > > > It's likely not worth the complexity and the cost. > > > Ideally, this should not add any new runtime features, but instead, should > be separating pre-existing features into separate actions. My hope is that > this can be an "unsafe" operation that can be proven "safe" by the compiler. > > And yes, there is a limit to what the compiler can prove. For example, if > the "constructor" in the example was actually a method on an interface, > well we don't know what the implementing method does. Maybe the > implementing method dereferences the variable, which then begs the question > of what failure should look like. > > I am of the mindset that we should treat failure the same way that we do > in the nonatomic concept from Valhalla and mark that as a cost developers > can opt into to get the tradeoffs they want. > > As for what should literally happen during failure, well based on what I > described, you can end up with an object containing garbage data. Of > course, the compiler can protect you from a vast majority of these > problems, but you opt into getting garbage data back if you use this > feature incorrectly. > > Please let me know your thoughts. > > Thank you for your time and questions! > David Alayachew > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Fri Jul 7 12:27:26 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Fri, 7 Jul 2023 08:27:26 -0400 Subject: Forward declaring the identity of an object In-Reply-To: References: Message-ID: Hello RedIODev, Thank you for your response! > I see you though of it as a compile time thing. But like > you also mentioned it would be difficult for the compiler > to prove that no access occurs before initialization > severely limiting the usefulness. More or less, though I wouldn't use the word "usefulness", but instead, "safety". Ultimately, the usefulness remains, it just becomes way more periless because the compiler can only protect you from so much. > Also the things with for example instanceof and > synchronization are real questions as they primary > operate on identity and don't use the (garbage) data of a > half initialized object. > > Like: > > Foo foo = __uninitialized; > boolean b = foo instanceof Foo //true or false > syncronized(foo) { //works or not? > > } > foo = new Foo(); Good point. Let me correct myself then. What if we reworked things like this instead. Rather than saying __uninitialized, what if we said something like temp-null, which I think is a garbage syntax, but it communicates the point more clearly. Basically, you literally set the value to null, which answers all the questions about what will happen if the uninitialized value gets passed in. The only difference is that, if we use temp-null instead of just null, then in the case of final fields, the compiler is permitted to set that variable one more time, this time to the actual value, whether that's actually null, or the constructor's output, etc. Then, once we finally initialize the value, the runtime will go back to all of the locations that we set to null via temp-null, and overwrite that with the actual memory location. So in your hypothetical example, if you switched out Foo foo for final Foo foo, then everything should make sense because it becomes obvious how the program works. That would simplify things significantly, because we're leaning on existing semantics that every Java developer already understands. > Also in terms of value objects and primitives it doesn't > really work at all since they by definition do not have > identity (in Java) so since they cannot be referenced at > all they can't ever be in this uninitialized state. > > Overall cyclical REFERENCE structures don't make sense or > work in datatypes without identity. 100% agreed. This is actually kind of an interesting point because it helps quantify the benefits that identity actually gives you. Sometimes, with all the Valhalla stuff coming out, it almost feels like Identity Objects are somehow inferior to these new value objects. Yes, there's obviously tradeoffs, but the benefits of identity feel like minimal benefits that you only opt into when absolutely necessary (mutability, which is falling out of favor/inheritance, which has long since fallen out of favor/synchronization, which isn't something you throw into every day code, etc.). Being able to represent complex relationships more directly and easily makes me feel like identity has actual strengths that are worth considering for more than 5 seconds when thinking about how to model my everyday code. And it would be very easy to switch between a value class to an identity class if the need arises, so already, this addresses many of the maintainability issues I brought up in the other thread above. I feel like if we were to allow identity classes to have this feature, then identity classes would have an actual argument for being used in everyday code aside from when Java forces you to use them. > The only big obstacle I see is that it's probably not > worth the cost of implementating. The private field or id > approach both work and solve the problem hence this is > merely a convenience feature, even though the concept > might allow unforeseen possibilities not obvious right > now. If other problems are found that would be solved > with it it might become viable to implement. I hear you. Part of the reason why I continue to push this feature is because, not only do I think that this is a powerful feature that uproots a lot of complexity involved in how programmer's think about relationships, but I am starting to suspect that this is a surprisingly simple feature to implement. I am gambling that this will be a low cost, high gain feature, which will justify it being worked on, even if not now (String templates and whatnot are higher priority). Thank you for your time and help! David Alayachew -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Fri Jul 7 13:11:32 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Fri, 7 Jul 2023 09:11:32 -0400 Subject: Forward declaring the identity of an object In-Reply-To: References: Message-ID: I actually just remembered this. I decided earlier on that any keyword that is used to denote this feature would be used where the variable is being passed in, like a "hall pass". Node a; Node b = new Node(__hall_pass a); a = new Node(b); I wanted to highlight this because your example put the keyword at the declaration site rather than the use site. Doing it my way is clearer because, for example in the enum example I gave earlier, you change things to say this. enum RPS7 { Rock(__hall_pass Scissors), Paper(Rock), Scissors(Paper), ; //etc } Plus, in my (very ignorant and uninformed) view of how the runtime works, putting the keyword at the use site would more closely map to what actions the runtime takes, as well as where it would do it. -------------- next part -------------- An HTML attachment was scrubbed... URL: From pedro.lamarao at prodist.com.br Fri Jul 7 13:27:48 2023 From: pedro.lamarao at prodist.com.br (=?UTF-8?Q?Pedro_Lamar=C3=A3o?=) Date: Fri, 7 Jul 2023 10:27:48 -0300 Subject: Assignment of final fields outside of constructors In-Reply-To: References: Message-ID: Em qui., 6 de jul. de 2023 ?s 21:18, Nir Lisker escreveu: > I remember some discussion for "nested methods": allowing methods (and > constructors) to contain other methods; I assume that that would be one way > to solve it. > Now that we have lambdas, many "nested method" solutions are already possible: class Boat { final int sails; Boat(Type type) { Supplier supplier = () -> { return switch (type) { case ENGINE -> 0; case SAIL -> 2; default -> 1; } }; this.sails = supplier.get(); } } -- Pedro Lamar?o -------------- next part -------------- An HTML attachment was scrubbed... URL: From holo3146 at gmail.com Fri Jul 7 14:37:31 2023 From: holo3146 at gmail.com (Holo The Sage Wolf) Date: Fri, 7 Jul 2023 17:37:31 +0300 Subject: Assignment of final fields outside of constructors In-Reply-To: References: Message-ID: The static analysis is not too complicated, but there are other problems with the idea. Allowing this feature will mean that change inside any place of the class may lead to an error at the constructor, is this feature important enough to allow that phenomenon? I don't think it is, especially when there is already a way to refactor logic into a method by returning the value: ```java Boat(Type t) { this.snail = getStail(t); } ``` For more values you can either use a private record for the carrier object, or even a local class as a carrier. About your complaints of "keep constructor stupid", a way to not get into visibility problems is to make a factory class that is a subclass of Boat, but this can make the file Boat.java a bit bloated. Alternatively, make a mirror types inside the factory class, which allows you to translate the result of the calculations (the mirror types) into the actual types inside the constructor using a simple lookup. On Fri, Jul 7, 2023 at 3:18?AM Nir Lisker wrote: > Hi, > > I believe that the following is a known request. Consider the class > > class Boat { > > final int sails; > > Boat(Type type) { > switch (type) { > case ENGINE -> sails = 0; > case SAIL -> sails = 2; > default -> sails = 1; > } > } > } > > This is fine (and yes, can be written better). However, if I try to > extract the constructor body into a private method, I get a compilation > error that the final field has not been assigned. Understandable at this > point. > > In some classes that are more complex than my contrived example, the > constructor's body is divided into steps ('createSails', 'createAnchor'...) > that assign final fields. The current situation forces all of the code to > appear in one big chunk in the constructor, instead of being able to divide > the work into methods, as we usually like to do with methods. There is a > known clear readability and maintenance benefit here. > > I'm aware that there is a school of thought saying that the constructor > should only do simple things, and all the preparation should be done before > instantiating the class. Basically, calling createSails() etc. first and > then sending the results to the constructor to just do checks and > assignments. I don't always subscribe to this approach, especially since it > exposes implementation details (what if the return type of createSails() > should be internal to Boat only?). > > I remember some discussion for "nested methods": allowing methods (and > constructors) to contain other methods; I assume that that would be one way > to solve it. One can also use simple scopes (`{ ... }`) to divide > the method/constructor. However, I would like you to consider the following > solution. > If the method satisfies these conditions: > 1. It is private (and thus final) > 2. It is called only from the/a constructor > 3. It is called only once > then it may assign final fields. The last condition might not be strictly > necessary because there is already a check if the final field has been > assigned already. These conditions can be applied recursively, checking if > the call originates in the constructor and allowing to further divide > the constructors into sub-steps, but I'm willing to wait with this addition. > > I think that this can be checked rather easily by static analysis. I CC'd > Archie who has done similar work in JEP 447 (static analysis to remove > unnecessary restrictions in the constructor) to weigh in. I say "rather > easily" because these conditions don't require looking at the class > hierarchy - they are limited to the inspected class only. > > Thoughts? > > - Nir > -- Holo The Wise Wolf Of Yoitsu -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Fri Jul 7 19:43:26 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Fri, 7 Jul 2023 15:43:26 -0400 Subject: Exhaustiveness for record pattern with switch (JEP 440+441) In-Reply-To: References: Message-ID: I've CC'd the Amber Dev Team. On Fri, Jul 7, 2023 at 1:52?PM Mikhail Pyltsin wrote: > Hi! > I investigated the algorithm, which Javac 21 (Build 30 (2023/7/6)) > ) uses to check exhaustiveness for record patterns (JEP 440+441), and > found the strange behavior: > - let's take compilable code > ``` > class Test22 { > > record Pair(I i1, I i2) {} > > sealed interface I {} > > record C() implements I {} > > record D() implements I {} > > void exhaustinvenessWithInterface(Pair pairI) { > switch (pairI) { > case Pair(C fst, D snd) -> { > } > case Pair(C fst, C snd) -> { > } > case Pair(I fst, C snd) -> { > } > case Pair(D fst, D snd) -> { > } > } > } > } > ``` > - If we swap types of components, it starts to produce the error "java: > the switch statement does not cover all possible input values": > ``` > void exhaustinvenessWithInterface(Pair pairI) { > switch (pairI) { > case Pair(D fst, C snd) -> { > } > case Pair(C fst, C snd) -> { > } > case Pair(C fst, I snd) -> { > } > case Pair(D fst, D snd) -> { > } > } > } > ``` > It happens because Javac tries to find the first distinguished type from > the beginning, but I couldn't find any mention of it in JEP. > Is this the expected behavior? > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Fri Jul 7 20:34:32 2023 From: forax at univ-mlv.fr (Remi Forax) Date: Fri, 7 Jul 2023 22:34:32 +0200 (CEST) Subject: Exhaustiveness for record pattern with switch (JEP 440+441) In-Reply-To: References: Message-ID: <314406701.99141832.1688762072167.JavaMail.zimbra@univ-eiffel.fr> Looks like a bug too me, the possible combinations are (C, C), (C, D) (D, C) (D,D), they are all covered. R?mi > From: "David Alayachew" > To: "Mikhail Pyltsin" > Cc: "compiler-dev" , "amber-dev" > > Sent: Friday, July 7, 2023 10:43:26 PM > Subject: Re: Exhaustiveness for record pattern with switch (JEP 440+441) > I've CC'd the Amber Dev Team. > On Fri, Jul 7, 2023 at 1:52 PM Mikhail Pyltsin < [ mailto:pyltsinm at gmail.com | > pyltsinm at gmail.com ] > wrote: >> Hi! >> I investigated the algorithm, which Javac 21 (Build 30 (2023/7/6)) >> ) uses to check exhaustiveness for record patterns (JEP 440+441), and found the >> strange behavior: >> - let's take compilable code >> ``` >> class Test22 { >> record Pair(I i1, I i2) {} >> sealed interface I {} >> record C() implements I {} >> record D() implements I {} >> void exhaustinvenessWithInterface(Pair pairI) { >> switch (pairI) { >> case Pair(C fst, D snd) -> { >> } >> case Pair(C fst, C snd) -> { >> } >> case Pair(I fst, C snd) -> { >> } >> case Pair(D fst, D snd) -> { >> } >> } >> } >> } >> ``` >> - If we swap types of components, it starts to produce the error "java: the >> switch statement does not cover all possible input values": >> ``` >> void exhaustinvenessWithInterface(Pair pairI) { >> switch (pairI) { >> case Pair(D fst, C snd) -> { >> } >> case Pair(C fst, C snd) -> { >> } >> case Pair(C fst, I snd) -> { >> } >> case Pair(D fst, D snd) -> { >> } >> } >> } >> ``` >> It happens because Javac tries to find the first distinguished type from the >> beginning, but I couldn't find any mention of it in JEP. >> Is this the expected behavior? -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Fri Jul 7 20:38:25 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Fri, 7 Jul 2023 16:38:25 -0400 Subject: Exhaustiveness for record pattern with switch (JEP 440+441) In-Reply-To: <314406701.99141832.1688762072167.JavaMail.zimbra@univ-eiffel.fr> References: <314406701.99141832.1688762072167.JavaMail.zimbra@univ-eiffel.fr> Message-ID: Definitely agreed. On Fri, Jul 7, 2023 at 4:34?PM Remi Forax wrote: > Looks like a bug too me, > the possible combinations are (C, C), (C, D) (D, C) (D,D), they are all > covered. > > R?mi > > ------------------------------ > > *From: *"David Alayachew" > *To: *"Mikhail Pyltsin" > *Cc: *"compiler-dev" , "amber-dev" < > amber-dev at openjdk.org> > *Sent: *Friday, July 7, 2023 10:43:26 PM > *Subject: *Re: Exhaustiveness for record pattern with switch (JEP 440+441) > > I've CC'd the Amber Dev Team. > > On Fri, Jul 7, 2023 at 1:52?PM Mikhail Pyltsin wrote: > >> Hi! >> I investigated the algorithm, which Javac 21 (Build 30 (2023/7/6)) >> ) uses to check exhaustiveness for record patterns (JEP 440+441), and >> found the strange behavior: >> - let's take compilable code >> ``` >> class Test22 { >> >> record Pair(I i1, I i2) {} >> >> sealed interface I {} >> >> record C() implements I {} >> >> record D() implements I {} >> >> void exhaustinvenessWithInterface(Pair pairI) { >> switch (pairI) { >> case Pair(C fst, D snd) -> { >> } >> case Pair(C fst, C snd) -> { >> } >> case Pair(I fst, C snd) -> { >> } >> case Pair(D fst, D snd) -> { >> } >> } >> } >> } >> ``` >> - If we swap types of components, it starts to produce the error "java: >> the switch statement does not cover all possible input values": >> ``` >> void exhaustinvenessWithInterface(Pair pairI) { >> switch (pairI) { >> case Pair(D fst, C snd) -> { >> } >> case Pair(C fst, C snd) -> { >> } >> case Pair(C fst, I snd) -> { >> } >> case Pair(D fst, D snd) -> { >> } >> } >> } >> ``` >> It happens because Javac tries to find the first distinguished type from >> the beginning, but I couldn't find any mention of it in JEP. >> Is this the expected behavior? >> >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: From redio.development at gmail.com Fri Jul 7 23:40:02 2023 From: redio.development at gmail.com (Red IO) Date: Sat, 8 Jul 2023 01:40:02 +0200 Subject: Forward declaring the identity of an object In-Reply-To: References: Message-ID: You got how null and objects work slightly wrong resulting in some wrong assumptions. Remember all object variables in Java are just memory addresses (it's a bit more complicated but the details don't matter here). If you assign the object to the final field/ variable all you are doing is taking the memory address of the thing on the right and storing it in the field. The field itself also has an address so it would be possible to change it afterwards but saying the field is final makes that "illegal". The "change the variable itself afterwards" idea also is the reason you think the keyword should be at the use site. But this puts all the work and the track keeping to the runtime which now has to keep a list of what fields belong to this delayed object. Something the memory address (field) is already doing by itself in case of a valid object. So the simpler and more logical approach is to just set the field to the already prepared memory location the object will be initialized to. But mark that location so it will be treated as null if accessed through a field. This requires only 1 note for the runtime per object instead of per field. I don't know if you are familiar with C but here are the 2 approaches modeled in C: your approach: Foo* foo = null; Foo* foo2 = null; //runtime remembers fields Foo** __foo_field = &foo; Foo** __foo_field2 = &foo2; // some stuff //foo init Foo* actual_foo = malloc(sizeof(Foo)); init_foo(actual_foo); // setting of every forwarded declare field *__foo_field = actual_foo; *__foo_field2 = actual_foo; // runtime forgetting now fully init fields. Foo** __foo_field = nullptr; Foo** __foo_field2 = nullptr; My approach: Foo* actual_foo = malloc(sizeof(Foo)); Foo* foo = actual_foo; Foo* foo2 = actual_foo; //Runtime remembers memory as not initialized Foo* uninit_memory = actual_foo; //Some stuff //foo init init_foo(uninnit_memory); // runtime forgetting now initialized memory uninnit_memory = nullptr; As you can see your approach is taking linearly more recourses and is harder to manage as it scales with the number of effected fields while my approach is staying constant. Hope this cleared up some things. But don't underestimate the effort to implement it. It's likely to much effort in both approaches. Great regards RedIODev On Fri, Jul 7, 2023, 15:11 David Alayachew wrote: > I actually just remembered this. > > I decided earlier on that any keyword that is used to denote this feature > would be used where the variable is being passed in, like a "hall pass". > > Node a; > Node b = new Node(__hall_pass a); > a = new Node(b); > > I wanted to highlight this because your example put the keyword at the > declaration site rather than the use site. > > Doing it my way is clearer because, for example in the enum example I gave > earlier, you change things to say this. > > enum RPS7 > { > > Rock(__hall_pass Scissors), > Paper(Rock), > Scissors(Paper), > ; > > //etc > > > } > > Plus, in my (very ignorant and uninformed) view of how the runtime works, > putting the keyword at the use site would more closely map to what actions > the runtime takes, as well as where it would do it. > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Mon Jul 10 13:31:27 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Mon, 10 Jul 2023 09:31:27 -0400 Subject: Using qualified enum in `old` switch In-Reply-To: References: Message-ID: CC'ing the Amber Dev Team. On Mon, Jul 10, 2023 at 4:21?AM Mikhail Pyltsin wrote: > Hi! > I am investigating a new version of jep440+441 > ( > https://cr.openjdk.org/~gbierman/jep440%2B441/jep440+441-20230612/specs/patterns-switch-record-patterns-jls.html > ) > After `2023-06-12: Misc editorial changes.` I can't find any explicit > mention that it is allowed to use qualified names for enums in `old` switch. > But according to https://openjdk.org/jeps/441 it must be allowed > ``` > > static void goodEnumSwitch2(Coin c) { > switch (c) { > case HEADS -> { > System.out.println("Heads"); > } > case Coin.TAILS -> { // Unnecessary qualification but allowed > System.out.println("Tails"); > } > } > } > > > ``` > before `2023-06-12: Misc editorial changes.`, It was > ```Every case constant must be either (1) the null literal, (2) a > constant expression (15.29 > ), > or (3) the (simple or qualified) name of an enum constant (8.9.1 > ); > otherwise a compile-time error occurs. A single null case constant may > also be paired with the default keyword. > ```, but now there is no mention of the type of enum names. > > Could you help me, which point allows it now? > > This question arose because the next code doesn't produce errors: > ``` > enum EN{A, B} > > public void test(EN en) { > switch (en) { > case A -> System.out.println("a"); > case EN.A -> System.out.println("a too"); > case EN.B -> System.out.println("b"); > } > } > ``` > Is this expected? > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Mon Jul 10 13:43:13 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Mon, 10 Jul 2023 09:43:13 -0400 Subject: Forward declaring the identity of an object In-Reply-To: References: Message-ID: Hello RedIODev, Thank you for your response! > The "change the variable itself afterwards" idea also is > the reason you think the keyword should be at the use > site. > > But this puts all the work and the track keeping to the > runtime which now has to keep a list of what fields > belong to this delayed object. Something the memory > address (field) is already doing by itself in case of a > valid object. > > So the simpler and more logical approach is to just set > the field to the already prepared memory location the > object will be initialized to. But mark that location so > it will be treated as null if accessed through a field. > This requires only 1 note for the runtime per object > instead of per field. > > I don't know if you are familiar with C but here are the > 2 approaches modeled in C: > > your approach: > > Foo* foo = null; > Foo* foo2 = null; > > //runtime remembers fields > Foo** __foo_field = &foo; > Foo** __foo_field2 = &foo2; > > // some stuff > > //foo init > Foo* actual_foo = malloc(sizeof(Foo)); > init_foo(actual_foo); > > // setting of every forwarded declare field > *__foo_field = actual_foo; > *__foo_field2 = actual_foo; > > // runtime forgetting now fully init fields. > Foo** __foo_field = nullptr; > Foo** __foo_field2 = nullptr; > > My approach: > > Foo* actual_foo = malloc(sizeof(Foo)); > Foo* foo = actual_foo; > Foo* foo2 = actual_foo; > > //Runtime remembers memory as not initialized > Foo* uninit_memory = actual_foo; > > //Some stuff > > //foo init > init_foo(uninnit_memory); > > // runtime forgetting now initialized memory > uninnit_memory = nullptr; > > As you can see your approach is taking linearly more > recourses and is harder to manage as it scales with the > number of effected fields while my approach is staying > constant. Thanks for highlighting this with the C example. I see now what you mean by how my idea should have been written. > You got how null and objects work slightly wrong > resulting in some wrong assumptions. > > Remember all object variables in Java are just memory > addresses (it's a bit more complicated but the details > don't matter here). If you assign the object to the final > field/ variable all you are doing is taking the memory > address of the thing on the right and storing it in the > field. The field itself also has an address so it would > be possible to change it afterwards but saying the field > is final makes that "illegal". Hmmmmm, I'm not fully following what you mean here though. It sounds like you are saying 2 addresses, one for the variable and one for the newly allocated object? Or am I misreading that? That may also contribute to me not understanding that first sentence of this quote either. > Hope this cleared up some things. > > But don't underestimate the effort to implement it. It's > likely to much effort in both approaches. It definitely did, thank you very much. And yes, I see that the level of effort for this is going to be significant. But I still feel like this is something within reach -- enough so that it might be worth doing. Thank you for your time and help! David Alayachew -------------- next part -------------- An HTML attachment was scrubbed... URL: From duke at openjdk.org Mon Jul 10 23:53:23 2023 From: duke at openjdk.org (David Alayachew) Date: Mon, 10 Jul 2023 23:53:23 GMT Subject: [amber-docs] RFR: Fixing syntax in example Message-ID: Missing a parentheses ------------- Commit messages: - Fixing syntax in example Changes: https://git.openjdk.org/amber-docs/pull/20/files Webrev: https://webrevs.openjdk.org/?repo=amber-docs&pr=20&range=00 Stats: 1 line in 1 file changed: 0 ins; 0 del; 1 mod Patch: https://git.openjdk.org/amber-docs/pull/20.diff Fetch: git fetch https://git.openjdk.org/amber-docs.git pull/20/head:pull/20 PR: https://git.openjdk.org/amber-docs/pull/20 From duke at openjdk.org Tue Jul 11 08:20:35 2023 From: duke at openjdk.org (duke) Date: Tue, 11 Jul 2023 08:20:35 GMT Subject: git: openjdk/amber-docs: Fixing syntax in example (#20) Message-ID: Changeset: 33c5b894 Author: David Alayachew Committer: GitHub Date: 2023-07-11 04:19:35 +0000 URL: https://git.openjdk.org/amber-docs/commit/33c5b894f224491e1c998f9bd6521b6395fca292 Fixing syntax in example (#20) Missing a parentheses ! site/design-notes/patterns/pattern-match-object-model.md From gbierman at openjdk.org Tue Jul 11 08:22:14 2023 From: gbierman at openjdk.org (Gavin Bierman) Date: Tue, 11 Jul 2023 08:22:14 GMT Subject: [amber-docs] Withdrawn: Fixing syntax in example In-Reply-To: References: Message-ID: On Mon, 10 Jul 2023 23:45:09 GMT, David Alayachew wrote: > Missing a parentheses This pull request has been closed without being integrated. ------------- PR: https://git.openjdk.org/amber-docs/pull/20 From duke at openjdk.org Tue Jul 11 08:24:21 2023 From: duke at openjdk.org (duke) Date: Tue, 11 Jul 2023 08:24:21 GMT Subject: git: openjdk/amber-docs: adding links to Inside.java and Dev.java. The former is very specific to Amber. The latter is a general learning page for many of the Amber topics. Let me know if I should remove the Dev.java link. (#12) Message-ID: <26e48f2f-262b-4ff9-a82e-e59a051c45b8@openjdk.org> Changeset: de5988ee Author: Chad Arimura Committer: GitHub Date: 2023-07-11 01:22:10 +0000 URL: https://git.openjdk.org/amber-docs/commit/de5988eeac2a671c61cf217c8180c8de35103620 adding links to Inside.java and Dev.java. The former is very specific to Amber. The latter is a general learning page for many of the Amber topics. Let me know if I should remove the Dev.java link. (#12) Co-authored-by: Gavin Bierman ! site/_index.md From duke at openjdk.org Tue Jul 11 08:26:06 2023 From: duke at openjdk.org (Chad Arimura) Date: Tue, 11 Jul 2023 08:26:06 GMT Subject: [amber-docs] RFR: adding links to Inside.java and Dev.java [v2] In-Reply-To: References: Message-ID: > Creating this PR because I wanted to point to Amber's wiki but realized it didn't point back to all the aggregated Amber content on Inside.java. I included Dev.java as well but can remove if it's not specific enough. It does have a lot of Amber coverage throughout. Chad Arimura has updated the pull request with a new target base due to a merge or a rebase. The pull request now contains two commits: - Merge branch 'master' into master - adding links to Inside.java and Dev.java. The former is very specific to Amber. The latter is a general learning page for many of the Amber topics. Let me know if I should remove the Dev.java link. ------------- Changes: https://git.openjdk.org/amber-docs/pull/12/files Webrev: https://webrevs.openjdk.org/?repo=amber-docs&pr=12&range=01 Stats: 3 lines in 1 file changed: 3 ins; 0 del; 0 mod Patch: https://git.openjdk.org/amber-docs/pull/12.diff Fetch: git fetch https://git.openjdk.org/amber-docs.git pull/12/head:pull/12 PR: https://git.openjdk.org/amber-docs/pull/12 From gbierman at openjdk.org Tue Jul 11 08:26:08 2023 From: gbierman at openjdk.org (Gavin Bierman) Date: Tue, 11 Jul 2023 08:26:08 GMT Subject: [amber-docs] Withdrawn: adding links to Inside.java and Dev.java In-Reply-To: References: Message-ID: On Wed, 9 Mar 2022 23:53:40 GMT, Chad Arimura wrote: > Creating this PR because I wanted to point to Amber's wiki but realized it didn't point back to all the aggregated Amber content on Inside.java. I included Dev.java as well but can remove if it's not specific enough. It does have a lot of Amber coverage throughout. This pull request has been closed without being integrated. ------------- PR: https://git.openjdk.org/amber-docs/pull/12 From duke at openjdk.org Tue Jul 11 12:07:11 2023 From: duke at openjdk.org (David Alayachew) Date: Tue, 11 Jul 2023 12:07:11 GMT Subject: [amber-docs] RFR: Fixing syntax in example In-Reply-To: References: Message-ID: On Mon, 10 Jul 2023 23:45:09 GMT, David Alayachew wrote: > Missing a parentheses > @GavinBierman @davidalayachew Warning! This commit did not result in any changes! No push attempt will be made. Does this mean anything, or are we good? I know for a fact that it indeed changed stuff, so I am thinking that this warning is being given in error? ------------- PR Comment: https://git.openjdk.org/amber-docs/pull/20#issuecomment-1630702126 From gbierman at openjdk.org Tue Jul 11 12:12:16 2023 From: gbierman at openjdk.org (Gavin Bierman) Date: Tue, 11 Jul 2023 12:12:16 GMT Subject: [amber-docs] RFR: Fixing syntax in example In-Reply-To: References: Message-ID: On Mon, 10 Jul 2023 23:45:09 GMT, David Alayachew wrote: > Missing a parentheses I believe I messed up but the change has been made. Sorry about that. On 11 Jul 2023, at 13:05, David Alayachew ***@***.***> wrote: @GavinBierman @davidalayachew Warning! This commit did not result in any changes! No push attempt will be made. Does this mean anything, or are we good? I know for a fact that it indeed changed stuff, so I am thinking that this warning is being given in error? ? Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you were mentioned.Message ID: ***@***.***> ------------- PR Comment: https://git.openjdk.org/amber-docs/pull/20#issuecomment-1630708385 From duke at openjdk.org Tue Jul 11 12:15:14 2023 From: duke at openjdk.org (David Alayachew) Date: Tue, 11 Jul 2023 12:15:14 GMT Subject: [amber-docs] RFR: Fixing syntax in example In-Reply-To: References: Message-ID: <3cFFcfL2ZjS9iqiM8cnnuSGRk3IWr4KR_2SyTnh6FUg=.a9018e0c-e03b-49fd-801d-ed219d7326ec@github.com> On Tue, 11 Jul 2023 12:09:27 GMT, Gavin Bierman wrote: >> Missing a parentheses > > I believe I messed up but the change has been made. Sorry about that. > > On 11 Jul 2023, at 13:05, David Alayachew ***@***.***> wrote: > > > > @GavinBierman @davidalayachew Warning! This commit did not result in any changes! No push attempt will be made. > > Does this mean anything, or are we good? I know for a fact that it indeed changed stuff, so I am thinking that this warning is being given in error? > > ? > Reply to this email directly, view it on GitHub, or unsubscribe. > You are receiving this because you were mentioned.Message ID: ***@***.***> > I believe I messed up but the change has been made. Sorry about that. On 11 Jul 2023, at 13:05, David Alayachew ***@***.***> wrote: @GavinBierman<[https://urldefense.com/v3/__https://github.com/GavinBierman__;!!ACWV5N9M2RV99hQ!LdwkA5XaeqAA1JHyj4tq5iCRkRJwFw5KTLk6gMLXoZZOn29_X61rPb_nuo7F2jL7BuHGYyGXmBH76QUfWBOlCRkhC_U$](https://urldefense.com/v3/__https://github.com/GavinBierman__;!!ACWV5N9M2RV99hQ!LdwkA5XaeqAA1JHyj4tq5iCRkRJwFw5KTLk6gMLXoZZOn29_X61rPb_nuo7F2jL7BuHGYyGXmBH76QUfWBOlCRkhC_U%24)> @davidalayachew<[https://urldefense.com/v3/__https://github.com/davidalayachew__;!!ACWV5N9M2RV99hQ!LdwkA5XaeqAA1JHyj4tq5iCRkRJwFw5KTLk6gMLXoZZOn29_X61rPb_nuo7F2jL7BuHGYyGXmBH76QUfWBOlEB5DMB8$](https://urldefense.com/v3/__https://github.com/davidalayachew__;!!ACWV5N9M2RV99hQ!LdwkA5XaeqAA1JHyj4tq5iCRkRJwFw5KTLk6gMLXoZZOn29_X61rPb_nuo7F2jL7BuHGYyGXmBH76QUfWBOlEB5DMB8%24)> Warning! This commit did not result in any changes! No push attempt will be made. Does this mean anything, or are we good? I k now for a fact that it indeed changed stuff, so I am thinking that this warning is being given in error? ? Reply to this email directly, view it on GitHub<[https://urldefense.com/v3/__https://github.com/openjdk/amber-docs/pull/20*issuecomment-1630702126__;Iw!!ACWV5N9M2RV99hQ!LdwkA5XaeqAA1JHyj4tq5iCRkRJwFw5KTLk6gMLXoZZOn29_X61rPb_nuo7F2jL7BuHGYyGXmBH76QUfWBOlyavvk-k$](https://urldefense.com/v3/__https://github.com/openjdk/amber-docs/pull/20*issuecomment-1630702126__;Iw!!ACWV5N9M2RV99hQ!LdwkA5XaeqAA1JHyj4tq5iCRkRJwFw5KTLk6gMLXoZZOn29_X61rPb_nuo7F2jL7BuHGYyGXmBH76QUfWBOlyavvk-k%24)>, or unsubscribe<[https://urldefense.com/v3/__https://github.com/notifications/unsubscribe-auth/ABVJVO5C4J7LZYPZ43BVIATXPU6PHANCNFSM6AAAAAA2FGUUNA__;!!ACWV5N9M2RV99hQ!LdwkA5XaeqAA1JHyj4tq5iCRkRJwFw5KTLk6gMLXoZZOn29_X61rPb_nuo7F2jL7BuHGYyGXmBH76QUfWBOlYHOIfAY$](https://urldefense.com/v3/__https://github.com/notifications/unsubscribe-auth/ABVJVO5C4J7LZYPZ43BVIATXPU6PHANCNFSM6AAAAAA2FGUUNA__;!!ACWV5N9M2RV99hQ !LdwkA5XaeqAA1JHyj4tq5iCRkRJwFw5KTLk6gMLXoZZOn29_X61rPb_nuo7F2jL7BuHGYyGXmBH76QUfWBOlYHOIfAY%24)>. You are receiving this because you were mentioned.Message ID: ***@***.***> All good. Let me know if I need to do anything. ------------- PR Comment: https://git.openjdk.org/amber-docs/pull/20#issuecomment-1630712876 From forax at univ-mlv.fr Tue Jul 11 13:45:38 2023 From: forax at univ-mlv.fr (Remi Forax) Date: Tue, 11 Jul 2023 15:45:38 +0200 (CEST) Subject: Presentation at JCrete.org Message-ID: <1767623611.101388881.1689083138947.JavaMail.zimbra@univ-eiffel.fr> Hello, I've given an informal talk at JCrete on the template processor / string template, the content is here https://github.com/forax/string-template Since i've hacked two template processors, - a JSON processor that creates a tree of JSONObject/JSONArray from a JSON string template and - a Logger processor that log the string template. The JSON Processor uses jackson to do the parsing but is able to only parse the string template once, so it's far more efficient than using ObjectMapper.readTree(). The Logger get the current class using a StackWalker, get the logger from the class name (it uses java.util.logging but there is nothing specific to it), checks if the logging level is enabled and log the string template. The whole implementation is loaded as a hidden class so it does not appear in the stacktrace. I had to rely on the fact that the return value of StringTemplate.fragments() is a constant for a callsite, so an implementation detail, otherwise the performance were terrible. Even with that, for the Logger, I was not able to avoid the creation of a StringTemplate when no logging is needed (something which is easy to do with a lambda). So I don't believe a logger based on a template processor makes sense at least not until StringTemplate.Processor.Linkage becomes non-sealed. Actually, the main issue in term of performance is the implementation of StringTemplate, which internally uses an array of Objects and and array of longs to store the values. A slimmer implementation could use fields and uses arrays only if the number of values get big (something like 6 objects and 2 longs and reuse at max 2 objects to store the array of Objects and/or the array of longs if there are more objects/primitives values than 6/2. At least, all the fields should be declared @Stable. The StringTemplate is also a good candidate to be a value type but the inheritance has to be removed for that. But all these optimisations would be unecessary if StringTemplate.Processor.Linkage was not sealed. The good news is that all this performance pot holes have nothing to do with the way a template processor is compiled so those things can be improved over time without needed to recompile the code. cheers, R?mi BTW, i've also discovered that the lambda metafactory fails with a VerifyError if the lambda is declared inside a hidden class, I will submit a bug soon once i've a small reproductible code. From michael.figuiere at gmail.com Tue Jul 11 18:34:25 2023 From: michael.figuiere at gmail.com (=?UTF-8?B?TWljaGHDq2wgRmlndWnDqHJl?=) Date: Tue, 11 Jul 2023 11:34:25 -0700 Subject: Presentation at JCrete.org In-Reply-To: <1767623611.101388881.1689083138947.JavaMail.zimbra@univ-eiffel.fr> References: <1767623611.101388881.1689083138947.JavaMail.zimbra@univ-eiffel.fr> Message-ID: > > I had to rely on the fact that the return value of > StringTemplate.fragments() is a constant for a callsite, so an > implementation detail, otherwise the performance were terrible. Even with > that, for the Logger, I was not able to avoid the creation of a > StringTemplate when no logging is needed (something which is easy to do > with a lambda). So I don't believe a logger based on a template processor > makes sense at least not until StringTemplate.Processor.Linkage becomes > non-sealed. > I've faced the same issue when experimenting with some StringTemplate Processor implementations: having to rely on the identity of the fragments to be able to uniquely identify a template callsite to be able to cache the expensive resources necessary for the processor (a parsed JSON structure here, but could be any kind of prepared statement for a database, compiled code, pre-populated buffers, etc). Complex use cases requiring such resource caching are likely to be popular once the API is finalized in a future JDK, which will lead to a bunch of IdentityHashMaps or equivalent over StringTemplate.fragments(). This feels a bit hackish for a brand new API. I understand from the past conversation with Stephen that separating the StringTemplate into a Template and BoundTemplate is likely off the table, but we'll still need a proper way to uniquely identify the callsite. If the identity of StringTemplate.fragments() is the way, then its Javadoc should clearly state that it's constant for a given callsite to permanently set it so that developers can rely upon it as such. cheers, Micha?l -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Sat Jul 15 01:32:52 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Fri, 14 Jul 2023 21:32:52 -0400 Subject: Found a bug? Message-ID: Hello Amber Dev Team, I was working on a project when, upon compilation, an exception occurred in the compiler. Here is the error message. david at 123 MINGW64 ~/_TESTING/io/github/davidalayachew/Calculator (main) $ ls Addition.java ConsEmpty.java Division.java Operator.java Symbol.java Calculator.java ConsIntegerDigit.java IntegerDigitList.java State.java SymbolCategory.java ConsDecimalDigit.java Constant.java InternalState.java SubState.java ConsDecimalPoint.java DecimalDigitList.java Multiplication.java Subtraction.java david at 123 MINGW64 ~/_TESTING/io/github/davidalayachew/Calculator (main) $ javac *.java An exception has occurred in the compiler (21-ea). Please file a bug against the Java compiler via the Java bug reporting page ( https://bugreport.java.com) after checking the Bug Database ( https://bugs.java.com) for duplicates. Include your program, the following diagnostic, and the parameters passed to the Java compiler in your report. Thank you. java.lang.AssertionError: Unknown pattern: ANYPATTERN at jdk.compiler/com.sun.tools.javac.util.Assert.error(Assert.java:162) at jdk.compiler/com.sun.tools.javac.comp.Check.patternDominated(Check.java:4759) at jdk.compiler/com.sun.tools.javac.comp.Check.patternDominated(Check.java:4748) at jdk.compiler/com.sun.tools.javac.comp.Check.checkSwitchCaseLabelDominated(Check.java:4703) at jdk.compiler/com.sun.tools.javac.comp.Attr.handleSwitch(Attr.java:1850) at jdk.compiler/com.sun.tools.javac.comp.Attr.visitSwitchExpression(Attr.java:1629) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCSwitchExpression.accept(JCTree.java:1399) at jdk.compiler/com.sun.tools.javac.comp.Attr.attribTree(Attr.java:662) at jdk.compiler/com.sun.tools.javac.comp.Attr.visitYield(Attr.java:2326) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCYield.accept(JCTree.java:1677) at jdk.compiler/com.sun.tools.javac.comp.Attr.attribTree(Attr.java:662) at jdk.compiler/com.sun.tools.javac.comp.Attr.attribStat(Attr.java:736) at jdk.compiler/com.sun.tools.javac.comp.Attr.attribStats(Attr.java:755) at jdk.compiler/com.sun.tools.javac.comp.Attr.lambda$visitSwitchExpression$7(Attr.java:1631) at jdk.compiler/com.sun.tools.javac.comp.Attr.handleSwitch(Attr.java:1839) at jdk.compiler/com.sun.tools.javac.comp.Attr.visitSwitchExpression(Attr.java:1629) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCSwitchExpression.accept(JCTree.java:1399) at jdk.compiler/com.sun.tools.javac.comp.Attr.attribTree(Attr.java:662) at jdk.compiler/com.sun.tools.javac.comp.Attr.visitReturn(Attr.java:2464) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCReturn.accept(JCTree.java:1728) at jdk.compiler/com.sun.tools.javac.comp.Attr.attribTree(Attr.java:662) at jdk.compiler/com.sun.tools.javac.comp.Attr.attribStat(Attr.java:736) at jdk.compiler/com.sun.tools.javac.comp.Attr.attribStats(Attr.java:755) at jdk.compiler/com.sun.tools.javac.comp.Attr.visitBlock(Attr.java:1439) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCBlock.accept(JCTree.java:1092) at jdk.compiler/com.sun.tools.javac.comp.Attr.attribTree(Attr.java:662) at jdk.compiler/com.sun.tools.javac.comp.Attr.attribStat(Attr.java:736) at jdk.compiler/com.sun.tools.javac.comp.Attr.visitMethodDef(Attr.java:1229) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCMethodDecl.accept(JCTree.java:916) at jdk.compiler/com.sun.tools.javac.comp.Attr.attribTree(Attr.java:662) at jdk.compiler/com.sun.tools.javac.comp.Attr.attribStat(Attr.java:736) at jdk.compiler/com.sun.tools.javac.comp.Attr.attribClassBody(Attr.java:5644) at jdk.compiler/com.sun.tools.javac.comp.Attr.attribClass(Attr.java:5532) at jdk.compiler/com.sun.tools.javac.comp.Attr.attribClass(Attr.java:5356) at jdk.compiler/com.sun.tools.javac.comp.Attr.attrib(Attr.java:5295) at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.attribute(JavaCompiler.java:1358) at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.compile(JavaCompiler.java:978) at jdk.compiler/com.sun.tools.javac.main.Main.compile(Main.java:319) at jdk.compiler/com.sun.tools.javac.main.Main.compile(Main.java:178) at jdk.compiler/com.sun.tools.javac.Main.compile(Main.java:64) at jdk.compiler/com.sun.tools.javac.Main.main(Main.java:50) I have also attached a zip file containing the project and the generated output from this error. If this is indeed a bug, I will submit a bug for it like the error requested. But I wanted to check here first. Thank you for your time and help! David Alayachew -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Sat Jul 15 01:34:38 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Fri, 14 Jul 2023 21:34:38 -0400 Subject: Found a bug? In-Reply-To: References: Message-ID: I also checked the bug database, and I didn't see anything with the same error message as this one. Wouldn't surprise me, since this feature is so new. -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Sat Jul 15 01:37:54 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Fri, 14 Jul 2023 21:37:54 -0400 Subject: Found a bug? In-Reply-To: References: Message-ID: Also forgot to mention, here is my javac and java version. $ javac --version javac 21-ea $ java --version openjdk 21-ea 2023-09-19 OpenJDK Runtime Environment (build 21-ea+31-2444) OpenJDK 64-Bit Server VM (build 21-ea+31-2444, mixed mode, sharing) -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Sat Jul 15 03:35:16 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Fri, 14 Jul 2023 23:35:16 -0400 Subject: Found a bug? In-Reply-To: References: Message-ID: I think I found the line of code that is causing the error. It is in Symbol.java. Here is the snippet itself. public List stateTransitions(final InternalState internalState) { return switch (this) { case C -> ADD_SYMBOLS.apply(List.of(NUMBER, SUB_STATE_OPERATOR), List.of()); case CE -> ADD_SYMBOLS.apply(List.of(NUMBER, SUB_STATE_OPERATOR), List.of(C)); case BACKSPACE -> switch (internalState) { case InternalState(_, ConsEmpty(), _, _, _, _) -> ADD_SYMBOLS.apply(List.of(NUMBER, SUB_STATE_OPERATOR), List.of(C, CE)); case InternalState(_, _, _, _, _, _) -> ADD_SYMBOLS.apply(List.of(NUMBER, SUB_STATE_OPERATOR, SCREEN_OPERATOR), List.of()); }; }; } -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Sat Jul 15 03:45:18 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Fri, 14 Jul 2023 23:45:18 -0400 Subject: Found a bug? In-Reply-To: References: Message-ID: And if I replace that snippet with this, I get the expected and desirable compiler error "didn't cover all switch cases" (referring to the parent switch where that statement is actually true). public List stateTransitions(final InternalState internalState) { return switch (this) { case C -> ADD_SYMBOLS.apply(List.of(NUMBER, SUB_STATE_OPERATOR), List.of()); case CE -> ADD_SYMBOLS.apply(List.of(NUMBER, SUB_STATE_OPERATOR), List.of(C)); case BACKSPACE -> switch (internalState) { case InternalState(_, ConsEmpty(), _, _, _, _) -> ADD_SYMBOLS.apply(List.of(NUMBER, SUB_STATE_OPERATOR), List.of(C, CE)); case InternalState is -> ADD_SYMBOLS.apply(List.of(NUMBER, SUB_STATE_OPERATOR, SCREEN_OPERATOR), List.of()); }; }; } -------------- next part -------------- An HTML attachment was scrubbed... URL: From angelos.bimpoudis at oracle.com Mon Jul 17 09:28:17 2023 From: angelos.bimpoudis at oracle.com (Angelos Bimpoudis) Date: Mon, 17 Jul 2023 09:28:17 +0000 Subject: Found a bug? In-Reply-To: References: Message-ID: Hey David, I minimized and filed it here: https://bugs.openjdk.org/browse/JDK-8312163 Thanks for the analysis, David! ________________________________ From: amber-dev on behalf of David Alayachew Sent: 15 July 2023 05:45 To: amber-dev Subject: Re: Found a bug? And if I replace that snippet with this, I get the expected and desirable compiler error "didn't cover all switch cases" (referring to the parent switch where that statement is actually true). public List stateTransitions(final InternalState internalState) { return switch (this) { case C -> ADD_SYMBOLS.apply(List.of(NUMBER, SUB_STATE_OPERATOR), List.of()); case CE -> ADD_SYMBOLS.apply(List.of(NUMBER, SUB_STATE_OPERATOR), List.of(C)); case BACKSPACE -> switch (internalState) { case InternalState(_, ConsEmpty(), _, _, _, _) -> ADD_SYMBOLS.apply(List.of(NUMBER, SUB_STATE_OPERATOR), List.of(C, CE)); case InternalState is -> ADD_SYMBOLS.apply(List.of(NUMBER, SUB_STATE_OPERATOR, SCREEN_OPERATOR), List.of()); }; }; } -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Tue Jul 18 04:12:49 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Tue, 18 Jul 2023 00:12:49 -0400 Subject: Got a compiler stack trace Message-ID: Hello Amber Dev Team, I received the following stack trace when trying to compile the attached project. david at 123 MINGW64 ~/ToDoList (main) $ ls ColumnName.java Operator.java OperatorType.java ToDoList.gpj ToDoList.java david at 123 MINGW64 ~/ToDoList (main) $ javac --version javac 21-ea david at 123 MINGW64 ~/ToDoList (main) $ java --version openjdk 21-ea 2023-09-19 OpenJDK Runtime Environment (build 21-ea+31-2444) OpenJDK 64-Bit Server VM (build 21-ea+31-2444, mixed mode, sharing) david at 123 MINGW64 ~/ToDoList (main) $ where java C:\Program Files\Java\jdk-21\bin\java.exe david at LAPTOP-OAAP0AJG MINGW64 ~/_WORKSPACE/_PROGRAMMING/_JAVA/HelperFunctions/src/main/java/io/github/davidalayachew/ToDoList (main) $ /c/Progra~1/Java/jdk-21/bin/javac.exe ToDoList.java An exception has occurred in the compiler (21-ea). Please file a bug against the Java compiler via the Java bug reporting page ( https://bugreport.java.com) after checking the Bug Database ( https://bugs.java.com) for duplicates. Include your program, the following diagnostic, and the parameters passed to the Java compiler in your report. Thank you. java.lang.AssertionError at jdk.compiler/com.sun.tools.javac.util.Assert.error(Assert.java:155) at jdk.compiler/com.sun.tools.javac.util.Assert.check(Assert.java:46) at jdk.compiler/com.sun.tools.javac.comp.Lower.access(Lower.java:1227) at jdk.compiler/com.sun.tools.javac.comp.Lower.visitIdent(Lower.java:3499) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCIdent.accept(JCTree.java:2715) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) at jdk.compiler/com.sun.tools.javac.comp.Lower.visitSelect(Lower.java:4201) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCFieldAccess.accept(JCTree.java:2581) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) at jdk.compiler/com.sun.tools.javac.comp.Lower.visitApply(Lower.java:3167) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCMethodInvocation.accept(JCTree.java:1816) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2192) at jdk.compiler/com.sun.tools.javac.comp.Lower.visitVarDef(Lower.java:3675) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCVariableDecl.accept(JCTree.java:1022) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:70) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.visitBlock(TreeTranslator.java:167) at jdk.compiler/com.sun.tools.javac.comp.Lower.visitBlock(Lower.java:3689) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCBlock.accept(JCTree.java:1092) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.visitMethodDef(TreeTranslator.java:150) at jdk.compiler/com.sun.tools.javac.comp.Lower.visitMethodDefInternal(Lower.java:2858) at jdk.compiler/com.sun.tools.javac.comp.Lower.visitMethodDef(Lower.java:2774) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCMethodDecl.accept(JCTree.java:916) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) at jdk.compiler/com.sun.tools.javac.comp.Lower.visitClassDef(Lower.java:2323) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCClassDecl.accept(JCTree.java:814) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) at jdk.compiler/com.sun.tools.javac.comp.Lower.visitNewClass(Lower.java:2956) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCNewClass.accept(JCTree.java:1871) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2192) at jdk.compiler/com.sun.tools.javac.comp.Lower.visitYield(Lower.java:4181) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCYield.accept(JCTree.java:1677) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:70) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.visitBlock(TreeTranslator.java:167) at jdk.compiler/com.sun.tools.javac.comp.Lower.visitBlock(Lower.java:3689) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCBlock.accept(JCTree.java:1092) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:70) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.visitCase(TreeTranslator.java:212) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCCase.accept(JCTree.java:1343) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translateCases(TreeTranslator.java:94) at jdk.compiler/com.sun.tools.javac.comp.Lower.handleSwitch(Lower.java:3801) at jdk.compiler/com.sun.tools.javac.comp.Lower.visitSwitchExpression(Lower.java:3733) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCSwitchExpression.accept(JCTree.java:1399) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2192) at jdk.compiler/com.sun.tools.javac.comp.Lower.visitVarDef(Lower.java:3675) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCVariableDecl.accept(JCTree.java:1022) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:70) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.visitBlock(TreeTranslator.java:167) at jdk.compiler/com.sun.tools.javac.comp.Lower.visitBlock(Lower.java:3689) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCBlock.accept(JCTree.java:1092) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.visitMethodDef(TreeTranslator.java:150) at jdk.compiler/com.sun.tools.javac.comp.Lower.visitMethodDefInternal(Lower.java:2858) at jdk.compiler/com.sun.tools.javac.comp.Lower.visitMethodDef(Lower.java:2774) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCMethodDecl.accept(JCTree.java:916) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) at jdk.compiler/com.sun.tools.javac.comp.Lower.visitClassDef(Lower.java:2323) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCClassDecl.accept(JCTree.java:814) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) at jdk.compiler/com.sun.tools.javac.comp.Lower.visitNewClass(Lower.java:2956) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCNewClass.accept(JCTree.java:1871) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2192) at jdk.compiler/com.sun.tools.javac.comp.Lower.visitVarDef(Lower.java:3675) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCVariableDecl.accept(JCTree.java:1022) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:70) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.visitBlock(TreeTranslator.java:167) at jdk.compiler/com.sun.tools.javac.comp.Lower.visitBlock(Lower.java:3689) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCBlock.accept(JCTree.java:1092) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.visitMethodDef(TreeTranslator.java:150) at jdk.compiler/com.sun.tools.javac.comp.Lower.visitMethodDefInternal(Lower.java:2858) at jdk.compiler/com.sun.tools.javac.comp.Lower.visitMethodDef(Lower.java:2774) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCMethodDecl.accept(JCTree.java:916) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) at jdk.compiler/com.sun.tools.javac.comp.Lower.visitClassDef(Lower.java:2323) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCClassDecl.accept(JCTree.java:814) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2200) at jdk.compiler/com.sun.tools.javac.comp.Lower.translateTopLevelClass(Lower.java:4293) at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.desugar(JavaCompiler.java:1653) at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.desugar(JavaCompiler.java:1467) at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.compile(JavaCompiler.java:978) at jdk.compiler/com.sun.tools.javac.main.Main.compile(Main.java:319) at jdk.compiler/com.sun.tools.javac.main.Main.compile(Main.java:178) at jdk.compiler/com.sun.tools.javac.Main.compile(Main.java:64) at jdk.compiler/com.sun.tools.javac.Main.main(Main.java:50) Please let me know if any more information is needed. I can also post this to the bug database like the directions say, just let me know. Finally, I have attached another email thread that may be relevant. I don't think it is the same bug, but adding in case it is. Thank you for your time and efforts! David Alayachew -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: ToDoList.zip Type: application/x-zip-compressed Size: 4180 bytes Desc: not available URL: -------------- next part -------------- An embedded message was scrubbed... From: Angelos Bimpoudis Subject: Re: Found a bug? Date: Mon, 17 Jul 2023 09:28:17 +0000 Size: 20811 URL: From davidalayachew at gmail.com Tue Jul 18 04:16:27 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Tue, 18 Jul 2023 00:16:27 -0400 Subject: Found a bug? In-Reply-To: References: Message-ID: Hello, Thank you for your response! Anytime. I think I have another 2 for you. Maybe they are the same issue, different error, or maybe they are a different problem entirely. https://mail.openjdk.org/pipermail/amber-dev/2023-July/008206.html https://mail.openjdk.org/pipermail/core-libs-dev/2023-July/109395.html Thank you for your time and help! David Alayachew On Mon, Jul 17, 2023 at 5:28?AM Angelos Bimpoudis < angelos.bimpoudis at oracle.com> wrote: > Hey David, > > I minimized and filed it here: https://bugs.openjdk.org/browse/JDK-8312163 > > Thanks for the analysis, David! > > > ------------------------------ > *From:* amber-dev on behalf of David > Alayachew > *Sent:* 15 July 2023 05:45 > *To:* amber-dev > *Subject:* Re: Found a bug? > > And if I replace that snippet with this, I get the expected and desirable > compiler error "didn't cover all switch cases" (referring to the parent > switch where that statement is actually true). > > > public List stateTransitions(final InternalState internalState) > { > > return > switch (this) > { > > case C -> ADD_SYMBOLS.apply(List.of(NUMBER, > SUB_STATE_OPERATOR), List.of()); > case CE -> ADD_SYMBOLS.apply(List.of(NUMBER, > SUB_STATE_OPERATOR), List.of(C)); > case BACKSPACE -> switch (internalState) > { > > case InternalState(_, ConsEmpty(), _, _, > _, _) -> ADD_SYMBOLS.apply(List.of(NUMBER, SUB_STATE_OPERATOR), List.of(C, > CE)); > case InternalState is > -> ADD_SYMBOLS.apply(List.of(NUMBER, SUB_STATE_OPERATOR, > SCREEN_OPERATOR), List.of()); > > }; > > }; > > } > -------------- next part -------------- An HTML attachment was scrubbed... URL: From angelos.bimpoudis at oracle.com Tue Jul 18 08:02:36 2023 From: angelos.bimpoudis at oracle.com (Angelos Bimpoudis) Date: Tue, 18 Jul 2023 08:02:36 +0000 Subject: Got a compiler stack trace In-Reply-To: References: Message-ID: Hello David, Many, many thanks for the investigations! ? Much appreciated. It would be ideal if we can have a minimized test case (self-contained test file without dependencies to other libraries, or code, no zip-attachments) that demonstrates the compiler issue succinctly and possibly fits in the email body (if possible, of course; usually it is possible for compiler bugs). For example, in your previous email "Found a bug?", even if you didn't include a minimized test file, pasting your code helped to reconstruct the issue, as the code was not attached. Can you give it a shot with this one and provide a minimized example in your next email? ? ________________________________ From: amber-dev on behalf of David Alayachew Sent: 18 July 2023 06:12 To: amber-dev Subject: Got a compiler stack trace Hello Amber Dev Team, I received the following stack trace when trying to compile the attached project. david at 123 MINGW64 ~/ToDoList (main) $ ls ColumnName.java Operator.java OperatorType.java ToDoList.gpj ToDoList.java david at 123 MINGW64 ~/ToDoList (main) $ javac --version javac 21-ea david at 123 MINGW64 ~/ToDoList (main) $ java --version openjdk 21-ea 2023-09-19 OpenJDK Runtime Environment (build 21-ea+31-2444) OpenJDK 64-Bit Server VM (build 21-ea+31-2444, mixed mode, sharing) david at 123 MINGW64 ~/ToDoList (main) $ where java C:\Program Files\Java\jdk-21\bin\java.exe david at LAPTOP-OAAP0AJG MINGW64 ~/_WORKSPACE/_PROGRAMMING/_JAVA/HelperFunctions/src/main/java/io/github/davidalayachew/ToDoList (main) $ /c/Progra~1/Java/jdk-21/bin/javac.exe ToDoList.java An exception has occurred in the compiler (21-ea). Please file a bug against the Java compiler via the Java bug reporting page (https://bugreport.java.com) after checking the Bug Database (https://bugs.java.com) for duplicates. Include your program, the following diagnostic, and the parameters passed to the Java compiler in your report. Thank you. java.lang.AssertionError at jdk.compiler/com.sun.tools.javac.util.Assert.error(Assert.java:155) at jdk.compiler/com.sun.tools.javac.util.Assert.check(Assert.java:46) at jdk.compiler/com.sun.tools.javac.comp.Lower.access(Lower.java:1227) at jdk.compiler/com.sun.tools.javac.comp.Lower.visitIdent(Lower.java:3499) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCIdent.accept(JCTree.java:2715) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) at jdk.compiler/com.sun.tools.javac.comp.Lower.visitSelect(Lower.java:4201) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCFieldAccess.accept(JCTree.java:2581) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) at jdk.compiler/com.sun.tools.javac.comp.Lower.visitApply(Lower.java:3167) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCMethodInvocation.accept(JCTree.java:1816) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2192) at jdk.compiler/com.sun.tools.javac.comp.Lower.visitVarDef(Lower.java:3675) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCVariableDecl.accept(JCTree.java:1022) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:70) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.visitBlock(TreeTranslator.java:167) at jdk.compiler/com.sun.tools.javac.comp.Lower.visitBlock(Lower.java:3689) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCBlock.accept(JCTree.java:1092) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.visitMethodDef(TreeTranslator.java:150) at jdk.compiler/com.sun.tools.javac.comp.Lower.visitMethodDefInternal(Lower.java:2858) at jdk.compiler/com.sun.tools.javac.comp.Lower.visitMethodDef(Lower.java:2774) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCMethodDecl.accept(JCTree.java:916) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) at jdk.compiler/com.sun.tools.javac.comp.Lower.visitClassDef(Lower.java:2323) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCClassDecl.accept(JCTree.java:814) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) at jdk.compiler/com.sun.tools.javac.comp.Lower.visitNewClass(Lower.java:2956) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCNewClass.accept(JCTree.java:1871) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2192) at jdk.compiler/com.sun.tools.javac.comp.Lower.visitYield(Lower.java:4181) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCYield.accept(JCTree.java:1677) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:70) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.visitBlock(TreeTranslator.java:167) at jdk.compiler/com.sun.tools.javac.comp.Lower.visitBlock(Lower.java:3689) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCBlock.accept(JCTree.java:1092) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:70) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.visitCase(TreeTranslator.java:212) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCCase.accept(JCTree.java:1343) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translateCases(TreeTranslator.java:94) at jdk.compiler/com.sun.tools.javac.comp.Lower.handleSwitch(Lower.java:3801) at jdk.compiler/com.sun.tools.javac.comp.Lower.visitSwitchExpression(Lower.java:3733) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCSwitchExpression.accept(JCTree.java:1399) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2192) at jdk.compiler/com.sun.tools.javac.comp.Lower.visitVarDef(Lower.java:3675) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCVariableDecl.accept(JCTree.java:1022) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:70) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.visitBlock(TreeTranslator.java:167) at jdk.compiler/com.sun.tools.javac.comp.Lower.visitBlock(Lower.java:3689) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCBlock.accept(JCTree.java:1092) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.visitMethodDef(TreeTranslator.java:150) at jdk.compiler/com.sun.tools.javac.comp.Lower.visitMethodDefInternal(Lower.java:2858) at jdk.compiler/com.sun.tools.javac.comp.Lower.visitMethodDef(Lower.java:2774) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCMethodDecl.accept(JCTree.java:916) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) at jdk.compiler/com.sun.tools.javac.comp.Lower.visitClassDef(Lower.java:2323) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCClassDecl.accept(JCTree.java:814) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) at jdk.compiler/com.sun.tools.javac.comp.Lower.visitNewClass(Lower.java:2956) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCNewClass.accept(JCTree.java:1871) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2192) at jdk.compiler/com.sun.tools.javac.comp.Lower.visitVarDef(Lower.java:3675) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCVariableDecl.accept(JCTree.java:1022) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:70) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.visitBlock(TreeTranslator.java:167) at jdk.compiler/com.sun.tools.javac.comp.Lower.visitBlock(Lower.java:3689) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCBlock.accept(JCTree.java:1092) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.visitMethodDef(TreeTranslator.java:150) at jdk.compiler/com.sun.tools.javac.comp.Lower.visitMethodDefInternal(Lower.java:2858) at jdk.compiler/com.sun.tools.javac.comp.Lower.visitMethodDef(Lower.java:2774) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCMethodDecl.accept(JCTree.java:916) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) at jdk.compiler/com.sun.tools.javac.comp.Lower.visitClassDef(Lower.java:2323) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCClassDecl.accept(JCTree.java:814) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2200) at jdk.compiler/com.sun.tools.javac.comp.Lower.translateTopLevelClass(Lower.java:4293) at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.desugar(JavaCompiler.java:1653) at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.desugar(JavaCompiler.java:1467) at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.compile(JavaCompiler.java:978) at jdk.compiler/com.sun.tools.javac.main.Main.compile(Main.java:319) at jdk.compiler/com.sun.tools.javac.main.Main.compile(Main.java:178) at jdk.compiler/com.sun.tools.javac.Main.compile(Main.java:64) at jdk.compiler/com.sun.tools.javac.Main.main(Main.java:50) Please let me know if any more information is needed. I can also post this to the bug database like the directions say, just let me know. Finally, I have attached another email thread that may be relevant. I don't think it is the same bug, but adding in case it is. Thank you for your time and efforts! David Alayachew -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Tue Jul 18 13:13:49 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Tue, 18 Jul 2023 09:13:49 -0400 Subject: Got a compiler stack trace In-Reply-To: References: Message-ID: Hello, Thank you for your response! Here is the best that I can do. import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JDialog; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.JTextField; import javax.swing.RowFilter; import javax.swing.table.DefaultTableModel; import javax.swing.table.JTableHeader; import javax.swing.table.TableRowSorter; import java.awt.Dimension; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Collection; import java.util.List; import java.util.function.BiConsumer; import java.util.stream.Stream; public class ToDoList { enum Operator { EQUAL, ; } enum OperatorType { NUMERIC, ; } public static void main(String[] args) { final ActionListener hoaisd = hmmm -> { final RowFilter rowFilter = switch (OperatorType.NUMERIC) { case OperatorType.NUMERIC -> { final JTextField filterTextField = new JTextField(10); yield new RowFilter<>() { public boolean include(final RowFilter.Entry entry) { final double filterNumber = Double.parseDouble(filterTextField.getText()); return true; } }; } default -> throw new UnsupportedOperationException(); }; }; } } I compiled the above with the following command. $ javac --enable-preview --source 21 ToDoList.java And I got the exact same error. Let me know if you need me to approach this differently. Thank you for your time and help! David Alayachew On Tue, Jul 18, 2023 at 4:02?AM Angelos Bimpoudis < angelos.bimpoudis at oracle.com> wrote: > Hello David, > > Many, many thanks for the investigations! ? Much appreciated. > > It would be ideal if we can have a minimized test case (self-contained > test file without dependencies to other libraries, or code, no > zip-attachments) that demonstrates the compiler issue succinctly and > possibly fits in the email body (if possible, of course; usually it is > possible for compiler bugs). > > For example, in your previous email "Found a bug?", even if you didn't > include a minimized test file, pasting your code helped to reconstruct the > issue, as the code was not attached. > > Can you give it a shot with this one and provide a minimized example in > your next email? ? > ------------------------------ > *From:* amber-dev on behalf of David > Alayachew > *Sent:* 18 July 2023 06:12 > *To:* amber-dev > *Subject:* Got a compiler stack trace > > Hello Amber Dev Team, > > I received the following stack trace when trying to compile the attached > project. > > david at 123 MINGW64 ~/ToDoList (main) > $ ls > ColumnName.java Operator.java OperatorType.java ToDoList.gpj > ToDoList.java > > david at 123 MINGW64 ~/ToDoList (main) > $ javac --version > javac 21-ea > > david at 123 MINGW64 ~/ToDoList (main) > $ java --version > openjdk 21-ea 2023-09-19 > OpenJDK Runtime Environment (build 21-ea+31-2444) > OpenJDK 64-Bit Server VM (build 21-ea+31-2444, mixed mode, sharing) > > david at 123 MINGW64 ~/ToDoList (main) > $ where java > C:\Program Files\Java\jdk-21\bin\java.exe > > > david at LAPTOP-OAAP0AJG MINGW64 > ~/_WORKSPACE/_PROGRAMMING/_JAVA/HelperFunctions/src/main/java/io/github/davidalayachew/ToDoList > (main) > $ /c/Progra~1/Java/jdk-21/bin/javac.exe ToDoList.java > An exception has occurred in the compiler (21-ea). Please file a bug > against the Java compiler via the Java bug reporting page ( > https://bugreport.java.com) after checking the Bug Database ( > https://bugs.java.com) for duplicates. Include your program, the > following diagnostic, and the parameters passed to the Java compiler in > your report. Thank you. > java.lang.AssertionError > at > jdk.compiler/com.sun.tools.javac.util.Assert.error(Assert.java:155) > at > jdk.compiler/com.sun.tools.javac.util.Assert.check(Assert.java:46) > at > jdk.compiler/com.sun.tools.javac.comp.Lower.access(Lower.java:1227) > at > jdk.compiler/com.sun.tools.javac.comp.Lower.visitIdent(Lower.java:3499) > at > jdk.compiler/com.sun.tools.javac.tree.JCTree$JCIdent.accept(JCTree.java:2715) > at > jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) > at > jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) > at > jdk.compiler/com.sun.tools.javac.comp.Lower.visitSelect(Lower.java:4201) > at > jdk.compiler/com.sun.tools.javac.tree.JCTree$JCFieldAccess.accept(JCTree.java:2581) > at > jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) > at > jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) > at > jdk.compiler/com.sun.tools.javac.comp.Lower.visitApply(Lower.java:3167) > at > jdk.compiler/com.sun.tools.javac.tree.JCTree$JCMethodInvocation.accept(JCTree.java:1816) > at > jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) > at > jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) > at > jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2192) > at > jdk.compiler/com.sun.tools.javac.comp.Lower.visitVarDef(Lower.java:3675) > at > jdk.compiler/com.sun.tools.javac.tree.JCTree$JCVariableDecl.accept(JCTree.java:1022) > at > jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) > at > jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) > at > jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:70) > at > jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.visitBlock(TreeTranslator.java:167) > at > jdk.compiler/com.sun.tools.javac.comp.Lower.visitBlock(Lower.java:3689) > at > jdk.compiler/com.sun.tools.javac.tree.JCTree$JCBlock.accept(JCTree.java:1092) > at > jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) > at > jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) > at > jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.visitMethodDef(TreeTranslator.java:150) > at > jdk.compiler/com.sun.tools.javac.comp.Lower.visitMethodDefInternal(Lower.java:2858) > at > jdk.compiler/com.sun.tools.javac.comp.Lower.visitMethodDef(Lower.java:2774) > at > jdk.compiler/com.sun.tools.javac.tree.JCTree$JCMethodDecl.accept(JCTree.java:916) > at > jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) > at > jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) > at > jdk.compiler/com.sun.tools.javac.comp.Lower.visitClassDef(Lower.java:2323) > at > jdk.compiler/com.sun.tools.javac.tree.JCTree$JCClassDecl.accept(JCTree.java:814) > at > jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) > at > jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) > at > jdk.compiler/com.sun.tools.javac.comp.Lower.visitNewClass(Lower.java:2956) > at > jdk.compiler/com.sun.tools.javac.tree.JCTree$JCNewClass.accept(JCTree.java:1871) > at > jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) > at > jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) > at > jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2192) > at > jdk.compiler/com.sun.tools.javac.comp.Lower.visitYield(Lower.java:4181) > at > jdk.compiler/com.sun.tools.javac.tree.JCTree$JCYield.accept(JCTree.java:1677) > at > jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) > at > jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) > at > jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:70) > at > jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.visitBlock(TreeTranslator.java:167) > at > jdk.compiler/com.sun.tools.javac.comp.Lower.visitBlock(Lower.java:3689) > at > jdk.compiler/com.sun.tools.javac.tree.JCTree$JCBlock.accept(JCTree.java:1092) > at > jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) > at > jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) > at > jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:70) > at > jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.visitCase(TreeTranslator.java:212) > at > jdk.compiler/com.sun.tools.javac.tree.JCTree$JCCase.accept(JCTree.java:1343) > at > jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) > at > jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) > at > jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translateCases(TreeTranslator.java:94) > at > jdk.compiler/com.sun.tools.javac.comp.Lower.handleSwitch(Lower.java:3801) > at > jdk.compiler/com.sun.tools.javac.comp.Lower.visitSwitchExpression(Lower.java:3733) > at > jdk.compiler/com.sun.tools.javac.tree.JCTree$JCSwitchExpression.accept(JCTree.java:1399) > at > jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) > at > jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) > at > jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2192) > at > jdk.compiler/com.sun.tools.javac.comp.Lower.visitVarDef(Lower.java:3675) > at > jdk.compiler/com.sun.tools.javac.tree.JCTree$JCVariableDecl.accept(JCTree.java:1022) > at > jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) > at > jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) > at > jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:70) > at > jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.visitBlock(TreeTranslator.java:167) > at > jdk.compiler/com.sun.tools.javac.comp.Lower.visitBlock(Lower.java:3689) > at > jdk.compiler/com.sun.tools.javac.tree.JCTree$JCBlock.accept(JCTree.java:1092) > at > jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) > at > jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) > at > jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.visitMethodDef(TreeTranslator.java:150) > at > jdk.compiler/com.sun.tools.javac.comp.Lower.visitMethodDefInternal(Lower.java:2858) > at > jdk.compiler/com.sun.tools.javac.comp.Lower.visitMethodDef(Lower.java:2774) > at > jdk.compiler/com.sun.tools.javac.tree.JCTree$JCMethodDecl.accept(JCTree.java:916) > at > jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) > at > jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) > at > jdk.compiler/com.sun.tools.javac.comp.Lower.visitClassDef(Lower.java:2323) > at > jdk.compiler/com.sun.tools.javac.tree.JCTree$JCClassDecl.accept(JCTree.java:814) > at > jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) > at > jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) > at > jdk.compiler/com.sun.tools.javac.comp.Lower.visitNewClass(Lower.java:2956) > at > jdk.compiler/com.sun.tools.javac.tree.JCTree$JCNewClass.accept(JCTree.java:1871) > at > jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) > at > jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) > at > jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2192) > at > jdk.compiler/com.sun.tools.javac.comp.Lower.visitVarDef(Lower.java:3675) > at > jdk.compiler/com.sun.tools.javac.tree.JCTree$JCVariableDecl.accept(JCTree.java:1022) > at > jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) > at > jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) > at > jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:70) > at > jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.visitBlock(TreeTranslator.java:167) > at > jdk.compiler/com.sun.tools.javac.comp.Lower.visitBlock(Lower.java:3689) > at > jdk.compiler/com.sun.tools.javac.tree.JCTree$JCBlock.accept(JCTree.java:1092) > at > jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) > at > jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) > at > jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.visitMethodDef(TreeTranslator.java:150) > at > jdk.compiler/com.sun.tools.javac.comp.Lower.visitMethodDefInternal(Lower.java:2858) > at > jdk.compiler/com.sun.tools.javac.comp.Lower.visitMethodDef(Lower.java:2774) > at > jdk.compiler/com.sun.tools.javac.tree.JCTree$JCMethodDecl.accept(JCTree.java:916) > at > jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) > at > jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) > at > jdk.compiler/com.sun.tools.javac.comp.Lower.visitClassDef(Lower.java:2323) > at > jdk.compiler/com.sun.tools.javac.tree.JCTree$JCClassDecl.accept(JCTree.java:814) > at > jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) > at > jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) > at > jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2200) > at > jdk.compiler/com.sun.tools.javac.comp.Lower.translateTopLevelClass(Lower.java:4293) > at > jdk.compiler/com.sun.tools.javac.main.JavaCompiler.desugar(JavaCompiler.java:1653) > at > jdk.compiler/com.sun.tools.javac.main.JavaCompiler.desugar(JavaCompiler.java:1467) > at > jdk.compiler/com.sun.tools.javac.main.JavaCompiler.compile(JavaCompiler.java:978) > at > jdk.compiler/com.sun.tools.javac.main.Main.compile(Main.java:319) > at > jdk.compiler/com.sun.tools.javac.main.Main.compile(Main.java:178) > at jdk.compiler/com.sun.tools.javac.Main.compile(Main.java:64) > at jdk.compiler/com.sun.tools.javac.Main.main(Main.java:50) > > > > > Please let me know if any more information is needed. I can also post this > to the bug database like the directions say, just let me know. > > Finally, I have attached another email thread that may be relevant. I > don't think it is the same bug, but adding in case it is. > > Thank you for your time and efforts! > David Alayachew > -------------- next part -------------- An HTML attachment was scrubbed... URL: From angelos.bimpoudis at oracle.com Tue Jul 18 16:45:15 2023 From: angelos.bimpoudis at oracle.com (Angelos Bimpoudis) Date: Tue, 18 Jul 2023 16:45:15 +0000 Subject: [External] : Re: Got a compiler stack trace In-Reply-To: References: Message-ID: That's an excellent report. Concise and self-contained! I created a bug report here with some additional decluttering: https://bugs.openjdk.org/browse/JDK-8312229 Many thanks ________________________________ From: David Alayachew Sent: 18 July 2023 15:13 To: Angelos Bimpoudis Cc: amber-dev Subject: [External] : Re: Got a compiler stack trace Hello, Thank you for your response! Here is the best that I can do. import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JDialog; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.JTextField; import javax.swing.RowFilter; import javax.swing.table.DefaultTableModel; import javax.swing.table.JTableHeader; import javax.swing.table.TableRowSorter; import java.awt.Dimension; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Collection; import java.util.List; import java.util.function.BiConsumer; import java.util.stream.Stream; public class ToDoList { enum Operator { EQUAL, ; } enum OperatorType { NUMERIC, ; } public static void main(String[] args) { final ActionListener hoaisd = hmmm -> { final RowFilter rowFilter = switch (OperatorType.NUMERIC) { case OperatorType.NUMERIC -> { final JTextField filterTextField = new JTextField(10); yield new RowFilter<>() { public boolean include(final RowFilter.Entry entry) { final double filterNumber = Double.parseDouble(filterTextField.getText()); return true; } }; } default -> throw new UnsupportedOperationException(); }; }; } } I compiled the above with the following command. $ javac --enable-preview --source 21 ToDoList.java And I got the exact same error. Let me know if you need me to approach this differently. Thank you for your time and help! David Alayachew On Tue, Jul 18, 2023 at 4:02?AM Angelos Bimpoudis > wrote: Hello David, Many, many thanks for the investigations! ? Much appreciated. It would be ideal if we can have a minimized test case (self-contained test file without dependencies to other libraries, or code, no zip-attachments) that demonstrates the compiler issue succinctly and possibly fits in the email body (if possible, of course; usually it is possible for compiler bugs). For example, in your previous email "Found a bug?", even if you didn't include a minimized test file, pasting your code helped to reconstruct the issue, as the code was not attached. Can you give it a shot with this one and provide a minimized example in your next email? ? ________________________________ From: amber-dev > on behalf of David Alayachew > Sent: 18 July 2023 06:12 To: amber-dev > Subject: Got a compiler stack trace Hello Amber Dev Team, I received the following stack trace when trying to compile the attached project. david at 123 MINGW64 ~/ToDoList (main) $ ls ColumnName.java Operator.java OperatorType.java ToDoList.gpj ToDoList.java david at 123 MINGW64 ~/ToDoList (main) $ javac --version javac 21-ea david at 123 MINGW64 ~/ToDoList (main) $ java --version openjdk 21-ea 2023-09-19 OpenJDK Runtime Environment (build 21-ea+31-2444) OpenJDK 64-Bit Server VM (build 21-ea+31-2444, mixed mode, sharing) david at 123 MINGW64 ~/ToDoList (main) $ where java C:\Program Files\Java\jdk-21\bin\java.exe david at LAPTOP-OAAP0AJG MINGW64 ~/_WORKSPACE/_PROGRAMMING/_JAVA/HelperFunctions/src/main/java/io/github/davidalayachew/ToDoList (main) $ /c/Progra~1/Java/jdk-21/bin/javac.exe ToDoList.java An exception has occurred in the compiler (21-ea). Please file a bug against the Java compiler via the Java bug reporting page (https://bugreport.java.com) after checking the Bug Database (https://bugs.java.com) for duplicates. Include your program, the following diagnostic, and the parameters passed to the Java compiler in your report. Thank you. java.lang.AssertionError at jdk.compiler/com.sun.tools.javac.util.Assert.error(Assert.java:155) at jdk.compiler/com.sun.tools.javac.util.Assert.check(Assert.java:46) at jdk.compiler/com.sun.tools.javac.comp.Lower.access(Lower.java:1227) at jdk.compiler/com.sun.tools.javac.comp.Lower.visitIdent(Lower.java:3499) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCIdent.accept(JCTree.java:2715) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) at jdk.compiler/com.sun.tools.javac.comp.Lower.visitSelect(Lower.java:4201) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCFieldAccess.accept(JCTree.java:2581) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) at jdk.compiler/com.sun.tools.javac.comp.Lower.visitApply(Lower.java:3167) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCMethodInvocation.accept(JCTree.java:1816) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2192) at jdk.compiler/com.sun.tools.javac.comp.Lower.visitVarDef(Lower.java:3675) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCVariableDecl.accept(JCTree.java:1022) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:70) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.visitBlock(TreeTranslator.java:167) at jdk.compiler/com.sun.tools.javac.comp.Lower.visitBlock(Lower.java:3689) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCBlock.accept(JCTree.java:1092) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.visitMethodDef(TreeTranslator.java:150) at jdk.compiler/com.sun.tools.javac.comp.Lower.visitMethodDefInternal(Lower.java:2858) at jdk.compiler/com.sun.tools.javac.comp.Lower.visitMethodDef(Lower.java:2774) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCMethodDecl.accept(JCTree.java:916) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) at jdk.compiler/com.sun.tools.javac.comp.Lower.visitClassDef(Lower.java:2323) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCClassDecl.accept(JCTree.java:814) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) at jdk.compiler/com.sun.tools.javac.comp.Lower.visitNewClass(Lower.java:2956) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCNewClass.accept(JCTree.java:1871) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2192) at jdk.compiler/com.sun.tools.javac.comp.Lower.visitYield(Lower.java:4181) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCYield.accept(JCTree.java:1677) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:70) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.visitBlock(TreeTranslator.java:167) at jdk.compiler/com.sun.tools.javac.comp.Lower.visitBlock(Lower.java:3689) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCBlock.accept(JCTree.java:1092) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:70) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.visitCase(TreeTranslator.java:212) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCCase.accept(JCTree.java:1343) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translateCases(TreeTranslator.java:94) at jdk.compiler/com.sun.tools.javac.comp.Lower.handleSwitch(Lower.java:3801) at jdk.compiler/com.sun.tools.javac.comp.Lower.visitSwitchExpression(Lower.java:3733) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCSwitchExpression.accept(JCTree.java:1399) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2192) at jdk.compiler/com.sun.tools.javac.comp.Lower.visitVarDef(Lower.java:3675) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCVariableDecl.accept(JCTree.java:1022) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:70) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.visitBlock(TreeTranslator.java:167) at jdk.compiler/com.sun.tools.javac.comp.Lower.visitBlock(Lower.java:3689) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCBlock.accept(JCTree.java:1092) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.visitMethodDef(TreeTranslator.java:150) at jdk.compiler/com.sun.tools.javac.comp.Lower.visitMethodDefInternal(Lower.java:2858) at jdk.compiler/com.sun.tools.javac.comp.Lower.visitMethodDef(Lower.java:2774) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCMethodDecl.accept(JCTree.java:916) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) at jdk.compiler/com.sun.tools.javac.comp.Lower.visitClassDef(Lower.java:2323) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCClassDecl.accept(JCTree.java:814) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) at jdk.compiler/com.sun.tools.javac.comp.Lower.visitNewClass(Lower.java:2956) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCNewClass.accept(JCTree.java:1871) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2192) at jdk.compiler/com.sun.tools.javac.comp.Lower.visitVarDef(Lower.java:3675) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCVariableDecl.accept(JCTree.java:1022) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:70) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.visitBlock(TreeTranslator.java:167) at jdk.compiler/com.sun.tools.javac.comp.Lower.visitBlock(Lower.java:3689) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCBlock.accept(JCTree.java:1092) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.visitMethodDef(TreeTranslator.java:150) at jdk.compiler/com.sun.tools.javac.comp.Lower.visitMethodDefInternal(Lower.java:2858) at jdk.compiler/com.sun.tools.javac.comp.Lower.visitMethodDef(Lower.java:2774) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCMethodDecl.accept(JCTree.java:916) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) at jdk.compiler/com.sun.tools.javac.comp.Lower.visitClassDef(Lower.java:2323) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCClassDecl.accept(JCTree.java:814) at jdk.compiler/com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2181) at jdk.compiler/com.sun.tools.javac.comp.Lower.translate(Lower.java:2200) at jdk.compiler/com.sun.tools.javac.comp.Lower.translateTopLevelClass(Lower.java:4293) at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.desugar(JavaCompiler.java:1653) at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.desugar(JavaCompiler.java:1467) at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.compile(JavaCompiler.java:978) at jdk.compiler/com.sun.tools.javac.main.Main.compile(Main.java:319) at jdk.compiler/com.sun.tools.javac.main.Main.compile(Main.java:178) at jdk.compiler/com.sun.tools.javac.Main.compile(Main.java:64) at jdk.compiler/com.sun.tools.javac.Main.main(Main.java:50) Please let me know if any more information is needed. I can also post this to the bug database like the directions say, just let me know. Finally, I have attached another email thread that may be relevant. I don't think it is the same bug, but adding in case it is. Thank you for your time and efforts! David Alayachew -------------- next part -------------- An HTML attachment was scrubbed... URL: From duke at openjdk.org Thu Jul 20 22:12:11 2023 From: duke at openjdk.org (David Alayachew) Date: Thu, 20 Jul 2023 22:12:11 GMT Subject: [amber-docs] RFR: Update pattern-matching-for-java.md Message-ID: Update pattern-matching-for-java.md ------------- Commit messages: - Update pattern-matching-for-java.md Changes: https://git.openjdk.org/amber-docs/pull/21/files Webrev: https://webrevs.openjdk.org/?repo=amber-docs&pr=21&range=00 Stats: 1 line in 1 file changed: 0 ins; 0 del; 1 mod Patch: https://git.openjdk.org/amber-docs/pull/21.diff Fetch: git fetch https://git.openjdk.org/amber-docs.git pull/21/head:pull/21 PR: https://git.openjdk.org/amber-docs/pull/21 From duke at openjdk.org Thu Jul 20 22:12:11 2023 From: duke at openjdk.org (Yannick Lamprecht) Date: Thu, 20 Jul 2023 22:12:11 GMT Subject: [amber-docs] RFR: Update pattern-matching-for-java.md In-Reply-To: References: Message-ID: On Thu, 20 Jul 2023 03:03:52 GMT, David Alayachew wrote: > Update pattern-matching-for-java.md Marked as reviewed by yannicklamprecht at github.com (no known OpenJDK username). ------------- PR Review: https://git.openjdk.org/amber-docs/pull/21#pullrequestreview-1540155008 From duke at openjdk.org Fri Jul 21 12:13:54 2023 From: duke at openjdk.org (David Alayachew) Date: Fri, 21 Jul 2023 12:13:54 GMT Subject: [amber-docs] RFR: Update pattern-matching-for-java.md In-Reply-To: References: Message-ID: On Thu, 20 Jul 2023 03:03:52 GMT, David Alayachew wrote: > Update pattern-matching-for-java.md Sorry about that. I'm used to the little bot telling me what to do. EDIT - it actually did, and I just didn't read the directions fully. My mistake. ------------- PR Comment: https://git.openjdk.org/amber-docs/pull/21#issuecomment-1645482938 From gbierman at openjdk.org Fri Jul 21 13:06:56 2023 From: gbierman at openjdk.org (Gavin Bierman) Date: Fri, 21 Jul 2023 13:06:56 GMT Subject: [amber-docs] RFR: Update pattern-matching-for-java.md In-Reply-To: References: Message-ID: On Thu, 20 Jul 2023 03:03:52 GMT, David Alayachew wrote: > Update pattern-matching-for-java.md No problem - let's try again :-) ------------- PR Comment: https://git.openjdk.org/amber-docs/pull/21#issuecomment-1645554973 From duke at openjdk.org Fri Jul 21 13:06:57 2023 From: duke at openjdk.org (David Alayachew) Date: Fri, 21 Jul 2023 13:06:57 GMT Subject: [amber-docs] Integrated: Update pattern-matching-for-java.md In-Reply-To: References: Message-ID: On Thu, 20 Jul 2023 03:03:52 GMT, David Alayachew wrote: > Update pattern-matching-for-java.md This pull request has now been integrated. Changeset: ce76c489 Author: David Alayachew Committer: Gavin Bierman URL: https://git.openjdk.org/amber-docs/commit/ce76c48970751e92342b8565b7e2bfc4a21cc3e6 Stats: 1 line in 1 file changed: 0 ins; 0 del; 1 mod Update pattern-matching-for-java.md ------------- PR: https://git.openjdk.org/amber-docs/pull/21 From brian.goetz at oracle.com Sun Jul 23 01:12:22 2023 From: brian.goetz at oracle.com (Brian Goetz) Date: Sat, 22 Jul 2023 21:12:22 -0400 Subject: Presentation at JCrete.org In-Reply-To: <1767623611.101388881.1689083138947.JavaMail.zimbra@univ-eiffel.fr> References: <1767623611.101388881.1689083138947.JavaMail.zimbra@univ-eiffel.fr> Message-ID: <1008f7d5-2069-f137-2d80-02022635dab4@oracle.com> > So I don't believe a logger based on a template processor makes sense at least not until StringTemplate.Processor.Linkage becomes non-sealed. I'll take this further: I don't think a logger based on a template processor makes sense *at all*. From forax at univ-mlv.fr Sun Jul 23 20:08:27 2023 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Sun, 23 Jul 2023 22:08:27 +0200 (CEST) Subject: Presentation at JCrete.org In-Reply-To: <1008f7d5-2069-f137-2d80-02022635dab4@oracle.com> References: <1767623611.101388881.1689083138947.JavaMail.zimbra@univ-eiffel.fr> <1008f7d5-2069-f137-2d80-02022635dab4@oracle.com> Message-ID: <603628355.2334455.1690142907127.JavaMail.zimbra@univ-eiffel.fr> ----- Original Message ----- > From: "Brian Goetz" > To: "Remi Forax" , "amber-dev" > Sent: Sunday, July 23, 2023 3:12:22 AM > Subject: Re: Presentation at JCrete.org >> So I don't believe a logger based on a template processor makes sense at least >> not until StringTemplate.Processor.Linkage becomes non-sealed. > > I'll take this further: I don't think a logger based on a template > processor makes sense *at all*. May i ask why ? A logger process interpolated strings, combined with the new shiny syntax of the template processor, make sense to me. And this is a slipery slope, if we have good template processor and bad template processor, how to recognize a good template processor from a bad one ? R?mi From davidalayachew at gmail.com Sun Jul 23 22:32:16 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Sun, 23 Jul 2023 18:32:16 -0400 Subject: Presentation at JCrete.org In-Reply-To: <603628355.2334455.1690142907127.JavaMail.zimbra@univ-eiffel.fr> References: <1767623611.101388881.1689083138947.JavaMail.zimbra@univ-eiffel.fr> <1008f7d5-2069-f137-2d80-02022635dab4@oracle.com> <603628355.2334455.1690142907127.JavaMail.zimbra@univ-eiffel.fr> Message-ID: Hello R?mi, > May i ask why ? > > A logger process interpolated strings, combined with the > new shiny syntax of the template processor, make sense to > me. 1. Isn't the entire purpose of String interpolation to (safely) go from raw, unsanitized inputs into a fully formed String? Java allows us to go one step further and create output values other than a String, since doing so aligns perfectly with the broad goal of interpolation -- take raw inputs, sanitize them, and produce a clean, validated output. The JEP even says so in the final bullet of the goals section -- "Enable the creation of non-string values computed from literal text and embedded expressions without having to transit through an intermediate string representation." -- https://openjdk.org/jeps/430 2. And isn't the entire point why the Java team thought this was worth doing was not (just) because it was a simple boilerplate reduction, but because String interpolation is pretty much the front door for injection attacks? So, by attaching the "safe way" to the new shiny syntax, they have incentivized the right way for all developers. With both of those in mind, it becomes clear that mixing side effects with String interpolation makes String interpolation inherently unsafe. For example, let's think through your Logger example. We just got finished (?) dealing with log4j. Somebody clever exploited our loggers, allowing them to download and execute scripts from the outside world. If I were using your logger, where exactly would I perform validations to address this new threat? Yes, I know most of us just upgraded to the version that turned that feature off by default, but assume that you still need that feature. Because of the way your logger is designed, you have actually made it HARDER for the developer to validate the incoming request, not easier. Maybe I could stick the validations in the logger itself? But what if I don't need those validations at every call site, only the service edges? Must I slow down all other log requests to defend myself in situations where it is not possible to be exploited? Should I validate my String before putting it into the Logger? But now the design of your Logger forces me to do low-level validation on raw objects. String Templates allow me to output a "richer" object, which can enable richer validations. For example, if the logger takes in String a, b, and c, then a, b, and c on their own might not create a url, but together they do. I know a url on its own doesn't execute code, but you see my point. The point is, because your Logger won't let me check the outputs before using them, I must perform my validation on the "more raw" data inputs instead. This makes things harder to validate in some cases, and may require that I duplicate work that could be done by the Logger String Template (creating the "rich" output object) in order to make sure I am safe. Alternatively, I could create different loggers that do different validations, or just forgoe validations entirely, but that's just moving the problem and complicating your solution. What if some call sites are vulnerable to ExploitA, others are vulnerable to ExploitB, and others are vulnerable to both? Do you plan to permutate through them all as individual types? Mixing side-effects into String interpolation compromises safety. String interpolation is meant to increase safety, but it cannot guarantee it on its own. The idea is that you take the created String (or T), and then perform further validations upon the output that could not be done within the context of a String interpolator, AND THEN, once you have proven that the output is actually safe, then execute the output, whether that's logging it, handing it to a DB, etc. Sometimes, that won't be necessary, but sometimes it will be, so the 2 need to be separate to facilitate that flexibility. Trying to bundle it all into a String template that executes its inputs takes away this exact opportunity from you. This is why your Logger example, and any other example that does side-effects in a String template, is a bad idea. > And this is a slipery slope, if we have good template > processor and bad template processor, how to recognize a > good template processor from a bad one ? There will be many things that make template processors better or worse than others. But as demonstrated above, state-changing side-effects are firmly in the bad category. And as for good, it's best if you try to make your String templates "pure functions" whenever possible. The rule can be a little flexible (for example, you may have a counter that counts the number of times a Template processor was called), but you should try to stick to this unless absolutely necessary. And since the only real benefit that your logger gives us is removing one line of code, that would definitely not be necessary. LOGGER."\{validate(a)}-\{validate(b)}-\{validate(c)}" Versus doing this. final String output = contextSpecificValidations(SOME_STR."\{a}-\{b}-\{c}"); actualLogger.info(output); You save one line of code and lose out on all of the flexibility. Does it make sense why the LOGGER Template Processor is problematic now? Thank you for your time! David Alayachew On Sun, Jul 23, 2023 at 4:09?PM wrote: > ----- Original Message ----- > > From: "Brian Goetz" > > To: "Remi Forax" , "amber-dev" > > > Sent: Sunday, July 23, 2023 3:12:22 AM > > Subject: Re: Presentation at JCrete.org > > >> So I don't believe a logger based on a template processor makes sense > at least > >> not until StringTemplate.Processor.Linkage becomes non-sealed. > > > > I'll take this further: I don't think a logger based on a template > > processor makes sense *at all*. > > May i ask why ? > > A logger process interpolated strings, combined with the new shiny syntax > of the template processor, make sense to me. > > And this is a slipery slope, if we have good template processor and bad > template processor, how to recognize a good template processor from a bad > one ? > > R?mi > -------------- next part -------------- An HTML attachment was scrubbed... URL: From robbepincket at live.be Mon Jul 24 08:54:15 2023 From: robbepincket at live.be (Robbe Pincket) Date: Mon, 24 Jul 2023 08:54:15 +0000 Subject: Presentation at JCrete.org In-Reply-To: References: <1767623611.101388881.1689083138947.JavaMail.zimbra@univ-eiffel.fr> <1008f7d5-2069-f137-2d80-02022635dab4@oracle.com> <603628355.2334455.1690142907127.JavaMail.zimbra@univ-eiffel.fr> Message-ID: Hi David I feel like I need to disagree on some points. The reason we had the log4j incident was not because people couldn?t validate their inputs, but a misuse of the api (imho due to a bad design decision). The line `logger.info(value);` did not mean ?log this value? like most people expected it to mean, but instead ?use this value as a format string?. In my mind this is not unlike passing a user provided untrusted to `printf` in C. Having a `LOG.?value: \{value}?` would solve this issue. You can?t provide a user provided untrusted string to the logger, only as one of the interpolation arguments, which in any reasonable design wouldn?t be interpreted as a template, only the string the programmer hardcoded into their code gets any template expansion. This means invoking a logger in the way R?mi wants to be able to invoke a logger, would have been more safe, and not less safe then we did (/do) with log4j. This does not mean I think having side effects to string interpolation is a good idea, and it also doesn?t mean that you can?t get this same safety with side effect free interpolation. If the logger?s log methods have an overload for a `Templated` object that would be returned by the `LOG.?...?` interpolation, one could just not template expand any `Strings` passed to the logger. Having `logger.info(LOG.?value: \{value}?)` is a bit more unwieldly to some than just `LOG.?value: \{value}?`, so I think it?s gonna be inevitable that people are gonna add sideeffects somewhere to interpolations. Kind regards Robbe Pincket ________________________________ Van: amber-dev namens David Alayachew Verzonden: Monday, July 24, 2023 12:32:16 AM Aan: forax at univ-mlv.fr CC: Brian Goetz ; amber-dev Onderwerp: Re: Presentation at JCrete.org Hello R?mi, > May i ask why ? > > A logger process interpolated strings, combined with the > new shiny syntax of the template processor, make sense to > me. 1. Isn't the entire purpose of String interpolation to (safely) go from raw, unsanitized inputs into a fully formed String? Java allows us to go one step further and create output values other than a String, since doing so aligns perfectly with the broad goal of interpolation -- take raw inputs, sanitize them, and produce a clean, validated output. The JEP even says so in the final bullet of the goals section -- "Enable the creation of non-string values computed from literal text and embedded expressions without having to transit through an intermediate string representation." -- https://openjdk.org/jeps/430 2. And isn't the entire point why the Java team thought this was worth doing was not (just) because it was a simple boilerplate reduction, but because String interpolation is pretty much the front door for injection attacks? So, by attaching the "safe way" to the new shiny syntax, they have incentivized the right way for all developers. With both of those in mind, it becomes clear that mixing side effects with String interpolation makes String interpolation inherently unsafe. For example, let's think through your Logger example. We just got finished (?) dealing with log4j. Somebody clever exploited our loggers, allowing them to download and execute scripts from the outside world. If I were using your logger, where exactly would I perform validations to address this new threat? Yes, I know most of us just upgraded to the version that turned that feature off by default, but assume that you still need that feature. Because of the way your logger is designed, you have actually made it HARDER for the developer to validate the incoming request, not easier. Maybe I could stick the validations in the logger itself? But what if I don't need those validations at every call site, only the service edges? Must I slow down all other log requests to defend myself in situations where it is not possible to be exploited? Should I validate my String before putting it into the Logger? But now the design of your Logger forces me to do low-level validation on raw objects. String Templates allow me to output a "richer" object, which can enable richer validations. For example, if the logger takes in String a, b, and c, then a, b, and c on their own might not create a url, but together they do. I know a url on its own doesn't execute code, but you see my point. The point is, because your Logger won't let me check the outputs before using them, I must perform my validation on the "more raw" data inputs instead. This makes things harder to validate in some cases, and may require that I duplicate work that could be done by the Logger String Template (creating the "rich" output object) in order to make sure I am safe. Alternatively, I could create different loggers that do different validations, or just forgoe validations entirely, but that's just moving the problem and complicating your solution. What if some call sites are vulnerable to ExploitA, others are vulnerable to ExploitB, and others are vulnerable to both? Do you plan to permutate through them all as individual types? Mixing side-effects into String interpolation compromises safety. String interpolation is meant to increase safety, but it cannot guarantee it on its own. The idea is that you take the created String (or T), and then perform further validations upon the output that could not be done within the context of a String interpolator, AND THEN, once you have proven that the output is actually safe, then execute the output, whether that's logging it, handing it to a DB, etc. Sometimes, that won't be necessary, but sometimes it will be, so the 2 need to be separate to facilitate that flexibility. Trying to bundle it all into a String template that executes its inputs takes away this exact opportunity from you. This is why your Logger example, and any other example that does side-effects in a String template, is a bad idea. > And this is a slipery slope, if we have good template > processor and bad template processor, how to recognize a > good template processor from a bad one ? There will be many things that make template processors better or worse than others. But as demonstrated above, state-changing side-effects are firmly in the bad category. And as for good, it's best if you try to make your String templates "pure functions" whenever possible. The rule can be a little flexible (for example, you may have a counter that counts the number of times a Template processor was called), but you should try to stick to this unless absolutely necessary. And since the only real benefit that your logger gives us is removing one line of code, that would definitely not be necessary. LOGGER."\{validate(a)}-\{validate(b)}-\{validate(c)}" Versus doing this. final String output = contextSpecificValidations(SOME_STR."\{a}-\{b}-\{c}"); actualLogger.info(output); You save one line of code and lose out on all of the flexibility. Does it make sense why the LOGGER Template Processor is problematic now? Thank you for your time! David Alayachew On Sun, Jul 23, 2023 at 4:09?PM > wrote: ----- Original Message ----- > From: "Brian Goetz" > > To: "Remi Forax" >, "amber-dev" > > Sent: Sunday, July 23, 2023 3:12:22 AM > Subject: Re: Presentation at JCrete.org >> So I don't believe a logger based on a template processor makes sense at least >> not until StringTemplate.Processor.Linkage becomes non-sealed. > > I'll take this further: I don't think a logger based on a template > processor makes sense *at all*. May i ask why ? A logger process interpolated strings, combined with the new shiny syntax of the template processor, make sense to me. And this is a slipery slope, if we have good template processor and bad template processor, how to recognize a good template processor from a bad one ? R?mi -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Mon Jul 24 10:25:06 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Mon, 24 Jul 2023 06:25:06 -0400 Subject: Presentation at JCrete.org In-Reply-To: References: <1767623611.101388881.1689083138947.JavaMail.zimbra@univ-eiffel.fr> <1008f7d5-2069-f137-2d80-02022635dab4@oracle.com> <603628355.2334455.1690142907127.JavaMail.zimbra@univ-eiffel.fr> Message-ID: Hello Robbe, Thank you for your response! I was attempting to demonstrate how, like in log4j, providing an unsanitized input to a method and then executing it without allowing/facilitating any context specific validations is less safe. When I said less safe, I did not mean less safe than log4j, I meant less safe than separating it out into 2 method calls, like I demonstrated at the end of my email. I can see how that was confusing though, apologies. And yes, ultimately, log4j was interpreting its inputs as a format. While that strongly resembles a potential failure case of LOGGER template, that was also not my intent. My intent was as simple as, put strings into a method, fail to perform adequate validations for aforementioned reasons, that causes problems. My point was that, when we realized that that method required validations later on, R?mi's idea would force us to validate on the low level inputs (etc., all mentioned in my original post) as opposed to the generated output that immediately gets logged. I understand how this too was unclear, apologies. As for the Logger template, that's a really indirect way of doing things, so much so that I'd say it's worse than my suggestions. I didn't mention it because I think users would sooner validate on inputs or recreate the rich object creation rather than trying to validate on a third type - template. But even putting that aside, my point is that, by design, a Logger template is still encouraging side-effectual code, and thus, is encouraging the problems I mentioned in my original email. The only thing that either of our suggestions do is provide alternative ways to not take the easy path. But having a safe front door does not detract from the fact that there is a (more convenient) back door. One bad apple spoils the bunch. And finally, yes, people will value convenience over safety, as always. Still worth calling out and criticizing when it happens -- especially when it's not just a blog on the internet, but a full blown presentation in front of several people that will likely color how they see this feature and its ideal use case. I ignored his original email last time, but if he doesn't even see what's wrong with it, then it really needs to be addressed. Side effects are a dangerous default, and I think it's unwise to encourage them as a default for a feature whose biggest reason for existing is making safe interpolation convenient. Thank you for your time! David Alayachew -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Mon Jul 24 12:22:26 2023 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Mon, 24 Jul 2023 14:22:26 +0200 (CEST) Subject: Presentation at JCrete.org In-Reply-To: References: <1767623611.101388881.1689083138947.JavaMail.zimbra@univ-eiffel.fr> <1008f7d5-2069-f137-2d80-02022635dab4@oracle.com> <603628355.2334455.1690142907127.JavaMail.zimbra@univ-eiffel.fr> Message-ID: <300958587.2629593.1690201346672.JavaMail.zimbra@univ-eiffel.fr> > From: "David Alayachew" > To: "Remi Forax" > Cc: "Brian Goetz" , "amber-dev" > Sent: Monday, July 24, 2023 12:32:16 AM > Subject: Re: Presentation at JCrete.org > Hello R?mi, > > May i ask why ? > > A logger process interpolated strings, combined with the > > new shiny syntax of the template processor, make sense to > > me. > 1. Isn't the entire purpose of String interpolation to (safely) go from raw, > unsanitized inputs into a fully formed String? Java allows us to go one step > further and create output values other than a String, since doing so aligns > perfectly with the broad goal of interpolation -- take raw inputs, sanitize > them, and produce a clean, validated output. The JEP even says so in the final > bullet of the goals section -- "Enable the creation of non-string values > computed from literal text and embedded expressions without having to transit > through an intermediate string representation." -- [ > https://openjdk.org/jeps/430 | https://openjdk.org/jeps/430 ] > 2. And isn't the entire point why the Java team thought this was worth doing was > not (just) because it was a simple boilerplate reduction, but because String > interpolation is pretty much the front door for injection attacks? So, by > attaching the "safe way" to the new shiny syntax, they have incentivized the > right way for all developers. > With both of those in mind, it becomes clear that mixing side effects with > String interpolation makes String interpolation inherently unsafe. [...] A call to a template processor is a method call (processor) to a Java object (StringTemplate.Processor). If we wanted to not have side effect, instead of using a OOP design we will have used a functional design, in this case a function call with an API closer to a varargs call (similar to what JavaScript does). I believe you have missunderstood how the validation works, there is a separation between the validation of the structure and the escaping of the values. The validation of the structure can be done once while the escaping has to be done for each call. For a logger, the validation depends on what we usually call an appender which is reponsible to transform the string template received by the logger to the correct format. By example, if an application is using a Time Series database like InfluxDB or Prometheus, the appender is responsible to encode the logging information to the right format using the TimeSeries client API. With a template processor and because most of the loggers in Java can change their configuration at runtime, ithe validation can be done once per configuration per appender. The problem of the template processor is not the syntax. It's not even the generated bytecode by javac. It's the implementation of the bootstrap method (see java.lang.runtime.TemplateRuntime) that - box all the values into a StringTemplate, storing all instances as Object in an array and all primitives as long in an another array, dropping the types nicely propagated by the compiler. So all calls using the themplate processor API are slow because of the boxing (the escape analysis is not able to recover this transformation if you take a look to the generated assembly code). - provides no way to do a different validation per callsite, so the validation has to done for each call (remember validation != escaping). There is a better API than the template processor API, FMT already uses it (StringTemplate.Processor.Linkage), but all other template processors can not use it. R?mi > Thank you for your time! > David Alayachew > On Sun, Jul 23, 2023 at 4:09 PM < [ mailto:forax at univ-mlv.fr | forax at univ-mlv.fr > ] > wrote: >> ----- Original Message ----- >>> From: "Brian Goetz" < [ mailto:brian.goetz at oracle.com | brian.goetz at oracle.com ] >> > > >>> To: "Remi Forax" < [ mailto:forax at univ-mlv.fr | forax at univ-mlv.fr ] >, >> > "amber-dev" < [ mailto:amber-dev at openjdk.org | amber-dev at openjdk.org ] > >> > Sent: Sunday, July 23, 2023 3:12:22 AM >> > Subject: Re: Presentation at JCrete.org >> >> So I don't believe a logger based on a template processor makes sense at least >> >> not until StringTemplate.Processor.Linkage becomes non-sealed. >> > I'll take this further: I don't think a logger based on a template >> > processor makes sense *at all*. >> May i ask why ? >> A logger process interpolated strings, combined with the new shiny syntax of the >> template processor, make sense to me. >> And this is a slipery slope, if we have good template processor and bad template >> processor, how to recognize a good template processor from a bad one ? >> R?mi -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Mon Jul 24 12:26:28 2023 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Mon, 24 Jul 2023 14:26:28 +0200 (CEST) Subject: Presentation at JCrete.org In-Reply-To: References: <1767623611.101388881.1689083138947.JavaMail.zimbra@univ-eiffel.fr> <1008f7d5-2069-f137-2d80-02022635dab4@oracle.com> <603628355.2334455.1690142907127.JavaMail.zimbra@univ-eiffel.fr> Message-ID: <1158621703.2630607.1690201588943.JavaMail.zimbra@univ-eiffel.fr> > From: "Robbe Pincket" > To: "David Alayachew" , "Remi Forax" > > Cc: "Brian Goetz" , "amber-dev" > Sent: Monday, July 24, 2023 10:54:15 AM > Subject: RE: Presentation at JCrete.org > Hi David > I feel like I need to disagree on some points. The reason we had the log4j > incident was not because people couldn?t validate their inputs, but a misuse of > the api (imho due to a bad design decision). > The line `logger.info(value);` did not mean ?log this value? like most people > expected it to mean, but instead ?use this value as a format string?. > In my mind this is not unlike passing a user provided untrusted to `printf` in > C. > Having a `LOG.?value: \{value}?` would solve this issue. You can?t provide a > user provided untrusted string to the logger, only as one of the interpolation > arguments, which in any reasonable design wouldn?t be interpreted as a > template, only the string the programmer hardcoded into their code gets any > template expansion. This means invoking a logger in the way R?mi wants to be > able to invoke a logger, would have been more safe, and not less safe then we > did (/do) with log4j. > This does not mean I think having side effects to string interpolation is a good > idea, and it also doesn?t mean that you can?t get this same safety with side > effect free interpolation. If the logger?s log methods have an overload for a > `Templated` object that would be returned by the `LOG.?...?` interpolation, one > could just not template expand any `Strings` passed to the logger. > Having `logger.info(LOG.?value: \{value}?)` is a bit more unwieldly to some than > just `LOG.?value: \{value}?`, so I think it?s gonna be inevitable that people > are gonna add sideeffects somewhere to interpolations. The problem of logger.info(LOG.?value: \{value}?) is that the logger receive a string, not a string template, so the validation of the structure has to be done at each call, and not once per template fragments. > Kind regards > Robbe Pincket R?mi > Van: amber-dev namens David Alayachew > > Verzonden: Monday, July 24, 2023 12:32:16 AM > Aan: forax at univ-mlv.fr > CC: Brian Goetz ; amber-dev > Onderwerp: Re: Presentation at JCrete.org > Hello R?mi, > > May i ask why ? > > A logger process interpolated strings, combined with the > > new shiny syntax of the template processor, make sense to > > me. > 1. Isn't the entire purpose of String interpolation to (safely) go from raw, > unsanitized inputs into a fully formed String? Java allows us to go one step > further and create output values other than a String, since doing so aligns > perfectly with the broad goal of interpolation -- take raw inputs, sanitize > them, and produce a clean, validated output. The JEP even says so in the final > bullet of the goals section -- "Enable the creation of non-string values > computed from literal text and embedded expressions without having to transit > through an intermediate string representation." -- [ > https://openjdk.org/jeps/430 | > https://openjdk.org/jeps/430 ] > 2. And isn't the entire point why the Java team thought this was worth doing was > not (just) because it was a simple boilerplate reduction, but because String > interpolation is pretty much the front door for injection attacks? So, by > attaching the "safe way" to the new shiny syntax, they have incentivized the > right way for all developers. > With both of those in mind, it becomes clear that mixing side effects with > String interpolation makes String interpolation inherently unsafe. > For example, let's think through your Logger example. > We just got finished (?) dealing with log4j. Somebody clever exploited our > loggers, allowing them to download and execute scripts from the outside world. > If I were using your logger, where exactly would I perform validations to > address this new threat? Yes, I know most of us just upgraded to the version > that turned that feature off by default, but assume that you still need that > feature. Because of the way your logger is designed, you have actually made it > HARDER for the developer to validate the incoming request, not easier. > Maybe I could stick the validations in the logger itself? But what if I don't > need those validations at every call site, only the service edges? Must I slow > down all other log requests to defend myself in situations where it is not > possible to be exploited? > Should I validate my String before putting it into the Logger? But now the > design of your Logger forces me to do low-level validation on raw objects. > String Templates allow me to output a "richer" object, which can enable richer > validations. For example, if the logger takes in String a, b, and c, then a, b, > and c on their own might not create a url, but together they do. I know a url > on its own doesn't execute code, but you see my point. The point is, because > your Logger won't let me check the outputs before using them, I must perform my > validation on the "more raw" data inputs instead. This makes things harder to > validate in some cases, and may require that I duplicate work that could be > done by the Logger String Template (creating the "rich" output object) in order > to make sure I am safe. > Alternatively, I could create different loggers that do different validations, > or just forgoe validations entirely, but that's just moving the problem and > complicating your solution. What if some call sites are vulnerable to ExploitA, > others are vulnerable to ExploitB, and others are vulnerable to both? Do you > plan to permutate through them all as individual types? > Mixing side-effects into String interpolation compromises safety. > String interpolation is meant to increase safety, but it cannot guarantee it on > its own. The idea is that you take the created String (or T), and then perform > further validations upon the output that could not be done within the context > of a String interpolator, AND THEN, once you have proven that the output is > actually safe, then execute the output, whether that's logging it, handing it > to a DB, etc. Sometimes, that won't be necessary, but sometimes it will be, so > the 2 need to be separate to facilitate that flexibility. > Trying to bundle it all into a String template that executes its inputs takes > away this exact opportunity from you. This is why your Logger example, and any > other example that does side-effects in a String template, is a bad idea. > > And this is a slipery slope, if we have good template > > processor and bad template processor, how to recognize a > > good template processor from a bad one ? > There will be many things that make template processors better or worse than > others. But as demonstrated above, state-changing side-effects are firmly in > the bad category. > And as for good, it's best if you try to make your String templates "pure > functions" whenever possible. The rule can be a little flexible (for example, > you may have a counter that counts the number of times a Template processor was > called), but you should try to stick to this unless absolutely necessary. > And since the only real benefit that your logger gives us is removing one line > of code, that would definitely not be necessary. > LOGGER."\{validate(a)}-\{validate(b)}-\{validate(c)}" > Versus doing this. > final String output = contextSpecificValidations(SOME_STR."\{a}-\{b}-\{c}"); > actualLogger.info(output); > You save one line of code and lose out on all of the flexibility. Does it make > sense why the LOGGER Template Processor is problematic now? > Thank you for your time! > David Alayachew > On Sun, Jul 23, 2023 at 4:09 PM < [ mailto:forax at univ-mlv.fr | forax at univ-mlv.fr > ] > wrote: >> ----- Original Message ----- >>> From: "Brian Goetz" < [ mailto:brian.goetz at oracle.com | brian.goetz at oracle.com ] >> > > >>> To: "Remi Forax" < [ mailto:forax at univ-mlv.fr | forax at univ-mlv.fr ] >, >> > "amber-dev" < [ mailto:amber-dev at openjdk.org | amber-dev at openjdk.org ] > >> > Sent: Sunday, July 23, 2023 3:12:22 AM >> > Subject: Re: Presentation at JCrete.org >> >> So I don't believe a logger based on a template processor makes sense at least >> >> not until StringTemplate.Processor.Linkage becomes non-sealed. >> > I'll take this further: I don't think a logger based on a template >> > processor makes sense *at all*. >> May i ask why ? >> A logger process interpolated strings, combined with the new shiny syntax of the >> template processor, make sense to me. >> And this is a slipery slope, if we have good template processor and bad template >> processor, how to recognize a good template processor from a bad one ? >> R?mi -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Mon Jul 24 13:38:43 2023 From: brian.goetz at oracle.com (Brian Goetz) Date: Mon, 24 Jul 2023 09:38:43 -0400 Subject: Presentation at JCrete.org In-Reply-To: References: <1767623611.101388881.1689083138947.JavaMail.zimbra@univ-eiffel.fr> <1008f7d5-2069-f137-2d80-02022635dab4@oracle.com> <603628355.2334455.1690142907127.JavaMail.zimbra@univ-eiffel.fr> Message-ID: > This does not mean I think having side effects to string interpolation > is a good idea, and it also doesn?t mean that you can?t get this same > safety with side effect free interpolation. If the logger?s log > methods have an overload for a `Templated` object that would be > returned by the `LOG.?...?` interpolation, one could just not template > expand any `Strings` passed to the logger. > > Having `logger.info(LOG.?value: \{value}?)` is a bit more unwieldly to > some than just `LOG.?value: \{value}?`, so I think it?s gonna be > inevitable that people are gonna add sideeffects somewhere to > interpolations. > > I think you've laid it out here: you acknowledge that having LOG."stuff" *do* the logging is a bad idea, when there's an entirely sensible alternative that gets you the desired safety and deferral of expensive formatting: ??? logger.info(LOGMSG."...") This is a responsible API design that meets the goals of its use case.? The real problem, as you've put it, is programmers that put concision above clarity: > Having `logger.info(LOG.?value: \{value}?)` is a bit more unwieldly to > some than just `LOG.?value: \{value}?`, Yes, bad programmers will say "the right way is a bit more unwieldy, so of course I will do it the other way".? Bad programmers will design bad APIs and misuse good ones all in the name of saving a few characters (or a few cycles when it doesn't matter.)? Let's just call it what it is, and try to set a better example. On 7/24/2023 8:26 AM, forax at univ-mlv.fr wrote: > > The problem of logger.info(LOG.?value: \{value}?) is that the logger > receive a string, not a string template No, not necessarily.? LOG can be a (purely functional) TemplateProcessor, which defers rendering the string until it is actually logged: ??? logger.info(LogMsg msg) Then you have a free choice over where to put the validation (in LOG, or in the logger.) -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Mon Jul 24 14:33:19 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Mon, 24 Jul 2023 10:33:19 -0400 Subject: Presentation at JCrete.org In-Reply-To: References: <1767623611.101388881.1689083138947.JavaMail.zimbra@univ-eiffel.fr> <1008f7d5-2069-f137-2d80-02022635dab4@oracle.com> <603628355.2334455.1690142907127.JavaMail.zimbra@univ-eiffel.fr> Message-ID: (looks like Brian beat me to the punch, but I'll respond anyways) Hello R?mi, Thank you for your response! > A call to a template processor is a method call > (processor) to a Java object (StringTemplate.Processor). > If we wanted to not have side effect, instead of using a > OOP design we will have used a functional design, in this > case a function call with an API closer to a varargs call > (similar to what JavaScript does). Who says that OOP demands side-effects? You can write pure functions using OOP code. The reason why there is a Java Object is to dilineate one template processor from the other. It's to separate them. But all that does is make an instance for each processor. No one said that the instances themselves must have side-effect causing methods. That's false equivalence. OOP does not imply nor facilitate side-effects any more than functional code. You mentioned JavaScript. We both know how dangerously easy it is to perform side-effectual changes using functional code in JavaScript. Being OOP or Functional has nothing to do with it, and it's false equivalence to claim otherwise. Side-effects are facilitated by the LANGUAGE, not the STYLE of the language or the code being written. That's why side-effects are so much harder to create in something like Haskell as opposed to JavaScript -- one attaches a screaming siren to every side-effectual change you make, and the other gives you little to no indication that you've done so. Each template processor has its own template, which doesn't change. All it does is take in the arguments, interpolates them either as pre-configured, or as the developer configured it to, then returns the specified output. And since all of the input arguments are just there to construct a larger object, no side-effects are necessary! It's not until you start trying to execute log statements that side-effects enter, and that's what I am taking issue with. Ultimately, a function is a function. Whatever objects it allocates in its body are put up for garbage collection at the end of it, as long as it is a pure function. And that's my point - each instance of the template processor should strive to only have pure functions. As I said, some flexibility is understandable in cases of need, but your Logger template goes wildly out of bounds from that need. > I believe you have missunderstood how the validation > works, there is a separation between the validation of > the structure and the escaping of the values. > The validation of the structure can be done once while > the escaping has to be done for each call. After reading Robbe's comment (and rereading mine), I realize it was a mistake to use log4j as an example. I was trying to speak way more generally than everyone else interpreted it to be. I would encourage you to reread my response to Robbe to better understand what I truly meant. But in short, my point was, each use site has different validation needs. And to use the correct terminology here, I mean validating that the value created after escaping is good and safe. If you make a template processor that not only creates the output from the given inputs, but also uses that output to perform a state-changing side-effectual action, then you make it harder, not easier, to validate that the output generated in the first place was correct. And since one of the biggest points of this feature is to make safe-interpolation easy, your example flies in the face of what this JEP is trying to do. Does that make sense why I am taking so much issue with your idea? > For a logger, the validation depends on... Since I did a poor job with my log4j example, this entire paragraph addresses something completely orthogonal to my point. I understood from the beginning how validation and escaping work for Template processors. However, I picked a poor example with a very unfortunate coincidence. > The problem of the template processor is not the syntax. > It's not even the generated bytecode by javac. It's the > implementation of the bootstrap method (see > java.lang.runtime.TemplateRuntime) that > > - box all the values into a StringTemplate, storing all > instances as Object in an array and all primitives as > long in an another array, dropping the types nicely > propagated by the compiler. > > So all calls using the themplate processor API are slow > because of the boxing (the escape analysis is not able > to recover this transformation if you take a look to > the generated assembly code). Maybe all of this is true, but this is entirely separate from my point. My point is about validation. > - provides no way to do a different validation per > callsite, so the validation has to done for each call > (remember validation != escaping). > > There is a better API than the template processor API, > FMT already uses it (StringTemplate.Processor.Linkage), > but all other template processors can not use it. Again, I understand how the validation and escaping works. As for your point here, I feel like you are trying to turn this feature into something that it is not, and absolutely should not be. It's fine to criticize the performance and the way that the template processor handles its internals. But those reasons are not the biggest reason why a Logger template would be a bad idea. The biggest reason is because of the side-effects that you are bringing front and center. OOP has nothing to do with it. You are turning something that can and should be a pure functional call into some state-modifying and side-effectual, and that is the core problem with your logger template. That is the entire point I was trying to make. Thank you for your time! David Alayachew -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Mon Jul 24 15:08:55 2023 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Mon, 24 Jul 2023 17:08:55 +0200 (CEST) Subject: Presentation at JCrete.org In-Reply-To: References: <1767623611.101388881.1689083138947.JavaMail.zimbra@univ-eiffel.fr> <1008f7d5-2069-f137-2d80-02022635dab4@oracle.com> <603628355.2334455.1690142907127.JavaMail.zimbra@univ-eiffel.fr> Message-ID: <1988255336.2809135.1690211335887.JavaMail.zimbra@univ-eiffel.fr> > From: "Brian Goetz" > To: "Robbe Pincket" , "David Alayachew" > , "Remi Forax" > Cc: "amber-dev" > Sent: Monday, July 24, 2023 3:38:43 PM > Subject: Re: Presentation at JCrete.org > On 7/24/2023 8:26 AM, [ mailto:forax at univ-mlv.fr | forax at univ-mlv.fr ] wrote: >> The problem of logger.info(LOG.?value: \{value}?) is that the logger receive a >> string, not a string template > No, not necessarily. LOG can be a (purely functional) TemplateProcessor, > which defers rendering the string until it is actually logged: > logger.info(LogMsg msg) > Then you have a free choice over where to put the validation (in LOG, or in the > logger.) If the validation is in LOG, it means you can bypass the validation. And If the validation is in logger, then LogMsg really looks like the same abstraction as StringTemplate. hence logger.info(). ?value: \{value}?; R?mi -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Mon Jul 24 15:14:16 2023 From: brian.goetz at oracle.com (Brian Goetz) Date: Mon, 24 Jul 2023 11:14:16 -0400 Subject: Presentation at JCrete.org In-Reply-To: <1988255336.2809135.1690211335887.JavaMail.zimbra@univ-eiffel.fr> References: <1767623611.101388881.1689083138947.JavaMail.zimbra@univ-eiffel.fr> <1008f7d5-2069-f137-2d80-02022635dab4@oracle.com> <603628355.2334455.1690142907127.JavaMail.zimbra@univ-eiffel.fr> <1988255336.2809135.1690211335887.JavaMail.zimbra@univ-eiffel.fr> Message-ID: > ??? logger.info(LogMsg msg) > > Then you have a free choice over where to put the validation (in > LOG, or in the logger.) > > > If the validation is in LOG, it means you can bypass the validation. No it doesn't; you can control (e.g. via sealing) the creation of LogMsg.? Of course, you can write an API that lets people bypass validation, but I'm assuming we're not doing deliberately silly things. > And If the validation is in logger, then LogMsg really looks like the > same abstraction as StringTemplate. It depends what you actually want to do.? LogMsg could incorporate localization, automatic timestamping, etc.? But yes, if all you want is deferral, then ??? log.info(RAW."...") will get you deferral without writing a processor. -------------- next part -------------- An HTML attachment was scrubbed... URL: From steve at ethx.net Mon Jul 24 15:19:07 2023 From: steve at ethx.net (Steve Barham) Date: Mon, 24 Jul 2023 16:19:07 +0100 Subject: Presentation at JCrete.org In-Reply-To: References: Message-ID: <8A048EBA-1B43-438D-9D82-842F42FCA984@ethx.net> > On 24 Jul 2023, at 14:41, Brian Goetz wrote: > > I think you've laid it out here: you acknowledge that having LOG."stuff" *do* the logging is a bad idea, when there's an entirely sensible alternative that gets you the desired safety and deferral of expensive formatting: One observation I?d make here is that the JEP presents an example in the form of a QueryBuilder template processor to produce a PreparedStatement. Per the Javadoc of Connection, this may well result in a side-effect by sending the query to the server for precompilation / validation. I think it?s worth having a consistent story to people when presenting the feature - in the JEP example, it would feel a little unwieldy for QueryBuilder to return its own thunky DeferredPreparedStatement, or similar, for the caller to work with. Cheers, Steve From davidalayachew at gmail.com Mon Jul 24 15:20:40 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Mon, 24 Jul 2023 11:20:40 -0400 Subject: Presentation at JCrete.org In-Reply-To: <8A048EBA-1B43-438D-9D82-842F42FCA984@ethx.net> References: <8A048EBA-1B43-438D-9D82-842F42FCA984@ethx.net> Message-ID: Definitely agreed. It was actually brought up before, both here and on reddit. I feel like the example in the JEP contributed more than anything to the confusion. On Mon, Jul 24, 2023 at 11:19?AM Steve Barham wrote: > > On 24 Jul 2023, at 14:41, Brian Goetz wrote: > > > > I think you've laid it out here: you acknowledge that having LOG."stuff" > *do* the logging is a bad idea, when there's an entirely sensible > alternative that gets you the desired safety and deferral of expensive > formatting: > > One observation I?d make here is that the JEP presents an example in the > form of a QueryBuilder template processor to produce a PreparedStatement. > Per the Javadoc of Connection, this may well result in a side-effect by > sending the query to the server for precompilation / validation. > > I think it?s worth having a consistent story to people when presenting the > feature - in the JEP example, it would feel a little unwieldy for > QueryBuilder to return its own thunky DeferredPreparedStatement, or > similar, for the caller to work with. > > Cheers, > > Steve > > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Mon Jul 24 15:31:33 2023 From: brian.goetz at oracle.com (Brian Goetz) Date: Mon, 24 Jul 2023 11:31:33 -0400 Subject: Presentation at JCrete.org In-Reply-To: <8A048EBA-1B43-438D-9D82-842F42FCA984@ethx.net> References: <8A048EBA-1B43-438D-9D82-842F42FCA984@ethx.net> Message-ID: <7f1d1452-7ecb-65f2-a3d1-47409f5a0162@oracle.com> > One observation I?d make here is that the JEP presents an example in the form of a QueryBuilder template processor to produce a PreparedStatement. Per the Javadoc of Connection, this may well result in a side-effect by sending the query to the server for precompilation / validation. > > I think it?s worth having a consistent story to people when presenting the feature - in the JEP example, it would feel a little unwieldy for QueryBuilder to return its own thunky DeferredPreparedStatement, or similar, for the caller to work with. I'm kind of surprised no one has pointed this out yet :) The naive interpretation here is "Wait, on one hand you say side effects are bad, on the other you given an example with side effects.? Inconsistant!"? But as always, the reality is more subtle. The spirit here is that the template processor is a *function*, taking some template-shaped ingredients and turning it into a useful *thing*, that you can then further use or manipulate.? It should have the effect of a factory, whether producing a String or a JsonDocument or a ResultSet.? The latter is interesting in that the factory has to make a connection to produce the result set, but what the processor is doing is *making a result set*.?? A constructor or "factory" that produced nothing and operated primarily through side-effects would rightly be declared to be a poor API. The ResultSet example is fine, because it's not confusing what is going on -- I describe my query with a template, and I get back a ResultSet, which is then my responsibility to iterate or not, and close when done.? It is a factory for result sets.? Whereas the LOG example is not, because it it is intrinsically confusing.? "Derive an X from a Y" is behavior one can generally intuit purely from the type Function (and knowledge of what X and Y are).? "Do an arbitrary side effect" is not.? If you want to do arbitrary side effects, write methods whose names clearly describe the behavior of the method. (I think everyone pretty much knows this, but then gets distracted by "but performance" or "but its more characters".? Again, we can't save people from themselves, but we can set good examples.) What this whole thread reinforces for me is that we need to have a document (like "Programmers Guide to Text Blocks") that spells out the principles.? In the meantime, hopefully my words shall suffice :) From davidalayachew at gmail.com Mon Jul 24 15:49:50 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Mon, 24 Jul 2023 11:49:50 -0400 Subject: Presentation at JCrete.org In-Reply-To: <7f1d1452-7ecb-65f2-a3d1-47409f5a0162@oracle.com> References: <8A048EBA-1B43-438D-9D82-842F42FCA984@ethx.net> <7f1d1452-7ecb-65f2-a3d1-47409f5a0162@oracle.com> Message-ID: Hello Brian, Thanks for the clarification, that's helpful! > I'm kind of surprised no one has pointed this out yet :) Maybe not this thread specifically, but a LOT of people have been confused about this. Multiple times on amber-dev even, let alone reddit (before the whole spez thing). A lot of people (me included) pretty stopped asking about that example because we never got any definitive responses. Up until now, I haven't seen a definitive answer as to why that example was left in. This definitely suffices though. > The spirit here is that the template processor is a > *function*, taking some template-shaped ingredients and > turning it into a useful *thing*, that you can then > further use or manipulate. It should have the effect of > a factory, whether producing a String or a JsonDocument > or a ResultSet. The latter is interesting in that the > factory has to make a connection to produce the result > set, but what the processor is doing is *making a result > set*. A constructor or "factory" that produced nothing > and operated primarily through side-effects would rightly > be declared to be a poor API. Ok, fair enough. I think the missing detail though is that, you need to understand JDBC in order for that to answer follow up questions. I am not very familiar with JDBC. So when I see a template processor called DB, my first thought is that I can do DB stuff, including updates and deletes. I was not aware that a ResultSet is more or less the output of a SELECT. By all means, after doing a decent amount of reading, I understand that now, but a lot of us never touch JDBC since we spend all our time in Hibernate/JPA, so this example probably flew over a lot of heads. I see the nuance in the example now, but I guarantee I am not the only one who got lost in that example and thought that it was supporting side-effects *because I saw DB*. > What this whole thread reinforces for me is that we need > to have a document (like "Programmers Guide to Text > Blocks") that spells out the principles. In the > meantime, hopefully my words shall suffice :) 100%, no question. I would have definitely thought that the JDBC example is bad, so spelling out what is or isn't useful behaviour would behoove everyone. Thank you for the insight! David Alayachew On Mon, Jul 24, 2023 at 11:31?AM Brian Goetz wrote: > > > > One observation I?d make here is that the JEP presents an example in the > form of a QueryBuilder template processor to produce a PreparedStatement. > Per the Javadoc of Connection, this may well result in a side-effect by > sending the query to the server for precompilation / validation. > > > > I think it?s worth having a consistent story to people when presenting > the feature - in the JEP example, it would feel a little unwieldy for > QueryBuilder to return its own thunky DeferredPreparedStatement, or > similar, for the caller to work with. > > > I'm kind of surprised no one has pointed this out yet :) > > The naive interpretation here is "Wait, on one hand you say side effects > are bad, on the other you given an example with side effects. > Inconsistant!" But as always, the reality is more subtle. > > The spirit here is that the template processor is a *function*, taking > some template-shaped ingredients and turning it into a useful *thing*, > that you can then further use or manipulate. It should have the effect > of a factory, whether producing a String or a JsonDocument or a > ResultSet. The latter is interesting in that the factory has to make a > connection to produce the result set, but what the processor is doing is > *making a result set*. A constructor or "factory" that produced > nothing and operated primarily through side-effects would rightly be > declared to be a poor API. > > The ResultSet example is fine, because it's not confusing what is going > on -- I describe my query with a template, and I get back a ResultSet, > which is then my responsibility to iterate or not, and close when done. > It is a factory for result sets. Whereas the LOG example is not, > because it it is intrinsically confusing. "Derive an X from a Y" is > behavior one can generally intuit purely from the type Function > (and knowledge of what X and Y are). "Do an arbitrary side effect" is > not. If you want to do arbitrary side effects, write methods whose > names clearly describe the behavior of the method. > > (I think everyone pretty much knows this, but then gets distracted by > "but performance" or "but its more characters". Again, we can't save > people from themselves, but we can set good examples.) > > What this whole thread reinforces for me is that we need to have a > document (like "Programmers Guide to Text Blocks") that spells out the > principles. In the meantime, hopefully my words shall suffice :) > -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Mon Jul 24 15:50:28 2023 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Mon, 24 Jul 2023 17:50:28 +0200 (CEST) Subject: Presentation at JCrete.org In-Reply-To: References: <1767623611.101388881.1689083138947.JavaMail.zimbra@univ-eiffel.fr> <1008f7d5-2069-f137-2d80-02022635dab4@oracle.com> <603628355.2334455.1690142907127.JavaMail.zimbra@univ-eiffel.fr> Message-ID: <717967696.2951765.1690213828242.JavaMail.zimbra@univ-eiffel.fr> > From: "David Alayachew" > To: "Brian Goetz" > Cc: "Robbe Pincket" , "Remi Forax" , > "amber-dev" > Sent: Monday, July 24, 2023 4:33:19 PM > Subject: Re: Presentation at JCrete.org > (looks like Brian beat me to the punch, but I'll respond anyways) > Hello R?mi, Hello, > Thank you for your response! > > A call to a template processor is a method call > > (processor) to a Java object (StringTemplate.Processor). > > If we wanted to not have side effect, instead of using a > > OOP design we will have used a functional design, in this > > case a function call with an API closer to a varargs call > > (similar to what JavaScript does). > Who says that OOP demands side-effects? I said OOP allows side effects. In fact, one nice feature of OOP is to be able to distangle the different side effects on an application, i.e to have side effects but in a controlled/organized way. I never said that OOP demands side-effects. [...] > Each template processor has its own template, which doesn't change. All it does > is take in the arguments, interpolates them either as pre-configured, or as the > developer configured it to, then returns the specified output. And since all of > the input arguments are just there to construct a larger object, no > side-effects are necessary! It's not until you start trying to execute log > statements that side-effects enter, and that's what I am taking issue with. I respectfully disagree, a template processor is an instance of a class, so values comes from the class itself, the template fragments and the template values. Some template processors will be immutable but some will not. [...] > > I believe you have missunderstood how the validation > > works, there is a separation between the validation of > > the structure and the escaping of the values. > > The validation of the structure can be done once while > > the escaping has to be done for each call. > After reading Robbe's comment (and rereading mine), I realize it was a mistake > to use log4j as an example. I was trying to speak way more generally than > everyone else interpreted it to be. I would encourage you to reread my response > to Robbe to better understand what I truly meant. > But in short, my point was, each use site has different validation needs. And to > use the correct terminology here, I mean validating that the value created > after escaping is good and safe. If you make a template processor that not only > creates the output from the given inputs, but also uses that output to perform > a state-changing side-effectual action, then you make it harder, not easier, to > validate that the output generated in the first place was correct. And since > one of the biggest points of this feature is to make safe-interpolation easy, > your example flies in the face of what this JEP is trying to do. Does that make > sense why I am taking so much issue with your idea? Let's take another example, a builder. I see no problem to have a template processor that acts as StringBuilder/StringJoiner. var joiner = new StringJoinerAndTemplateProcessor(); for(...) { joiner.""" several lines and \{ holes } """; } return joiner.toString(); I do not see why I can not to that. [...] > It's fine to criticize the performance and the way that the template processor > handles its internals. But those reasons are not the biggest reason why a > Logger template would be a bad idea. The biggest reason is because of the > side-effects that you are bringing front and center. OOP has nothing to do with > it. You are turning something that can and should be a pure functional call > into some state-modifying and side-effectual, and that is the core problem with > your logger template. That is the entire point I was trying to make. Designing is about trying to separate different concerns. Logging something has to return void / do side effects, because logging is a separate concern from the goal of the method which contains the logging statement. Otherwise it means polutting the method signature with logging metadata like the IO monad in Haskell. Having a template processor that returns Void is fine, exactly like having a template processor that throws an exception is fine (the API even allow checked exceptions). > Thank you for your time! > David Alayachew R?mi -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Mon Jul 24 16:02:16 2023 From: brian.goetz at oracle.com (Brian Goetz) Date: Mon, 24 Jul 2023 12:02:16 -0400 Subject: Presentation at JCrete.org In-Reply-To: <717967696.2951765.1690213828242.JavaMail.zimbra@univ-eiffel.fr> References: <1767623611.101388881.1689083138947.JavaMail.zimbra@univ-eiffel.fr> <1008f7d5-2069-f137-2d80-02022635dab4@oracle.com> <603628355.2334455.1690142907127.JavaMail.zimbra@univ-eiffel.fr> <717967696.2951765.1690213828242.JavaMail.zimbra@univ-eiffel.fr> Message-ID: <5cc3cf33-b0c2-93d8-b028-d7661bbb0ca1@oracle.com> > > Let's take another example, a builder. I see no problem to have a > template processor that acts as StringBuilder/StringJoiner. > > var joiner = new StringJoinerAndTemplateProcessor(); > for(...) { > ? joiner.""" > ?? several lines and \{ holes } > ?? """; > } > return joiner.toString(); > > I do not see why I can not to that. I am sorry to hear this.? This is a needlessly-bad design. The invocation of ."..." has an effect, but there is no verb visible.? What does it do?? (I am not looking for an answer; the fact that I have to ask makes this a terrible API design.) Whereas, when TemplateProcessor behaves more like Function, it is clear what it does; takes an X, and gives me a Y.? (It is then on me to handle the Y properly.) > > Designing is about trying to separate different concerns. And having LOG."..." do both the formatting and the logging side-effects is cramming two separate concerns into one. I think it is probably time to end this conversation here, though; further arguing about "I wish the design center of this feature were different" doesn't seem counterproductive, and it seems you are pretty much there. -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Mon Jul 24 16:14:59 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Mon, 24 Jul 2023 12:14:59 -0400 Subject: Presentation at JCrete.org In-Reply-To: <717967696.2951765.1690213828242.JavaMail.zimbra@univ-eiffel.fr> References: <1767623611.101388881.1689083138947.JavaMail.zimbra@univ-eiffel.fr> <1008f7d5-2069-f137-2d80-02022635dab4@oracle.com> <603628355.2334455.1690142907127.JavaMail.zimbra@univ-eiffel.fr> <717967696.2951765.1690213828242.JavaMail.zimbra@univ-eiffel.fr> Message-ID: Hello R?mi, Thank you for your response! >Having a template processor that returns Void is fine. The entire point of the Template Processor is to produce something. That's even more fundamental to its goal than the point about safety. You are trying to turn this into side-effect machine because you can, and I'm telling you that that is a bad use of the tool because of all the reasons I've mentioned above. > Let's take another example, a builder. I see no problem > to have a template processor that acts as > StringBuilder/StringJoiner. > > var joiner = new StringJoinerAndTemplateProcessor(); > for(...) { > joiner.""" > several lines and \{ holes } > """; > } > return joiner.toString(); > > I do not see why I can not to that. This is so much worse than the logger template, R?mi. At least with the logger one, it was only side-effects. Now you want to add state that the processor takes with it. And the only benefit you get is that you get to save a few key strokes per append(). There are so many ways around this without having to depend on carrying state between calls. This feels like to me that you want to make all sorts of Template Processors that do what existing libraries do, but as a template processor. The entire purpose of this api is supposed to be for interpolation, and I think Brian put it even better by thinking of it as a factory. Thank you for your time! David Alayachew On Mon, Jul 24, 2023 at 11:50?AM wrote: > > > ------------------------------ > > *From: *"David Alayachew" > *To: *"Brian Goetz" > *Cc: *"Robbe Pincket" , "Remi Forax" < > forax at univ-mlv.fr>, "amber-dev" > *Sent: *Monday, July 24, 2023 4:33:19 PM > *Subject: *Re: Presentation at JCrete.org > > (looks like Brian beat me to the punch, but I'll respond anyways) > > Hello R?mi, > > > Hello, > > > > Thank you for your response! > > > A call to a template processor is a method call > > (processor) to a Java object (StringTemplate.Processor). > > If we wanted to not have side effect, instead of using a > > OOP design we will have used a functional design, in this > > case a function call with an API closer to a varargs call > > (similar to what JavaScript does). > > Who says that OOP demands side-effects? > > > I said OOP allows side effects. > In fact, one nice feature of OOP is to be able to distangle the different > side effects on an application, i.e to have side effects but in a > controlled/organized way. > I never said that OOP demands side-effects. > > [...] > > > > Each template processor has its own template, which doesn't change. All it > does is take in the arguments, interpolates them either as pre-configured, > or as the developer configured it to, then returns the specified output. > And since all of the input arguments are just there to construct a larger > object, no side-effects are necessary! It's not until you start trying to > execute log statements that side-effects enter, and that's what I am taking > issue with. > > > I respectfully disagree, a template processor is an instance of a class, > so values comes from the class itself, the template fragments and the > template values. > Some template processors will be immutable but some will not. > > [...] > > > > > I believe you have missunderstood how the validation > > works, there is a separation between the validation of > > the structure and the escaping of the values. > > The validation of the structure can be done once while > > the escaping has to be done for each call. > > After reading Robbe's comment (and rereading mine), I realize it was a > mistake to use log4j as an example. I was trying to speak way more > generally than everyone else interpreted it to be. I would encourage you to > reread my response to Robbe to better understand what I truly meant. > > But in short, my point was, each use site has different validation needs. > And to use the correct terminology here, I mean validating that the value > created after escaping is good and safe. If you make a template processor > that not only creates the output from the given inputs, but also uses that > output to perform a state-changing side-effectual action, then you make it > harder, not easier, to validate that the output generated in the first > place was correct. And since one of the biggest points of this feature is > to make safe-interpolation easy, your example flies in the face of what > this JEP is trying to do. Does that make sense why I am taking so much > issue with your idea? > > > Let's take another example, a builder. I see no problem to have a template > processor that acts as StringBuilder/StringJoiner. > > var joiner = new StringJoinerAndTemplateProcessor(); > for(...) { > joiner.""" > several lines and \{ holes } > """; > } > return joiner.toString(); > > I do not see why I can not to that. > > [...] > > > > It's fine to criticize the performance and the way that the template > processor handles its internals. But those reasons are not the biggest > reason why a Logger template would be a bad idea. The biggest reason is > because of the side-effects that you are bringing front and center. OOP has > nothing to do with it. You are turning something that can and should be a > pure functional call into some state-modifying and side-effectual, and that > is the core problem with your logger template. That is the entire point I > was trying to make. > > > Designing is about trying to separate different concerns. > Logging something has to return void / do side effects, because logging is > a separate concern from the goal of the method which contains the logging > statement. > Otherwise it means polutting the method signature with logging metadata > like the IO monad in Haskell. > > Having a template processor that returns Void is fine, exactly like having > a template processor that throws an exception is fine (the API even allow > checked exceptions). > > > > Thank you for your time! > David Alayachew > > > R?mi > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Mon Jul 24 17:02:21 2023 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Mon, 24 Jul 2023 19:02:21 +0200 (CEST) Subject: Presentation at JCrete.org In-Reply-To: <5cc3cf33-b0c2-93d8-b028-d7661bbb0ca1@oracle.com> References: <1767623611.101388881.1689083138947.JavaMail.zimbra@univ-eiffel.fr> <603628355.2334455.1690142907127.JavaMail.zimbra@univ-eiffel.fr> <717967696.2951765.1690213828242.JavaMail.zimbra@univ-eiffel.fr> <5cc3cf33-b0c2-93d8-b028-d7661bbb0ca1@oracle.com> Message-ID: <1540635837.3048151.1690218141048.JavaMail.zimbra@univ-eiffel.fr> > From: "Brian Goetz" > To: "Remi Forax" , "David Alayachew" > > Cc: "Robbe Pincket" , "amber-dev" > Sent: Monday, July 24, 2023 6:02:16 PM > Subject: Re: Presentation at JCrete.org >> Let's take another example, a builder. I see no problem to have a template >> processor that acts as StringBuilder/StringJoiner. >> var joiner = new StringJoinerAndTemplateProcessor(); >> for(...) { >> joiner.""" >> several lines and \{ holes } >> """; >> } >> return joiner.toString(); >> I do not see why I can not to that. > I am sorry to hear this. This is a needlessly-bad design. > The invocation of ."..." has an effect, but there is no verb visible. What does > it do? (I am not looking for an answer; the fact that I have to ask makes this > a terrible API design.) I know that you are playing the devil advocate here but given that the variable is called joiner, you may think, occasionnally, that a joiner may join. Anyway, you convince me one year ago that using an interface instead of a function call was a better idea in term of syntax, but now you are saying that the semantics is not the usual semantics on a method call on an interface. I think i've the right to be confused. > Whereas, when TemplateProcessor behaves more like Function, it is clear what it > does; takes an X, and gives me a Y. (It is then on me to handle the Y > properly.) Some template processors are like a Function, like RAW, but for example, FMT is an instance of a real class, FormatProcessor. [...] > I think it is probably time to end this conversation here, though; further > arguing about "I wish the design center of this feature were different" doesn't > seem counterproductive, and it seems you are pretty much there. I do not whish the design center of this feature to be different*. I'm trying to figure out why you think that having a template processor that do side effects is a bad idea. R?mi * I may think that the runtime implementation can be simplified by removing j.l.r.Carriers and friends, and make Processor.Linkage open. -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Mon Jul 24 17:14:45 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Mon, 24 Jul 2023 13:14:45 -0400 Subject: Presentation at JCrete.org In-Reply-To: <1540635837.3048151.1690218141048.JavaMail.zimbra@univ-eiffel.fr> References: <1767623611.101388881.1689083138947.JavaMail.zimbra@univ-eiffel.fr> <603628355.2334455.1690142907127.JavaMail.zimbra@univ-eiffel.fr> <717967696.2951765.1690213828242.JavaMail.zimbra@univ-eiffel.fr> <5cc3cf33-b0c2-93d8-b028-d7661bbb0ca1@oracle.com> <1540635837.3048151.1690218141048.JavaMail.zimbra@univ-eiffel.fr> Message-ID: I'm sending this message to notify that I'm continuing this thread off of the amber-dev list. If anyone wants to be included/excluded, please reach out individually to me. And please DO NOT CC OR RESPOND TO AMBER DEV. On Mon, Jul 24, 2023 at 1:02?PM wrote: > > > ------------------------------ > > *From: *"Brian Goetz" > *To: *"Remi Forax" , "David Alayachew" < > davidalayachew at gmail.com> > *Cc: *"Robbe Pincket" , "amber-dev" < > amber-dev at openjdk.org> > *Sent: *Monday, July 24, 2023 6:02:16 PM > *Subject: *Re: Presentation at JCrete.org > > > > Let's take another example, a builder. I see no problem to have a template > processor that acts as StringBuilder/StringJoiner. > > var joiner = new StringJoinerAndTemplateProcessor(); > for(...) { > joiner.""" > several lines and \{ holes } > """; > } > return joiner.toString(); > > I do not see why I can not to that. > > > I am sorry to hear this. This is a needlessly-bad design. > > The invocation of ."..." has an effect, but there is no verb visible. > What does it do? (I am not looking for an answer; the fact that I have to > ask makes this a terrible API design.) > > > I know that you are playing the devil advocate here but given that the > variable is called joiner, you may think, occasionnally, that a joiner may > join. > > Anyway, you convince me one year ago that using an interface instead of a > function call was a better idea in term of syntax, but now you are saying > that the semantics is not the usual semantics on a method call on an > interface. I think i've the right to be confused. > > > > > Whereas, when TemplateProcessor behaves more like Function, it is clear > what it does; takes an X, and gives me a Y. (It is then on me to handle > the Y properly.) > > > Some template processors are like a Function, like RAW, but for example, > FMT is an instance of a real class, FormatProcessor. > > [...] > > > > > I think it is probably time to end this conversation here, though; further > arguing about "I wish the design center of this feature were different" > doesn't seem counterproductive, and it seems you are pretty much there. > > > I do not whish the design center of this feature to be different*. > I'm trying to figure out why you think that having a template processor > that do side effects is a bad idea. > > R?mi > > * I may think that the runtime implementation can be simplified by > removing j.l.r.Carriers and friends, and make Processor.Linkage open. > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From amaembo at gmail.com Tue Jul 25 08:42:37 2023 From: amaembo at gmail.com (Tagir Valeev) Date: Tue, 25 Jul 2023 10:42:37 +0200 Subject: [string-templates] Javac accepts char literal as template Message-ID: Hello! Code: public class Demo { public static void main(String[] args) { System.out.println(STR.'\{1+2}'); } } Latest javac (build 22-ea+7-489) compiles this and produces 3 as an output. I believe this is a mistake in the compiler. With best regards, Tagir Valeev From james.laskey at oracle.com Tue Jul 25 09:29:09 2023 From: james.laskey at oracle.com (Jim Laskey) Date: Tue, 25 Jul 2023 09:29:09 +0000 Subject: [string-templates] Javac accepts char literal as template In-Reply-To: References: Message-ID: It is indeed a bug. Apologies. I?m very surprised I missed that. I will file a bug against this. ? > On Jul 25, 2023, at 5:43 AM, Tagir Valeev wrote: > > ?Hello! > > Code: > > public class Demo { > public static void main(String[] args) { > System.out.println(STR.'\{1+2}'); > } > } > > Latest javac (build 22-ea+7-489) compiles this and produces 3 as an > output. I believe this is a mistake in the compiler. > > With best regards, > Tagir Valeev From amaembo at gmail.com Tue Jul 25 09:30:47 2023 From: amaembo at gmail.com (Tagir Valeev) Date: Tue, 25 Jul 2023 11:30:47 +0200 Subject: [string-templates] Compiler crash when template processor type is a captured wildcard Message-ID: Code sample: import java.util.List; public class Demo { void x(List> list) { list.get(0).""; } } Output (build 22-ea+7-489): java: An exception has occurred in the compiler (22-ea). Please file a bug against the Java compiler via the Java bug reporting page (https://bugreport.java.com) after checking the Bug Database (https://bugs.java.com) for duplicates. Include your program, the following diagnostic, and the parameters passed to the Java compiler in your report. Thank you. java: java.lang.AssertionError: .process java: at jdk.compiler/com.sun.tools.javac.util.Assert.error(Assert.java:162) java: at jdk.compiler/com.sun.tools.javac.code.Symbol$MethodSymbol.(Symbol.java:1942) java: at jdk.compiler/com.sun.tools.javac.code.Symbol$MethodSymbol$1.(Symbol.java:1948) java: at jdk.compiler/com.sun.tools.javac.code.Symbol$MethodSymbol.clone(Symbol.java:1948) java: at jdk.compiler/com.sun.tools.javac.code.Symbol$MethodSymbol.clone(Symbol.java:1918) java: at jdk.compiler/com.sun.tools.javac.jvm.Gen.binaryQualifier(Gen.java:265) java: at jdk.compiler/com.sun.tools.javac.jvm.Gen.visitSelect(Gen.java:2388) java: at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCFieldAccess.accept(JCTree.java:2581) java: at jdk.compiler/com.sun.tools.javac.jvm.Gen.genExpr(Gen.java:885) java: at jdk.compiler/com.sun.tools.javac.jvm.Gen.visitApply(Gen.java:1921) With best regards, Tagir Valeev From bas.leijdekkers at jetbrains.com Wed Jul 12 11:27:19 2023 From: bas.leijdekkers at jetbrains.com (Bas Leijdekkers) Date: Wed, 12 Jul 2023 11:27:19 -0000 Subject: String Templates Expression Statement Message-ID: Hello Amber-dev, In the String Templates draft specification, I don't see any changes in JLS Chapter 14.8 Expression Statements. This makes me think that Template Expressions are not allowed to be used as statements. However javac in jdk21-ea accepts a Template Expression used as a statement without reporting any error. (I am using build 21-ea+30-2426). So it appears to me either the specification or javac is incorrect. Which one should I believe? Example code: ``` class TemplateExpressionStatement { public static void main(String[] args) { STR."\{}"; } } ``` Bas -------------- next part -------------- An HTML attachment was scrubbed... URL: From pyltsinm at gmail.com Sat Jul 8 14:20:15 2023 From: pyltsinm at gmail.com (Mikhail Pyltsin) Date: Sat, 08 Jul 2023 14:20:15 -0000 Subject: Using qualified enum in `old` switch Message-ID: Hi! I am investigating a new version of jep440+441 ( https://cr.openjdk.org/~gbierman/jep440%2B441/jep440+441-20230612/specs/patterns-switch-record-patterns-jls.html ) After `2023-06-12: Misc editorial changes.` I can't find any explicit mention that it is allowed to use qualified names for enums in `old` switch. But according to https://openjdk.org/jeps/441 it must be allowed ``` static void goodEnumSwitch2(Coin c) { switch (c) { case HEADS -> { System.out.println("Heads"); } case Coin.TAILS -> { // Unnecessary qualification but allowed System.out.println("Tails"); } } } ``` before `2023-06-12: Misc editorial changes.`, It was ```Every case constant must be either (1) the null literal, (2) a constant expression (15.29 ), or (3) the (simple or qualified) name of an enum constant (8.9.1 ); otherwise a compile-time error occurs. A single null case constant may also be paired with the default keyword. ```, but now there is no mention of the type of enum names. Could you help me, which point allows it now? -------------- next part -------------- An HTML attachment was scrubbed... URL: