Thoughts on peeling and readability

Vitaly Davidovich vitalyd at gmail.com
Sun Dec 13 19:23:03 UTC 2015


The one thing I always found lacking in C# is the limited constraints on
generic types' constructor.  You can state that a T must have a
public default ctor, but cannot state more than that.  For example, if you
want to initialize T instances generically from an int, you cannot.  If you
could provide such a constraint though, then you can use that facility as a
way to provide conversion from primitives to the T.  But even more
generally, constraints that aren't just type based is a nice facility.  Not
sure if that's even on the table at this point, but wanted to throw that
out there.

On Sunday, December 13, 2015, Brian Goetz <brian.goetz at oracle.com> wrote:

> Primitives like int and long are "special" in that all bit patterns are
> valid and there are no integrity constraints that would prevent a client
> from requesting a specific bit pattern. But this is the special case, not
> the general case.  A linguistic construct like T.default would have to work
> for *all* T, not just the special cases.
>
> There's nothing to stop you from writing an implementation that takes
> advantage of knowledge of specific types like int; there's a range of
> options there.  What we're uninterested in doing is allowing clients to
> have unsafe bit-level access to the representation of all value types.
> There's also nothing to stop you from writing value types that allow
> raw-bit operations; we're just not going to require that every value type
> support that (which is what asking for a bit-oriented T.default would be.)
>
> T.default will almost certainly not go through a constructor; the VM will
> zero out the bits as it does with the existing built-in types (the eight
> primitive types plus references.)  This process is pretty obvious from both
> a specification and implementation perspective, but it does create some
> responsibility for writers of value types -- specifically the need to deal
> with the default bit pattern.
>
> As our friends in the .NET community have discovered, trying to enforce
> that the no-arg ctor is always executed before a value is exposed is a game
> of whack-a-mole, so having T.default go through a constructor simply
> reduces the probability of the "implicit null" surprise, but doesn't banish
> it.  One can be as snarky as one likes about the tradeoffs ("reinvented
> null" vs "reinvented serialization"), but the reality here is that there
> are risks lurking around both corners.  Personally I like the tradeoff
> we're converging towards, but we are aware it is not perfect.
>
>
> Stepping back, rather than arguing the merits or demerits of a particular
> solution (which at this point we've more than exhausted), it's far more
> helpful to talk about the problem instead of the solution.  So, let me ask
> -- what use cases are you concerned about, other then the manufacture of
> multiple sentinels for use inside data structure implementations?
>
> (I suspect in the end you will find that you will be able to accomplish
> what you need with the tools available, but the "let me specify the bit
> pattern for an arbitrary value type" approach is not the way to get there.)
>
>
> On 12/13/2015 1:32 PM, Timo Kinnunen wrote:
>
>>
>> Well, it’s ints and longs, and all primitive types copyable around as
>> ints and longs, and all objects serializable to and from arrays of ints and
>> longs, and all arrays of such, and all values made of such, and all arrays
>> of such values, and all values made of such values, aaand I’m probably
>> missing a dimension or two somewhere.
>>
>> These values are just as valid regardless of which bit patterns, all-zero
>> or not, were used to construct them. They are safe to be copied around and,
>> if you implemented them yourself, hashCode, equals, toString and any
>> component-wise operations could also be done safely. Such operations simply
>> can’t call any foreign code of any of the value or reference types
>> involved. We don’t expect that we can take an arbitrary Object, use
>> reflection to zero out its fields and then be able to call its instance
>> methods like nothing had happened either. So, for reference types these
>> operations would have to done using reflection, for value types VarHandles
>> might give better performance.
>>
>> Ultimately I guess it all depends on whether T.default invokes some
>> constructor or not. If it doesn’t or if the constructor is specified to
>> always succeed trivially then we have reinvented null. Our new nulls trade
>> off a large number of NPEs for silent invariant violations. This could be a
>> good tradeoff but without knowing about the consequences of the violations
>> beforehand it’s gonna be hard to say for certain. The good news is that
>> hey, null is back!
>>
>> If T.default executes a constructor that can refuse an all-zero bit
>> pattern, then we have reinvented serialization for value types and are
>> requiring all value types support it. Our serialization protocol only
>> recognizes one input value and can only deserialize, so it’s a bit useless.
>> But with the addition of the missing serialize-function we can then define
>> transforms for long[] <-> val <-> long[] and will have a quite general and
>> capable system already.
>>
>> Or are we gonna just include the worst parts from both?
>>
>>
>>
>>
>>
>>
>> --
>> Have a nice day,
>> Timo
>>
>> Sent from Mail for Windows 10
>>
>>
>> *From: *Brian Goetz
>> *Sent: *Sunday, December 13, 2015 01:20
>> *To: *Timo Kinnunen
>> *Cc: *Maurizio Cimadamore;Paul Benedict;valhalla-dev at openjdk.java.net
>> *Subject: *Re: Thoughts on peeling and readability
>>
>> No.  In general, to/from raw bit operations are not safe except in a few
>> corner cases (like int and long.)  Values are not uncontrolled buckets of
>> bits.
>>
>> On the other hand, T.default *is* safe, because every type has a default
>> bit pattern which is the initialization of any otherwise uninitialized
>> field or array element.  (It so happens that this default bit pattern
>> corresponds to all zero bits for all types, though this is mostly a
>> convenience for VM implementors.)  For a composite value, the default value
>> is comprised of the default value for all fields.  By *definition*, the
>> all-zero bit pattern is a valid element of all value types.  However, there
>> is no guarantee that any other bit pattern is valid for any given value
>> type.
>>
>> If a particular value type wants to expose to/from raw bit constructors,
>> that’s fine — but you’re asking for a language feature that applies to
>> *all* values — and there is no guarantee that this is a safe operation for
>> all values.
>>
>> On Dec 12, 2015, at 5:44 PM, Timo Kinnunen <timo.kinnunen at gmail.com
>> <mailto:timo.kinnunen at gmail.com>> wrote:
>>
>>
>>
>>     Field layout and bit fiddling isn’t exactly what I was thinking.
>>     Rather I was thinking something like Float.floatToRawIntBits() and
>>     Double.doubleToRawLongBits(), but without having to know about the
>>     types Float and Double or how many bits are in their raw bits. So
>>     something like this syntax:
>>
>>                     static <any T> T nextUp (T value) {
>>
>>     <?missing type?> rawBits = T.toRawBits(value);
>>
>>                                     T nextValue =
>>     T.fromRawBits(rawBits + 1);
>>
>>     return nextValue;
>>
>>                     }
>>
>>     This should fit in Valhalla reasonably well, as it is just a
>>     generalization of T.default with its complement operation
>>     included. And as it is, all of the problems you listed already
>>     apply to T.default. For example, a value type with one long field:
>>     If the long value in the field is a handle pointing to a
>>     memory-mapped buffer then any use of a default value of such a
>>     type could cause a crash. Which can include asking a properly
>>     constructed value if it is equal to any of the values in an array
>>     you have.
>>
>>
>>
>>
>>     --
>>     Have a nice day,
>>     Timo
>>
>>     Sent from Mail for Windows 10
>>
>>
>>     *From:*Brian Goetz
>>     *Sent:*Saturday, December 12, 2015 18:21
>>     *To:*Timo Kinnunen;Maurizio Cimadamore;Paul
>>     Benedict;valhalla-dev at openjdk.java.net
>>     <mailto:valhalla-dev at openjdk.java.net>
>>     *Subject:*Re: Thoughts on peeling and readability
>>
>>     Precise layout and bit control of values are anti-goals of
>>     Valhalla, so
>>
>>     we're not really exploring this direction at this time.
>>
>>     The problem with approaches like the one you suggest is they fall
>>     apart
>>
>>     as soon as you leave the realm of "primitives modeled as values."
>>  What
>>
>>     about values that have refs in them?  What about values whose
>>
>>     representations are private?  Their implementation is supposed to
>>     be in
>>
>>     sole control of their representation.  This runs contrary to the
>>     "codes
>>
>>     like a class" dictum.
>>
>>     On 12/12/2015 4:43 AM, Timo Kinnunen wrote:
>>
>>     > Hi,
>>
>>     >
>>
>>     > One thing that I don’t remember seeing is any syntax for
>>     constructing arbitrary values in generic code without having to
>>     know about the precise field layouts and what the meaning of such
>>     fields is. Something like T.default but for values other than 0.
>>     Perhaps T.default(12345) or some such?
>>
>>     >
>>
>>     > Or maybe this is slated to go with bytecode type specialization…
>>     What sort of syntax is envisioned to be driving that anyways?
>>
>>     >
>>
>>     >
>>
>>     >
>>
>>     >
>>
>>     >
>>
>>
>

-- 
Sent from my phone


More information about the valhalla-dev mailing list