Re: [records] Ancillary fields

2018-04-18 Thread Brian Goetz

For primitives, you can always force yourself to use Integer:

    lazy Integer i = f();

and make sure f() never returns null.  You can do something similar with 
a library class (e.g., Optional) for references.  So there are surely 
_safe_ ways to do it, albeit ugly ones.


I kind of prefer to have boxing like this be explicit rather than 
implicit; if the user thinks they're putting an `int` in their class, 
I'd like to be as transparent about that as we can.


You were willing to throw on null in the reference case; that can also 
be simulated by:


    lazy Foo f = requireNonNull(f());

Which isn't even that ugly or expensive.  So I suspect that this is less 
of a problem that one might first think, but I could be wrong.




On 4/18/2018 5:59 PM, Kevin Bourrillion wrote:


For instance fields, we have a choice; use extra space in the
object to store the "already initialized" bit, or satisfy
ourselves with the trick that String does with hashCode() -- allow
redundant recomputation in the case where the initializer serves
up the default value.


I strongly suspect there isn't going to be any generally safe way to 
do the latter.




Re: [records] Ancillary fields

2018-04-18 Thread Remi Forax


On April 18, 2018 8:39:12 PM UTC, Brian Goetz  wrote:
>
>
>> Ahh, you missed the `lazy` keyword on there :-) Which is good because
>
>> it raises an issue: when you forget it, bad performance may result 
>> without other observable consequence. Although, it's already the case
>
>> that reading code like the above ought to raise all kinds of alarm 
>> bells (e.g., now I want to go check which fields computeHashCode() 
>> might be referring to, and where /they're/ initialized), so I 
>> /should/ be looking for that `lazy` keyword to put my mind at ease.
>So 
>> maybe this is okay.
>
>Well, "bad" is relative; it won't be any worse than what you do today 
>with eager static fields.  But yes, I did drop the lazy there.
>
>> I assume that, unlike other field initializers, I'm safe to refer 
>> to/any/ other field regardless of how and where that field is 
>> initialized. Right?
>
>I think you mostly are asking about instance fields.  It would be safe 
>to refer to any other field, however, if you _read_ a lazy field in the
>
>constructor, it might trigger computation of the field based on a 
>partially initialized object.  The compiler could warn on the obvious 
>cases where this happens, but of course it can be buried in a chain of 
>method calls.
>
>> The intersection with primitives is interesting. I assume it gets 
>> secretly created as an Integer? So there's a little extra hidden 
>> memory consumption.
>
>For static fields, there's an obvious and good answer that is optimally
>
>time and space efficient with no anomalies: condy.  We desugar
>
>     lazy static T t = e
>     ...
>     moo(t)
>
>into
>
>     // no field needed
>     static t$init() { return ; }
>     ...
>     moo( ldc condy[ ... ] )
>
>and let the constant pool do the lazy initialization and caching. JITs 
>love this.
>
>For instance fields, we have a choice; use extra space in the object to
>
>store the "already initialized" bit, or satisfy ourselves with the
>trick 
>that String does with hashCode() -- allow redundant recomputation in
>the 
>case where the initializer serves up the default value.
>
>So I think the divide is not ref-vs-primitive but whether we are
>willing 
>to take the recomputation hit when it serves up a default value.

I fully agree.
The lazy static with condy also has the same semantics, if the bsm do a side 
effect you may see that the bsm can be called multiple times.

For the record, I've just presented the lazy static this afternoon at devoxx fr 
(in order to explain the semantics of condy) and several people reach me 
afterward saying  it was in interesting idea.

Remi




-- 
Sent from my Android device with K-9 Mail. Please excuse my brevity.


Re: [records] Ancillary fields

2018-04-18 Thread Brian Goetz



Ahh, you missed the `lazy` keyword on there :-) Which is good because 
it raises an issue: when you forget it, bad performance may result 
without other observable consequence. Although, it's already the case 
that reading code like the above ought to raise all kinds of alarm 
bells (e.g., now I want to go check which fields computeHashCode() 
might be referring to, and where /they're/ initialized), so I 
/should/ be looking for that `lazy` keyword to put my mind at ease. So 
maybe this is okay.


Well, "bad" is relative; it won't be any worse than what you do today 
with eager static fields.  But yes, I did drop the lazy there.


I assume that, unlike other field initializers, I'm safe to refer 
to/any/ other field regardless of how and where that field is 
initialized. Right?


I think you mostly are asking about instance fields.  It would be safe 
to refer to any other field, however, if you _read_ a lazy field in the 
constructor, it might trigger computation of the field based on a 
partially initialized object.  The compiler could warn on the obvious 
cases where this happens, but of course it can be buried in a chain of 
method calls.


The intersection with primitives is interesting. I assume it gets 
secretly created as an Integer? So there's a little extra hidden 
memory consumption.


For static fields, there's an obvious and good answer that is optimally 
time and space efficient with no anomalies: condy.  We desugar


    lazy static T t = e
    ...
    moo(t)

into

    // no field needed
    static t$init() { return ; }
    ...
    moo( ldc condy[ ... ] )

and let the constant pool do the lazy initialization and caching. JITs 
love this.


For instance fields, we have a choice; use extra space in the object to 
store the "already initialized" bit, or satisfy ourselves with the trick 
that String does with hashCode() -- allow redundant recomputation in the 
case where the initializer serves up the default value.


So I think the divide is not ref-vs-primitive but whether we are willing 
to take the recomputation hit when it serves up a default value.