Queries regarding value classes
Remi Forax
forax at univ-mlv.fr
Fri Mar 18 09:10:21 UTC 2022
----- Original Message -----
> From: "Julian Waters" <tanksherman27 at gmail.com>
> To: "Pedro Lamarão" <pedro.lamarao at prodist.com.br>
> Cc: "valhalla-dev" <valhalla-dev at openjdk.java.net>
> Sent: Friday, March 18, 2022 3:11:59 AM
> Subject: Re: Queries regarding value classes
> Hi Pedro,
>
> I think you misunderstood me, what I was proposing was for value class X,
> x.a = 3; would be equivalent to new X(3); internally (*A la* syntactic
> sugar that could be disabled by being marked final), but it seems Amber
> already has something related to this, so I'll move the discussion there
> (Keeping the value by default records and the mark values at site of usage
> here though)
That's interesting !
Let's use "const" instead of "final", because "final" has another meaning.
You are proposing that if a local variable is tagged with const, then the compiler will rewrite
const Point point = new Point();
point.x = 3;
to
Point point = new Point();
point = new Point(3, point.y);
This is a transformation is similar to the one done by the compiler inside a constructor of a value class
class Point {
Point(int x, int y) {
this.x = x;
this.y = y;
}
}
is rewritten to
class Point {
static Point <new>(int x, int y) {
Point point = Point.default;
point = _with_field_x(point, x);
point = _with_field_y(point, y);
return point;
}
}
As you can see, the transformation not only rewrite the field assignments to use with_field but it also changes the signature of the containing method,
a constructor returns void while a <new> factory method returns the type of the object.
So either your proposal is only about the content of a method, but in my opinion it's like adding syntactic sugar for the sake of adding syntactic sugar or you also want to do cross method rewriting but it means introducing a new kind of methods which lead us right into the colored function problem [1] (and the magic will also break when using reflection).
When we have introduced records, we have debated on the different ways to introduce a "with" operator, something like
point = Point.__with__ { x = 3; } // this is more or less the C# syntax, not a syntax proposal!
Sooner this month i've done a presentation of Loom and at the end two different persons ask me about either having a with syntax for records or having withers (setter that create a new object) added to the record class).
Julian, correct me if i'm wrong but is it not what you want ? a "with" syntax (whatever it's form) on a record (and value record) ?
>
> best regards,
> Julian
regards,
Rémi
[1] https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/
>
> On Fri, Mar 18, 2022 at 2:07 AM Pedro Lamarão <pedro.lamarao at prodist.com.br>
> wrote:
>
>> My two cents.
>> It may seem at first that doing something like this:
>>
>> X x = new X(1)
>> x.a = 3
>>
>> is very useful, but, that is an artifact of the implementation.
>> Because creating a new object in the heap has a cost, we would rather set
>> its fields directly to avoid this cost.
>> But if X is a value class, doing the below should have no such cost (in
>> favourable circumstances):
>>
>> X x = new X(1)
>> x = new X(3)
>>
>> Atte.
>> Pedro.
>>
>> Em qua., 16 de mar. de 2022 às 23:05, Julian Waters <
>> tanksherman27 at gmail.com> escreveu:
>>
>>> Alternatively, something like
>>>
>>> X x = new X(1);
>>> x.a = 3;
>>>
>>> could be valid while
>>>
>>> final X x = new X(1);
>>> x.a = 3;
>>>
>>> would not?
>>>
>>> best regards,
>>> Julian
>>>
>>> On Tue, Mar 15, 2022 at 11:12 AM Julian Waters <tanksherman27 at gmail.com>
>>> wrote:
>>>
>>> > So if I understand correctly, value objects are essentially the same as
>>> > JEP-401's primitives but with nullability, and according to JEP-401,
>>> > guarantee of atomic modification?
>>> >
>>> > "you are saying "change the a field of the object referred to by x to
>>> 3.""
>>> >
>>> > Regarding this earlier x.a = 3 example, if X was a good ol' identity
>>> > object, x would then be a reference to the actual instance, and we'd
>>> simply
>>> > follow the reference to where the actual object is stored and set a to
>>> 3.
>>> > But if it was a value object, my understanding from your previous post
>>> is
>>> > that it would be stored directly on the abstract (Ignoring HotSpot's
>>> actual
>>> > complex implementation) stack to be operated on as a bunch of fields
>>> with
>>> > no additional metadata other than the check for null. What's stopping us
>>> > from then changing those fields directly? Like Quân Anh mentioned
>>> earlier,
>>> > we would've effectively created a new object if we did do that, since a
>>> > value object has no identity, but wouldn't we have technically changed
>>> the
>>> > value of a after initialization? At the very least, I'd say it would be
>>> > useful syntactic sugar to be able to do x.a on a value object instead of
>>> > having to do new X(3); every time we wanted to change it, unless a is
>>> > explicitly marked as final, without affecting == or related checks in
>>> any
>>> > way, since (from what I know) value objects are compared by field with
>>> ==.
>>> >
>>> > Apologies for my lack of knowledge in this area, it still feels like I
>>> > haven't really gotten a correct idea of what value types really are,
>>> > especially compared to the true primitives of 401
>>> >
>>> > Thanks for the reply and have a great day!
>>> >
>>> > best regards,
>>> > Julian
>>> >
>>> > On Tue, Mar 15, 2022 at 5:59 AM Brian Goetz <brian.goetz at oracle.com>
>>> > wrote:
>>> >
>>> >> You are (understandably) conflating the properties of identity with the
>>> >> properties of reference-ness.
>>> >>
>>> >> If you say
>>> >>
>>> >> x.a = 3
>>> >>
>>> >> you are saying "change the a field of the object referred to by x to
>>> 3."
>>> >> The key to this sentence is *the object*; in order for assignment to
>>> make
>>> >> sense, you have to know exactly which variable you are talking about.
>>> And
>>> >> if that variable is an instance field, then you have to know exactly
>>> which
>>> >> object it lives in. This is where identity comes in; if x is a
>>> reference
>>> >> to an identity object, we then know how to find it. Which means, you
>>> need
>>> >> an identity in order to be able to talk about mutability in a
>>> well-defined
>>> >> way.
>>> >>
>>> >> So if that's true, what is a *reference* to a value? This is the
>>> >> confusing part. If you think in terms of implementation, you'll
>>> probably
>>> >> think "reference means pointer", which puts you into a C frame of mind,
>>> >> because pointers always point at things. But that's not what reference
>>> >> means in Java; its a more abstract kind of indirection.
>>> >>
>>> >> The distinction between int and Integer may be helpful. At one level,
>>> >> int and Integer may seem like "almost the same sets of values"; the
>>> Integer
>>> >> value set has one more value -- null -- than the int value set, and for
>>> >> each of the other values, there is a bidirectional, 1:1 mapping
>>> between the
>>> >> two value sets. But if we look closer, in fact these value sets are
>>> >> disjoint! An int is not an Integer (even if they describe the same
>>> >> number.)
>>> >>
>>> >> So one big difference between references and values is nullability;
>>> null
>>> >> is a special kind of reference, the reference to no object at all.
>>> >> Reference types can be null; primitive types cannot. (There's another,
>>> >> even more subtle distinction, which is tearability, which comes from
>>> the
>>> >> guarantees of the Java memory model.)
>>> >>
>>> >> If we have a value class:
>>> >>
>>> >> value class X { }
>>> >>
>>> >> this gives rise to a reference type X, whose values are references to
>>> >> instances of X. But those instances do not have identity, which means
>>> we
>>> >> can freely shred an X into the values of its fields, and later
>>> reconstitute
>>> >> it into a new X, all without anyone noticing. That's what the lack of
>>> >> identity enables. But the flip side of that means that any given X
>>> does
>>> >> not "live" anywhere; it is just a loose aggregation field values, which
>>> >> behaves like an object when called upon to do object-y things.
>>> >>
>>> >>
>>> >>
>>> >>
>>> >>
>>> >> On 3/13/2022 5:46 AM, Julian Waters wrote:
>>> >>
>>> >> Hi Quân Anh, thanks for the reply,
>>> >>
>>> >> I think I understand what you mean; Since value objects have no
>>> identity,
>>> >> changing their fields would essentially mean the object is now entirely
>>> >> different. However since that's the case, I'm still a little confused
>>> by
>>> >> the semantics of what they really are. Specifically, most of their
>>> benefits
>>> >> are centered around them being nothing more than their fields in
>>> memory,
>>> >> but at the same time documentation states that they're referred to by
>>> >> reference, which I find a little contradictory. If it's the former,
>>> they'd
>>> >> be no different from primitives, and if the latter, they'd be the exact
>>> >> same as identity objects, which has left me puzzled for quite some
>>> time.
>>> >>
>>> >> Sorry for all the trouble, have a great day
>>> >>
>>> >> best regards,
>>> >> Julian
>>> >>
>>> >> On Sun, Mar 13, 2022 at 4:26 PM Quân Anh Mai <anhmdq at gmail.com> <
>>> anhmdq at gmail.com> wrote:
>>> >>
>>> >>
>>> >> Hi,
>>> >>
>>> >> Regarding your concern of mutable value objects, having a mutable
>>> >> identity-less object is illogical. Because a value class does not have
>>> a
>>> >> unique identity, upon modification, the object is changed to become
>>> another
>>> >> object. A value object is immutable in the same way that an int is
>>> >> immutable. You can change a variable int a from containing a value of 3
>>> >> to a value of 4, but you can’t change the number itself, it has been
>>> >> mathematically and programmatically defined to be a specific bit
>>> sequence.
>>> >> Similarly, you can change a variable Point a from enclosing the point
>>> (3,
>>> >> 4) into the point (2, 5), but you can’t change the coordination of the
>>> >> points themselves, they have been (in this case) also mathematically
>>> >> defined. Your understanding of how value classes work is also not
>>> correct.
>>> >> The fact that a value object can be deconstructed to its fields does
>>> not
>>> >> mean that it will. This is simply an optimisation choice of the
>>> compiler.
>>> >>
>>> >> Regards,
>>> >> Quan Anh
>>> >>
>>> >> On Sun, 13 Mar 2022 at 15:42, Julian Waters <tanksherman27 at gmail.com>
>>> <tanksherman27 at gmail.com>
>>> >> wrote:
>>> >>
>>> >>
>>> >> Hi all,
>>> >>
>>> >> I'd like to discuss several topics related to the current draft on
>>> value
>>> >> classes.
>>> >>
>>> >> As mentioned in another post to this list at
>>> >>
>>> https://mail.openjdk.java.net/pipermail/valhalla-dev/2022-January/009871.html
>>> >> ,
>>> >> currently value classes are specified as immutable, with all fields
>>> being
>>> >> declared as final. If I understand this correctly, value based classes,
>>> >> unlike the true primitives which work is currently focused on, can
>>> still
>>> >> be
>>> >> referred to by references; It is only identity that has been given up.
>>> In
>>> >> that instance, naively speaking, wouldn't it be possible for value
>>> objects
>>> >> to have mutable fields? In the same way that one can reference the
>>> fields,
>>> >> one might possibly be able to also change their values through the
>>> >> available object reference as well. From what I can tell, value objects
>>> >> are
>>> >> planned to work internally by decomposing them to nothing more than
>>> their
>>> >> constituent fields and reconstructing them elsewhere. If that is the
>>> case,
>>> >> shouldn't modification of their fields be possible? To me it seems the
>>> >> modification would simply modify the copied value object *b *while
>>> leaving
>>> >> original value object *a *unchanged, which sits perfectly with the
>>> >> constraint that identity should not be supported. Am I mistaken in my
>>> >> assumption?
>>> >>
>>> >> Additionally, if the above holds true, I also suggest allowing an
>>> object
>>> >> to
>>> >> be declared as value based at the site of their usage, instead of just
>>> at
>>> >> their definition. Something like
>>> >>
>>> >> // Item is by default an identity class, but the field "item" here is
>>> now
>>> >> // a value object due to the declaration of value
>>> >> value Item item = new Item();
>>> >>
>>> >> would allow typical identity classes to be treated as value objects
>>> where
>>> >> they're used. This would, to me, present a great deal of flexibility as
>>> >> one
>>> >> could then choose how to allocate a particular object without
>>> >> constraining *every
>>> >> *instance of that class to a specific value/identity dichotomy, and
>>> also
>>> >> avoids the hassle of having to know which classes should be value or
>>> >> identity types ahead of time. On a side note, the current proposal also
>>> >> includes the ability to mark records as value based, given that records
>>> >> are
>>> >> meant exclusively as data classes, I suggest we flip that around and
>>> >> instead have records as *value based by default, *and instead allow
>>> >> marking
>>> >> them as identity objects explicitly if required, with the currently
>>> used
>>> >> ref keyword (Or whatever ref will eventually become), both at
>>> definition
>>> >> and when an actual instance is constructed, as mentioned above. This
>>> could
>>> >> save frustration from having to type value over and over whenever
>>> writing
>>> >> the definition of a new record.
>>> >>
>>> >> Have a great day, hope I can hear your opinions on this! :)
>>> >>
>>> >> best regards,
>>> >> Julian
>>> >>
>>> >>
>>> >>
>>> >>
>>>
>>
>>
>> --
>> Pedro Lamarão
>> https://www.prodist.com.br
>> Securing Critical Systems
>> Tel: +55 11 4380-6585
>>
>> Antes de imprimir esta mensagem e seus anexos, certifique-se que seja
>> realmente necessário.
>> Proteger o meio ambiente é nosso dever.
>> Before printing this e-mail or attachments, be sure it is necessary.
>> It is in our hands to protect the environment.
More information about the valhalla-dev
mailing list