Empty value types

John Rose john.r.rose at oracle.com
Tue Aug 5 19:56:04 UTC 2014

On Aug 5, 2014, at 11:56 AM, Paul Govereau <paul.govereau at oracle.com> wrote:

> Ok, that makes sense. You are saying we should treat what I was calling an "empty value type" as a unit type instead. That is, a type with exactly one member, and not a void type like I was thinking (which has zero members). Also, if we think about value types in general as named tuples, then the empty tuple is naturally a unit.
> This raises another question though: what about void? There are several uses of the type java.lang.Void in the libraries. The way I read these, what is wanted is an analog of "void": that is, a truly empty type, not a unit type (otherwise it would be called java.lang.Unit?). Given this, should we have a way to declare analogs of "void"?

I have always thought of "void" (at least as the return type of a method) as a unit type.

For us, I think the type of no values is that of an expression which does not complete normally, sometimes called "Unreachable" or "Nothing".  See Neal Gafter's closures proposal, http://gafter.blogspot.com/2006/11/closures-esoterica-completion.html

>  final __ByValue class LikeUnit { }
>  final __ByValue class LikeInt { int x; }
>  final __ByValue class LikeVoid { ????? }

s/?????//; same as LikeUnit

> Relatedly, is this a valid declaration:
>  final __ByValue V<T extends V<T>> { T t; }
> and what is its relation to LikeVoid given that T is provably empty (has no members)?

(a) It is a nominally distinct equivalent to LikeUnit and LikeVoid, (b) it is illegal because a value type is kinda ugly as a bound, (c) it is non-realizable because the only type-instance features a field of type V<V<V<...>>> inside a value of the same type, and values cannot contain themselves as fields (we wrote that one down already!), and such a non-realizable type is illegal because ugly, or (d) such a non-realizable type is quite handy, because it exactly denotes the nature of an expression which can never return normally.

> Another related question is: can we instantiate generic types with "void"? e.g.
>  class F implements Function<int,void> {
>    void apply(int x) { ... }
>  }

Currently "void" is not a type.  But IMO it should be a unit type.  (BTW Void already acts as a box for a unit; it is a reference type with exactly one value, null.)  With some broad but superficial changes to JLS, void could be declared to be the unit type we all thought it could be.

If void is a unit type, then of course generic arrow types should be allowed to return it.

> and,
>  Set<void> one;

Yes; that is a type with 2^(2^0)=2 values: empty, and contains the sole possible (unit) value.  I suppose it could be specialized to a single-bit representation, if we had enough machinery.  Note that Set<void> expands to something containing a Map<void,void>, which is also reasonable and useful, as a filled-in corner case of a larger schema of types.

> In some sense, we are promoting the primitive types up to being first-class citizens in the language: they can be user defined as value types, used in generics, etc.

More or less.  We need to find a graceful way to do this.  It seems likely that we can define new wrapper types java.lang.'int', java.lang.'void', etc.  And these wrapper types can express (in "codes like a class" form) the key properties of 'int' and 'void'.

We will drive ourselves crazy if we try to capture *all* semantics of the primitive types.  For example, JLS suggests that long is a supertype of int, and there are special-purpose promotion rules for assignments and operators.  But it's not worth our trouble to try to express all those legacy rules in a general form that applies to all value types.

I.e., we can do a certain amount of retconning of int and void as value types, but we need to accept from the beginning that it cannot be perfectly done.

The distinction between a value type and its (same-named) box type helps here, because we can choose to make java.lang.'int' a perfectly normal box type, even if its corresponding value type 'int' is not a fully regular value.

> Is void coming along for the ride?

Whatever ride there is, I hope so.  The unit type is valuable.  But it is probably less valuable if you have many nominal versions of it floating around.  So we should try to retcon void as a unit type.

> My guess is no, the examples above are all illegal: void has no user-defined analogs, values can't be parametrized by empty types, and void can't be used to instantiate a generic type. Is this right, or do we want to think about making void a first-class citizen along with int, etc.?

Those examples are all illegal today.  The question is what graceful changes we can make to (a) extend the present type system to include values and (b) adapt the existing types to be regular in their interactions with values.

> Paul
> PS: In other languages we can see first-class void types, like:
> type Void
> type Unit = ()
> type Option a = Some a | None
> type AnotherUnit = Option Void
> However, the language must guarantee that no value of a void type can ever appear in a term.

There is a terminology problem here.  I think to avoid confusion we Java folk should use "void" as a proper noun for both the type and its sole value, but not as an adjective ("void type", "void expression", "void return").

We Java folks unfortunately cannot use the term "void type" as a synonym for "empty type", because our keyword 'void' behaves as a unit type.  Doesn't it?  The behavior of a function which returns 'void' (or even "no value") is indistinguishable from a function which returns the same value every time.  Both behaviors differ from a function which does not return; that function can be given a more specific type (of zero values) than the others.

Gafter solves the problem by using a new name, "Nothing".  I like that.  (He also introduces "Null" for the type of null; I retconned "Void" for that in JSR 292.)

A Scala guy muses here on "void vs unit vs nothing":

— John

> On 08/05/2014 01:04 AM, John Rose wrote:
>> Empty value types are perfectly reasonable to instantiate, although there is only one value of each such type (1 = 2^0).
>> A type Set<T> can be efficiently implemented as Map<T,Unit>, if Unit is an empty value type which is instantiated for each set element.
>> It is true that static members of empty types are not interestingly different from non static members. But I don't see a reason to forbid one or the other. If the type implements an interface it needs non static methods. Factories are static. Seems we need both even for the empties.
>> Lots of types in FP langs use a unit type, and corresponding unit values, at least for arrow types. Am I missing something here?

More information about the valhalla-dev mailing list