From mcnepp02 at googlemail.com Thu Jun 30 07:33:42 2022 From: mcnepp02 at googlemail.com (Gernot Neppert) Date: Thu, 30 Jun 2022 09:33:42 +0200 Subject: Please don't restrict access to the companion-type! Message-ID: I've been following the valhalla-development for a very long time, and have also posted quite a few comments, some of them raising valid concerns, some of them proving my ignorance. This comment hopefully falls into the first category: My concern is that allowing access-restriction for a value-type's "companion-type" is a severe case of "throwing the baby out with the bathwater". Yes, I know what it is supposed to achieve: prevent users from accidentally creating zero-initialized values for value-types "with no resonable default". However, the proposed solution of hiding the companion-type will force programmers to use the reference-type even if they do not want to. Please have a look at the following class "Accumulator". It assumes that "Sample" is a value-class in the same package with a non-public companion-type. The Javadoc must now explicitly mention some pitfalls that would not be there if "Sample.val" were accessible. Especially the necessary precaution about the returned array-type is rather ugly, right?! public class Accumulator { private Sample.val samples; /** Yields the samples that were taken. Note: the returned array is actually a "flat" array! No element can be null. While processing this array, do not try to set any of its elements to null, as that may trigger an ArrayStoreException! */ public Sample[] samples() { return samples.clone(); } } To sum it up, my proposal is: 1. Make the companion-type public always. 2. When introducing value-classes, document the risks of having "uninitialized" values under very specific circumstances (uninitialized fields, flat arrays). 3. Let the compiler treat fields of companion-types like final fields today, i.e. enforce initialization. 4. The risk of still encountering uninitialized fields is really really low, and is, btw, absolutely not new. 4. Provide the users with a convenient API for creating arrays with all elements initialized to a specific value. 5. In Java, one could possibly also use this currently disallowed syntax for creating initialized arrays: new Sample.val[20] { Sample.of("Hello") }; -------------- next part -------------- An HTML attachment was scrubbed... URL: From jf.mend at gmail.com Thu Jun 30 22:02:17 2022 From: jf.mend at gmail.com (=?UTF-8?B?Sm/Do28gTWVuZG9uw6dh?=) Date: Thu, 30 Jun 2022 23:02:17 +0100 Subject: nullable-inlined-values on the heap Message-ID: Hello, Would it be possible to decomplect nullability from a variable's encoding-mode (reference or inline)? I have been looking at the C# spec on "nullable-value-types" and I wonder if the Java runtime could do something similar under the hood to allow nullable-inlined-values, even on the heap. I think that, compared to C#'s "value-types", Java can take advantage of the fact that its value-class instances are immutable, which means that pass-by-value or pass-by-reference is indistinguishable, which, with nullable-inlined-values, could mean that Java can have the variable encoding-mode completely encapsulated/hidden from the user-model as a runtime implementation detail. If this is possible, maybe Valhalla's Java could have a user-model like this: *** A decomplected user-model *** For class-authors: - *value-knob* to reject identity - Applicable on class declarations, indicates that the class instances don't require identity (a value-class). - *zero-knob* to indicate that the value-class has a zero-value - if a value-class does not have a zero-value, its instances won't be inlined in any shared-variables (?17.4.1.) since this is the only way for the language to ensure the non-existence of the zero-value. If the value-class is declared with a zero-value, then care must be taken when reading/writing constructors since *no constructor invariant can exclude the zero-value*. - *tearable-knob* to allow tearing - Applicable on zero value-class declarations with bitSize > 32 bits, may be used by the class-author to hand the class-user the responsibility of how to avoid tearing, freeing the runtime to always inline instances in shared-mutables (non-final shared-variables). Conversely, if this knob is not used, instances will be kept atomic, which allows the class-author to guarantee constructor invariants *provided they're not broken by the zero-value*, which may be useful for the class implementation and class-users to rely upon. For class-users: - *not-nullable-knob (!)* to exclude null from a variable's value-set - Applicable on any variable declarations. On nullable variables, the default value is null and, in either encoding-mode (reference or inline), the runtime is free to choose the encoding for the extra bit of information required to represent the null state. - *atomic-knob* to avoid tearing - Applicable on shared-mutable declarations, may be used to reverse the effect of the tearable-knob, thereby restoring atomicity. The encoding-mode of a variable is decided at runtime according to this ternary expression: var encodingMode = !valueClass(variable.type) ? REFERENCE // value-knob : tooBig(variable.type.bitSize) ? REFERENCE : !shared(variable) ? INLINE // (?17.4.1.) : !zeroValueClass(variable.type) ? REFERENCE // zero-knob : final(variable) ? INLINE : atomicWrite(variable.type.bitSize) ? INLINE : atomic(variable) ? REFERENCE // atomic-knob : tearableValueClass(variable.type) ? INLINE // tearable-knob : REFERENCE; The variable.type.bitSize depends on nullability as nullable types may require more space. The predicates tooBig and atomicWrite depend on the hardware. As an example, they could be: boolean tooBig(int bitSize) {return bitSize > 256;} boolean atomicWrite(int bitSize) {return bitSize <= 64;} Table-view of the user-model knobs: identity ? (identity) | value | zeroness ? (no-zero) | (no-zero) | zero | atomicity ? (atomic) | (atomic) | (atomic) | tearable | nullability ? (?) | ! | (?) | ! | (?) | ! | (?) | ! | ============================================================================================== encoding-mode ? reference | inline/reference | needs reference ? everywhere | shared-variables | no/shared-mutables | no | definite-assignment ? no | yes | no | yes | no | yes | yes | yes | default ? null | n.a. | null | n.a. | null | n.a. | n.a. | n.a. | init-default ? null | null | null | zero/null | null | zero/null | Notes: - tokens in parenthesis are the default when no knob is used - definite-assignment (?16.) means that the compiler enforces (to the best of its ability) variable initialization before usage - default is the default-value of a variable when not definitely-assigned - init-default is the default-value of a variable before any initialization code runs - on non-nullable zero value-classes, the init-default (zero or null) depends on the encoding-mode chosen by the runtime - on atomic zero value-classes, reference-encoding is needed on shared-mutables if instance bitSize cannot be written atomically *** Migration of value-based classes *** Requiring definite-assignment on all non-nullable shared-mutables is useful to get rid of missed-initialization-bugs, so I think it's a good idea to require it wherever source-compatibility allows. In this model, all value-based classes can be migrated to (atomic) zero value-classes. Due to definite-assignment, even if LocalDate is migrated to a zero value-class, it will be hard to get an accidental "Jan 1, 1970". Rational can also be a zero value-class but users will have to keep in mind that it's possible to get a zero-denominator Rational, even if the constructor throws when we try to build one. To maintain source-compatibility, no migrated value-based class can be tearable, not even Double or Long, since wherever in existing code we have a field declaration such as: ValueBasedClass v; v is always reference encoded and, therefore, atomic. For Double and Long, this is a bit of an anomaly, because it means that for these two primitives, and for them alone, each of these pair of field declarations will not be semantically equivalent: long v; // tearable Long! v; // atomic double d; // tearable Double! d; // atomic Jo?o Mendon?a -------------- next part -------------- An HTML attachment was scrubbed... URL: