What we have lost ?
John Rose
john.r.rose at oracle.com
Sat Sep 10 01:50:34 UTC 2022
On 7 Sep 2022, at 19:19, Dan Smith wrote:
> Summarizing my takeaways from talking over these use cases today:
>
> On Sep 7, 2022, at 12:56 AM,
> forax at univ-mlv.fr<mailto:forax at univ-mlv.fr> wrote:
>
> Here is a list of such value types:
> - unit types, value types like by example Nothing (which mean that a
> method
> never returns) with no fields.
> Because creating a ref on it creates something :)
>
> If you truly mean for such a class to have no instances, that's not
> something a
> class with a value type can assert—the default instance always
> exists. I can
> see how it would be nice, for example, to have a type like Void that
> is also
> non-nullable, but value types are not the feature to accomplish that.
To pile on with Dan, value types are not at all like empty types
(nothing, not-reached). Like all objects, they are records, products of
fields, and so can be units (what Dan calls singletons), but not empties
(nothings). Only types structured as sums can be empty.
Background: A unit type is different from an empty type, just as the
product identity element 1 (in various settings) differs from the sum
identity element 0 (in various settings). A unit type has one instance
and takes up no storage (O(1) storage globally for its metadata), but
can be stored in many places. An empty type has zero instances, and
takes up no storage because it cannot be stored anywhere, not even on
stack.
Void is a unit type, because you can return from a void method.
NotReached is an empty type, because you cannot return from a method
that declares its return as not reached.
If you add a unit type to a union (sum type), you increase the
information content of that union; a unit type is not an identity for
sum-aggregation. By contrast, if you add an empty type to a union, you
leave the union unchanged (since that union branch is never reached); an
empty type is an identity for sum-aggregation in the same way that 0 is
an identity for addition.
Likewise, if you add a unit type to a product (tuple record), you leave
the record unchanged in its information content, because a unit type is
an identity for product-aggregation. But if you add an empty type to a
record, you change it; the record can no longer be instantiated, so it
collapses to an empty type (with a different label, maybe). This is one
reason we associate 1 with units and 0 with “empties”: the product
of an empty times any type is empty, just as 0*x = 0.
Taking this all into account, a type `Map<int,Nothing>` is *not* a set
of ints. It is a set that *you cannot take any ints out of*. It is an
empty type, isomorphic to `Nothing` itself (assuming `Nothing` is empty
not a unit).
I think the closest Java gets to mapping the world of empty results is
`throws` clauses, which talk about how a method’s (normal) return
point can fail to be reached. (From another POV, both kinds of method
returns, normal and exceptional, are coordinated terms in a
sum-aggregation of the form `Either<NormalType,ThrowType>`.) But
`throws` don’t get all the way there, since regardless of its `throws`
clauses, a method can *always* return normally, so it’s return type is
never actually empty.
An empty type is what you get when you dereference `null`. Maybe the
type of dereferencing `null` could be something like
`NotReached<NullPointerException>`? Maybe. But if we did such a thing,
the generic type `NotReached` would have to be special-cased everywhere
in the language. Probably you could never declare a field or variable
of that type. (Except in dead code. But Java forbids you to write dead
code.) Methods which return `NotReached` would be obligated to throw
the indicated exception (or `RTE` or `Error`) instead of returning.
FWIW, I really like that value types can express labeled units, and
embrace all the quirky corollaries, such as that an array of those guys
is isomorphic to a non-negative `int`. I look forward to the day when
`Map.Entry<T,Void>` specializes so that the second component takes up
zero bits, and so sets can be built on maps with storage efficiency.
*Note* that `Void`! I really mean `java.lang.Void`, not a new value
class. If I specialize to a type which is hardwired to have zero
instances (and that means it is always `null`) I have a unit type
that’s even better than a value-class with no fields, since the `null`
is the only possible value, not `null` plus a default instance.
Specialized generics should IMO specialize on `Void`, when we get there!
The conversation about a hypothetical specialized generic called
`Atomic` for Java is way premature, IMO. Is it mutable or immutable?
Does it affect its container? …Who knows? I am sympathetic with the
idea that wrapping a specialized generic around a random type can
somehow create a new kind of container for that type; I envision such
generics for `Atomic`, `Contended`, `WeakReference`, `Lazy`, and many
more, to replace various ad hoc mechanisms we have today. I want to try
for it, but I don’t regard any of those as a necessary goal, just a
“nice to have if we can get it”.
(One example: We suffer from the lack, today, of arrays of weak
references. But it’s not clear that value-types-plus-arrays can ever
help with that. I’m OK if they don’t. We don’t need to replicate
all of C++’s `atomic` template tricks, nor the tricks from any
particular language.)
Regarding unwanted nulls from ref-default types: Meh; the JVM has a
quarter century invested in dealing with unwanted nulls, to the point
where folks just don’t think about them very much as a performance
problem. As a correctness problem, they are a pain, but again we have
many ways to cope. I think forgetting to put `.val` on your fields or
array creations is just fine; you can ship code that has too many nulls
with a clear conscience, as long as you have a way to recognize when
they are a measurable problem. At that point, fix your code by adding
`.val` in a few places. But you can wait to measure a need in most
cases. People writing high-performance code with complex numbers or
vectors will readily learn where they must “salute the val”; it is
no worse than the current question of when to use `float` or `Float`.
The rest of us can mostly ignore the issue.
To conclude, I don’t feel much “loss” here, nor do I think we are
“lost”. But some of the confusion between units and empties did
leave me “at a loss”. Hence this message.
— John
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/valhalla-spec-observers/attachments/20220909/ec63e86e/attachment.htm>
More information about the valhalla-spec-observers
mailing list