Re: [records] Ancillary fields
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
On April 18, 2018 8:39:12 PM UTC, Brian Goetzwrote: > > >> 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
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.