Re: RefObject and ValObject
On 15/04/2019 21:54, fo...@univ-mlv.fr wrote: m(List list ) { ... } ... m(List.of(new Foo(), new Bar())); Slightly incorrect sir :-) This is effectively equivalent to the example I wrote earlier List list = List.of(...) The expected type will force the equality constraint and will drive inference home. Otherwise it would not even work today in cases when you pass e.g. Integer and String to List.of, as their common supertype is something sharper than Object. This stuff used indeed to fail pre-Java 8, but I think we cured most of the issues. The remaining ones are when inference eagerly resolves variables w/o looking at the target, because a target is not there, as for 'var', or because the expression is in a receiver position, as in: List list2 = List.of(new Foo(), new Bar()).subList(0, 42); Now, this will fail, but I believe we're in the corner^2 territory? Maurizio
Re: RefObject and ValObject
Most of what i wrote in the L10/L20 memo is accurate :) > On Apr 15, 2019, at 5:32 PM, fo...@univ-mlv.fr wrote: > > > > De: "Brian Goetz" > À: "Remi Forax" > Cc: "valhalla-spec-experts" > Envoyé: Lundi 15 Avril 2019 23:23:57 > Objet: Re: RefObject and ValObject > V <: V? by value set inclusion; V? is the type obtained by adjoining `null` > to the value set of V. > > Looking for.a better name for this. :”Nullable value types” is a terrible > name, so I don’t want to say that. (Too confusing with null-default value > types.). They could properly be define as “null-adjoined value types”, but > that’s not helpful if you don’t know what an adjunction is. Similarly for > “nullable projection”. > > But the basic idea is that V? is the denotation of the union type of V | > Null. > > > Ok, i think i got slightly confused about the difference between RefObject > and ValueRef? > > Rémi > > >
Re: RefObject and ValObject
> De: "Brian Goetz" > À: "Remi Forax" > Cc: "valhalla-spec-experts" > Envoyé: Lundi 15 Avril 2019 23:23:57 > Objet: Re: RefObject and ValObject >> V <: V? by value set inclusion; V? is the type obtained by adjoining `null` >> to >> the value set of V. > Looking for.a better name for this. :”Nullable value types” is a terrible > name, > so I don’t want to say that. (Too confusing with null-default value types.). > They could properly be define as “null-adjoined value types”, but that’s not > helpful if you don’t know what an adjunction is. Similarly for “nullable > projection”. > But the basic idea is that V? is the denotation of the union type of V | Null. Ok, i think i got slightly confused about the difference between RefObject and ValueRef? Rémi
Re: RefObject and ValObject
The word "reference" is available and fits the bill. "nullable reference type" > On Apr 15, 2019, at 2:23 PM, Brian Goetz wrote: > >> V <: V? by value set inclusion; V? is the type obtained by adjoining `null` >> to the value set of V. > > Looking for.a better name for this. :”Nullable value types” is a terrible > name, so I don’t want to say that. (Too confusing with null-default value > types.). They could properly be define as “null-adjoined value types”, but > that’s not helpful if you don’t know what an adjunction is. Similarly for > “nullable projection”. > > But the basic idea is that V? is the denotation of the union type of V | > Null. >
Re: RefObject and ValObject
> V <: V? by value set inclusion; V? is the type obtained by adjoining `null` > to the value set of V. Looking for.a better name for this. :”Nullable value types” is a terrible name, so I don’t want to say that. (Too confusing with null-default value types.). They could properly be define as “null-adjoined value types”, but that’s not helpful if you don’t know what an adjunction is. Similarly for “nullable projection”. But the basic idea is that V? is the denotation of the union type of V | Null.
Re: RefObject and ValObject
In the document on “Towards a plan for L10 / L20” I tried to answer these, but I got it slightly wrong. I said: V <: V? <: ValObject <: Object But really that should be V <: V? <: ValObject? <: Object V <: ValObject <: Object V <: V? by value set inclusion; V? is the type obtained by adjoining `null` to the value set of V. I am leaning towards saying that `RefObject?` and `Object?` are not sensible things to write, because they are equal to `RefObject` and `Object`. (That’s separately from `T?`, which always makes sense, but sometimes just means “T”.). No flavor of Point is a subtype of any flavor of RefObject; ValObject and RefObject are disjoint. > On Apr 15, 2019, at 5:10 PM, fo...@univ-mlv.fr wrote: > > > > De: "Brian Goetz" > À: "Remi Forax" > Cc: "Maurizio Cimadamore" , > "valhalla-spec-experts" > Envoyé: Lundi 15 Avril 2019 22:46:08 > Objet: Re: RefObject and ValObject > yes ! > all generics will suddenly accept value types. > > > Yes, this is by design. If you can’t have an ArrayList of Point, that would > be terrible. Of course, until we have specialization (later in the story), > these will be erased, and restricted to the nullable projection. > > Does it means that Point? is a subtype of RefObject ? > > Note: the second question mark in this sentence is because it's a question. > > Rémi >
Re: RefObject and ValObject
> De: "Brian Goetz" > À: "Remi Forax" > Cc: "Maurizio Cimadamore" , > "valhalla-spec-experts" > Envoyé: Lundi 15 Avril 2019 22:46:08 > Objet: Re: RefObject and ValObject >> yes ! >> all generics will suddenly accept value types. > Yes, this is by design. If you can’t have an ArrayList of Point, that would be > terrible. Of course, until we have specialization (later in the story), these > will be erased, and restricted to the nullable projection. Does it means that Point? is a subtype of RefObject ? Note: the second question mark in this sentence is because it's a question. Rémi
Re: RefObject and ValObject
> De: "Maurizio Cimadamore" > À: "Brian Goetz" , "Remi Forax" > Cc: "valhalla-spec-experts" > Envoyé: Lundi 15 Avril 2019 22:53:49 > Objet: Re: RefObject and ValObject > I think we should be ok inference-wise, except of course for edge cases (like > vars) which exposes the guts of the inference engine. > In normal cases like: > List list = List.of(new Foo(), new Bar()) > the equality constraint on the LHS should force a solution which overrides the > new 'RefObject' bound that comes in from the RHS. > (that is, you have T = Object, then T :> Foo and T :> Bar, so T = Object > 'wins', > even if less precise) > Maurizio yes, i think you're right, Rémi > On 15/04/2019 21:39, Brian Goetz wrote: >>> 1) any codes that has inference >>> var list = List.of(new Foo(), new Bar()); >>> will be inferred as List instead of List, so calling a >>> method >>> that takes a List will not compile anymore. >> Not to dismiss the “what about inference” issue, but any code that combines >> `var` with `List,of()` should not be surprised at the result of inference …. >> But, “what about inference” noted. >>> 2) any code that consider Object as special class may stop working, dynamic >>> proxies that have a special case for Object, any code that reflect >>> recursively >>> on the hierarchy to find all the methods, any code that remove getClass() >>> from >>> the getter list, etc. >> Absorbing value types is going to require some changes on the part of most >> frameworks to get best results. This one seems in that category? >> Again, not to dismiss, keep them coming, but so far this isn’t scaring me >> away >> from having the object model that makes most sense.
Re: RefObject and ValObject
I think we should be ok inference-wise, except of course for edge cases (like vars) which exposes the guts of the inference engine. In normal cases like: List list = List.of(new Foo(), new Bar()) the equality constraint on the LHS should force a solution which overrides the new 'RefObject' bound that comes in from the RHS. (that is, you have T = Object, then T :> Foo and T :> Bar, so T = Object 'wins', even if less precise) Maurizio On 15/04/2019 21:39, Brian Goetz wrote: 1) any codes that has inference var list = List.of(new Foo(), new Bar()); will be inferred as List instead of List, so calling a method that takes a List will not compile anymore. Not to dismiss the “what about inference” issue, but any code that combines `var` with `List,of()` should not be surprised at the result of inference …. But, “what about inference” noted. 2) any code that consider Object as special class may stop working, dynamic proxies that have a special case for Object, any code that reflect recursively on the hierarchy to find all the methods, any code that remove getClass() from the getter list, etc. Absorbing value types is going to require some changes on the part of most frameworks to get best results. This one seems in that category? Again, not to dismiss, keep them coming, but so far this isn’t scaring me away from having the object model that makes most sense.
Re: RefObject and ValObject
> De: "Brian Goetz" > À: "Remi Forax" > Cc: "Maurizio Cimadamore" , > "valhalla-spec-experts" > Envoyé: Lundi 15 Avril 2019 22:39:43 > Objet: Re: RefObject and ValObject >> 1) any codes that has inference >> var list = List.of(new Foo(), new Bar()); >> will be inferred as List instead of List, so calling a >> method >> that takes a List will not compile anymore. > Not to dismiss the “what about inference” issue, but any code that combines > `var` with `List,of()` should not be surprised at the result of inference …. > But, “what about inference” noted. you don't need var, var represent the inference value of an expression, instead of having two lines, you can always write m(List list ) { ... } ... m(List.of(new Foo(), new Bar())); >> 2) any code that consider Object as special class may stop working, dynamic >> proxies that have a special case for Object, any code that reflect >> recursively >> on the hierarchy to find all the methods, any code that remove getClass() >> from >> the getter list, etc. > Absorbing value types is going to require some changes on the part of most > frameworks to get best results. This one seems in that category? > Again, not to dismiss, keep them coming, but so far this isn’t scaring me away > from having the object model that makes most sense. but this compatibility issues have nothing to do with value types pre se, it's because of the introduction of RefObject, and currently all other compatibility issues can be solved at use site (i believe), here you are adding compatibility issue that can only be solved at declaration site, that's a big change because now as a user you have to wait until all your dependencies have been updated before using value type. Rémi
Re: RefObject and ValObject
> yes ! > all generics will suddenly accept value types. > Yes, this is by design. If you can’t have an ArrayList of Point, that would be terrible. Of course, until we have specialization (later in the story), these will be erased, and restricted to the nullable projection.
Re: RefObject and ValObject
> De: "Maurizio Cimadamore" > À: "Brian Goetz" , "Remi Forax" > Cc: "valhalla-spec-experts" > Envoyé: Lundi 15 Avril 2019 22:23:25 > Objet: Re: RefObject and ValObject > Maybe (Remi correct me if I'm wrong), the problem Remi was referring to is > that > we also have existing generic declarations like which, in > the new world, will mean either VALUE or REFERENCE. I think this is a > consequence of the choice (1) I described in my email - e.g. reinterpret > Object > in type position as TOP_TYPE. > Maurizio yes ! all generics will suddenly accept value types. Rémi > On 15/04/2019 19:00, Brian Goetz wrote: >>> It's not a minor change, and all code that uses a type parameter that have >>> Object as bound will become ambiguous. >> I don’t think so. You can’t say >> new T() >> when T is bounded at Object (or anything, for that matter.). >> What ambiguity are you afraid of here?
Re: RefObject and ValObject
> > 1) any codes that has inference >var list = List.of(new Foo(), new Bar()); > will be inferred as List instead of List, so calling a > method that takes a List will not compile anymore. Not to dismiss the “what about inference” issue, but any code that combines `var` with `List,of()` should not be surprised at the result of inference …. But, “what about inference” noted. > 2) any code that consider Object as special class may stop working, dynamic > proxies that have a special case for Object, any code that reflect > recursively on the hierarchy to find all the methods, any code that remove > getClass() from the getter list, etc. Absorbing value types is going to require some changes on the part of most frameworks to get best results. This one seems in that category? Again, not to dismiss, keep them coming, but so far this isn’t scaring me away from having the object model that makes most sense.
Re: RefObject and ValObject
It's not bikeshedding, hence the "if you prefer ...", it's trying to explain that it's a typing issue and not a runtime class issue. Rémi - Mail original - > De: "John Rose" > À: "Remi Forax" , "Maurizio Cimadamore" > > Cc: "Brian Goetz" , "valhalla-spec-experts" > > Envoyé: Lundi 15 Avril 2019 20:13:50 > Objet: Re: RefObject and ValObject > Avoid the Bikeshed! Make sure that temporary or provisional > syntaxes look __Different from permanent ones. > > On Apr 15, 2019, at 8:03 AM, Remi Forax wrote: >> >> val, ref and any only applies to Object. >> >> if you prefer in term of notation, let's rename ref Object to Object?, val >> Object to Object! and any Object to Object*, >> >> you can apply ? on any value type, Object included, > > you can apply ! and * only on Object.
Re: RefObject and ValObject
> De: "Brian Goetz" > À: "Remi Forax" > Cc: "Maurizio Cimadamore" , > "valhalla-spec-experts" > Envoyé: Lundi 15 Avril 2019 20:00:52 > Objet: Re: RefObject and ValObject >> It's not a minor change, and all code that uses a type parameter that have >> Object as bound will become ambiguous. > I don’t think so. You can’t say > new T() > when T is bounded at Object (or anything, for that matter.). > What ambiguity are you afraid of here? 1) any codes that has inference var list = List.of(new Foo(), new Bar()); will be inferred as List instead of List, so calling a method that takes a List will not compile anymore. 2) any code that consider Object as special class may stop working, dynamic proxies that have a special case for Object, any code that reflect recursively on the hierarchy to find all the methods, any code that remove getClass() from the getter list, etc. Rémi
Re: RefObject and ValObject
That’s right, as we think that will be the best default. Obviously in some cases users will want to re-bound to . (For existing code; this comes with compatibility concerns, which we can handle in various ways. The standard trick to change a bound without changing erasure is to bound at Object&X; this works when X is an interface but currently would be unhappy if X is a class. But this could be adjusted.) > On Apr 15, 2019, at 4:23 PM, Maurizio Cimadamore > wrote: > > Maybe (Remi correct me if I'm wrong), the problem Remi was referring to is > that we also have existing generic declarations like > which, in the new world, will mean either VALUE or REFERENCE. I think this is > a consequence of the choice (1) I described in my email - e.g. reinterpret > Object in type position as TOP_TYPE. > > Maurizio > > On 15/04/2019 19:00, Brian Goetz wrote: >>> >>> It's not a minor change, and all code that uses a type parameter that have >>> Object as bound will become ambiguous. >> >> I don’t think so. You can’t say >> >> new T() >> >> when T is bounded at Object (or anything, for that matter.). >> >> What ambiguity are you afraid of here? >> >>
Re: RefObject and ValObject
Maybe (Remi correct me if I'm wrong), the problem Remi was referring to is that we also have existing generic declarations like Object> which, in the new world, will mean either VALUE or REFERENCE. I think this is a consequence of the choice (1) I described in my email - e.g. reinterpret Object in type position as TOP_TYPE. Maurizio On 15/04/2019 19:00, Brian Goetz wrote: It's not a minor change, and all code that uses a type parameter that have Object as bound will become ambiguous. I don’t think so. You can’t say new T() when T is bounded at Object (or anything, for that matter.). What ambiguity are you afraid of here?
Re: RefObject and ValObject
On 4/15/19 11:23 AM, Brian Goetz wrote: > Then we have: > > abstract class Object { } > abstract class RefObject <: Object { } > abstract class ValObject <: Object { } > I also think it is plausible. As one part of migration story, maybe the public no-arg constructor could be explicitly listed as @Deprecated, adding a non-deprecated "protected" one. -Doug
Re: RefObject and ValObject
I think this hangs together very well. For legacy bytecode (only), the JVM has to be willing to do these one-time fudges: - rewrite new java/lang/Object to new java/lang/RefObject - rewrite invokespecial java/lang/Object.()V to invokespecial java/lang/RefObject.()V The verifier can observe these rewrites, but it may also take this additional step, for legacy code only: - widen the type of new java/lang/RefObject to plain Object (…which would prevent the verifier from passing the new RefObject to a method that actually takes RefObject. Not sure that step is useful; it's a "one hand clapping" type of move, which in practice won't be observable.) — John > On Apr 15, 2019, at 8:23 AM, Brian Goetz wrote: > >> >> Your idea of treating Object as abstract is, I believe, a sound one (which >> doesn't need any extra rule) - but we might have to figure out some story >> for anonymous inner classes of the kind `new Object() { ... }`. > > After thinking about it for all of five minutes, I think this may have broken > the logjam (or, at least the current logjam.). We’ve been asking ourselves > whether RO/VO are classes or interfaces, when we didn’t really consider > abstract classes. Which we didn’t consider because we had assumed that the > concrete-ness of Object was nailed down. Let’s assume it’s not. > > Then we have: > > abstract class Object { } > abstract class RefObject <: Object { } > abstract class ValObject <: Object { } > > Existing classes that extend Object are silently reparented to RefObject, > both at compile time and runtime. This may have some small .getSuperclass() > anomalies but this seems pretty minor. Same with anon classes of Object. > Inline classes implicitly extend ValObject. > > We add a method `Object::newInstance` (name to be bikeshod later.). We start > warning in the compiler on `new Object`, to motivate migration to > `Object::newInstance`. Runtime rewrites these too. > > There are some minor behavioral compatibility issues here, but they seem > pretty minor, and in the end, we end up with a hierarchy that describes the > way we want users to see the type system. > >
Re: RefObject and ValObject
Avoid the Bikeshed! Make sure that temporary or provisional syntaxes look __Different from permanent ones. On Apr 15, 2019, at 8:03 AM, Remi Forax wrote: > > val, ref and any only applies to Object. > > if you prefer in term of notation, let's rename ref Object to Object?, val > Object to Object! and any Object to Object*, > > you can apply ? on any value type, Object included, > you can apply ! and * only on Object.
Re: RefObject and ValObject
> > It's not a minor change, and all code that uses a type parameter that have > Object as bound will become ambiguous. I don’t think so. You can’t say new T() when T is bounded at Object (or anything, for that matter.). What ambiguity are you afraid of here?
Re: RefObject and ValObject
> De: "Brian Goetz" > À: "Maurizio Cimadamore" > Cc: "valhalla-spec-experts" > Envoyé: Lundi 15 Avril 2019 17:23:36 > Objet: Re: RefObject and ValObject >> Your idea of treating Object as abstract is, I believe, a sound one (which >> doesn't need any extra rule) - but we might have to figure out some story for >> anonymous inner classes of the kind `new Object() { ... }`. > After thinking about it for all of five minutes, I think this may have broken > the logjam (or, at least the current logjam.). We’ve been asking ourselves > whether RO/VO are classes or interfaces, when we didn’t really consider > abstract classes. Which we didn’t consider because we had assumed that the > concrete-ness of Object was nailed down. Let’s assume it’s not. > Then we have: > abstract class Object { } > abstract class RefObject <: Object { } > abstract class ValObject <: Object { } > Existing classes that extend Object are silently reparented to RefObject, both > at compile time and runtime. This may have some small .getSuperclass() > anomalies but this seems pretty minor. Same with anon classes of Object. > Inline > classes implicitly extend ValObject. > We add a method `Object::newInstance` (name to be bikeshod later.). We start > warning in the compiler on `new Object`, to motivate migration to > `Object::newInstance`. Runtime rewrites these too. > There are some minor behavioral compatibility issues here, but they seem > pretty > minor, and in the end, we end up with a hierarchy that describes the way we > want users to see the type system. It's not a minor change, and all code that uses a type parameter that have Object as bound will become ambiguous. Rémi
Re: RefObject and ValObject
> For (2) the proposal I saw earlier said something like, we'd like for `new > Object` to mean REFERENCE. I think that is a siren song, and this issue has > been addressed by Brian's proposal to deal with `new Object` as a migration > problem. Rather than suggesting to use some static factory instead (which > works for plain instance creation but not for inner class creation) I think > perhaps the user code should be migrated to use `new RefObject` and `new > RefObject() { }` instead (at least if people want the reference semantics). > But these are minor details > What I like about treating this as a migration problem is, that despite the inconvenience, the resulting code is actually _a lot more clear_. The first time I saw “new Object()” (that was a long time ago), I remember thinking “What the heck is the point of that?”, until I realized that Objects had a secret object identity. Whereas “new IdentityObject()” is more clear that you are creating an instance _for the precise purpose of using its identity_. So, while there is some migration pain, the resulting language is actually more clear. I like that.
Re: RefObject and ValObject
Again, I was not proposing we went down there (I like RefObject, ValObject). But... using subclassing to have a semantics discussion has issues on its own (e.g. should we use interfaces or classes), which I think are distracting at this point. Hence my use of an alternate notation in an attempt to make problems pop out more clearly. But, since the notation was already used in past proposals, it seems like I opened a can of worms :-). To me the important questions are: 1) TYPES - utterances of `Object` in a type positon; what do they mean? Do they mean VALUE or REFERENCE, or TOP-TYPE? 2) EXPRESSIONS - utterances of `Object` inside expressions such as `new Object()`, what do they mean, again VALUE, REFERENCE or TOP-TYPE? For (1), I think we are all in agreement that utterances of Object-as-a-type means TOP-TYPE. For (2) the proposal I saw earlier said something like, we'd like for `new Object` to mean REFERENCE. I think that is a siren song, and this issue has been addressed by Brian's proposal to deal with `new Object` as a migration problem. Rather than suggesting to use some static factory instead (which works for plain instance creation but not for inner class creation) I think perhaps the user code should be migrated to use `new RefObject` and `new RefObject() { }` instead (at least if people want the reference semantics). But these are minor details. The more important fact is that, even if we make Object abstract, you could still make new Object() { ... } So there's still the issue of how that expression is interpreted. I think the answer has gotta be, again, TOP-TYPE (as for question (1)). So: RefObject ro = new Object() { ... } //error Maurizio On 15/04/2019 16:03, Remi Forax wrote: *De: *"Brian Goetz" *À: *"Maurizio Cimadamore" *Cc: *"valhalla-spec-experts" *Envoyé: *Lundi 15 Avril 2019 16:38:58 *Objet: *Re: RefObject and ValObject Let's model the value vs. reference constraint explicitly - e.g. with 'val' and 'ref' type kinds (and let's not open the can worm as to what the syntax should be, whether it should be a type anno, etc.) So: val Object ---> accepts all values ref Object ---> accepts all references any Object ---> accepts all references We explored this sort of thing in Q world. One place where this really was painful was what it did to generic tvar constraints; we had an entire new algebra of constraints: void m(T t) { … } Brian, val, ref and any only applies to Object. if you prefer in term of notation, let's rename ref Object to Object?, val Object to Object! and any Object to Object*, you can apply ? on any value type, Object included, you can apply ! and * only on Object. Object? is erased to Ljava/lang/Object; by the generic signature is Ljava/lang/Object/* (so it's Object for the VM and Object! for the compiler), Object! is erased to Qjav/lang/Object; Object* is erased to Ljava/lang/Object; As a bound of a type variable, Object is equivalent to Object? by backward compatibility. and void m(T t) { … } means that T is not nullable, so it's a value type. And at runtime, instead of (o instanceof Object!) one will write o.getClass().isValue() Rémi which was ad hoc and composed terribly. This hell is _exactly_ the thing that pushed us to Ref/ValObject as _types_ in the first place. (More of the same: what is “ref Object”.class, and how does it differ from “any Object”.class?). 2) reinterpret `Object` as `any Object` That is, the semantics of `Object` is being redefined here - code which assumed to work with references might need to opt-in to additional constraints (e.g. add `ref`) in order to make sure it still work as intended. Right. Q-world tried it the other way, and we were in utter migration hell. There are migration cases in this direction too, but we are convinced they are orders of magnitude fewer. I don't see another way out of this conundrum - other than adding a special rule (z2) which says that `new Object()` is treated specially and always has kind `ref`. But doing so will run afoul in almost every possible way - as soon as you manipulate the result of the `new` in any way (cast, assignment to variable of type `Object`, ...) you go back to `any` and you are back to a place that is incompatible with `ref Object`. Yes, this is the cost. I have to think that given a choice between some weirdness around ’new Object’, and dramatic, awful new kinds of types that complicate type uses, type descriptors, reflection, etc, etc, etc, that making the former work is going to be less painful, both for us and for users. Your idea of treating Object as abstract is, I believ
Re: RefObject and ValObject
Please, no. > On Apr 15, 2019, at 11:03 AM, Remi Forax wrote: > > > > De: "Brian Goetz" > À: "Maurizio Cimadamore" > Cc: "valhalla-spec-experts" > Envoyé: Lundi 15 Avril 2019 16:38:58 > Objet: Re: RefObject and ValObject > Let's model the value vs. reference constraint explicitly - e.g. with 'val' > and 'ref' type kinds (and let's not open the can worm as to what the syntax > should be, whether it should be a type anno, etc.) > > So: > > val Object ---> accepts all values > ref Object ---> accepts all references > any Object ---> accepts all references > > > We explored this sort of thing in Q world. One place where this really was > painful was what it did to generic tvar constraints; we had an entire new > algebra of constraints: > > void m(T t) { … } > > Brian, > val, ref and any only applies to Object. > > if you prefer in term of notation, let's rename ref Object to Object?, val > Object to Object! and any Object to Object*, > > you can apply ? on any value type, Object included, > you can apply ! and * only on Object. > > Object? is erased to Ljava/lang/Object; by the generic signature is > Ljava/lang/Object/* (so it's Object for the VM and Object! for the compiler), > Object! is erased to Qjav/lang/Object; > Object* is erased to Ljava/lang/Object; > > As a bound of a type variable, Object is equivalent to Object? by backward > compatibility. > and >void m(T t) { … } > means that T is not nullable, so it's a value type. > > And at runtime, instead of >(o instanceof Object!) > one will write > o.getClass().isValue() > > Rémi > > > which was ad hoc and composed terribly. This hell is _exactly_ the thing > that pushed us to Ref/ValObject as _types_ in the first place. (More of the > same: what is “ref Object”.class, and how does it differ from “any > Object”.class?). > > 2) reinterpret `Object` as `any Object` > > That is, the semantics of `Object` is being redefined here - code which > assumed to work with references might need to opt-in to additional > constraints (e.g. add `ref`) in order to make sure it still work as intended. > > Right. Q-world tried it the other way, and we were in utter migration hell. > There are migration cases in this direction too, but we are convinced they > are orders of magnitude fewer. > I don't see another way out of this conundrum - other than adding a special > rule (z2) which says that `new Object()` is treated specially and always has > kind `ref`. But doing so will run afoul in almost every possible way - as > soon as you manipulate the result of the `new` in any way (cast, assignment > to variable of type `Object`, ...) you go back to `any` and you are back to a > place that is incompatible with `ref Object`. > > Yes, this is the cost. I have to think that given a choice between some > weirdness around ’new Object’, and dramatic, awful new kinds of types that > complicate type uses, type descriptors, reflection, etc, etc, etc, that > making the former work is going to be less painful, both for us and for users. > > Your idea of treating Object as abstract is, I believe, a sound one (which > doesn't need any extra rule) - but we might have to figure out some story for > anonymous inner classes of the kind `new Object() { ... }`. > > > Right. And, again, this can be treated as a migration issue, and we can > start warning users to migrate their source now.
Re: RefObject and ValObject
> > Your idea of treating Object as abstract is, I believe, a sound one (which > doesn't need any extra rule) - but we might have to figure out some story for > anonymous inner classes of the kind `new Object() { ... }`. After thinking about it for all of five minutes, I think this may have broken the logjam (or, at least the current logjam.). We’ve been asking ourselves whether RO/VO are classes or interfaces, when we didn’t really consider abstract classes. Which we didn’t consider because we had assumed that the concrete-ness of Object was nailed down. Let’s assume it’s not. Then we have: abstract class Object { } abstract class RefObject <: Object { } abstract class ValObject <: Object { } Existing classes that extend Object are silently reparented to RefObject, both at compile time and runtime. This may have some small .getSuperclass() anomalies but this seems pretty minor. Same with anon classes of Object. Inline classes implicitly extend ValObject. We add a method `Object::newInstance` (name to be bikeshod later.). We start warning in the compiler on `new Object`, to motivate migration to `Object::newInstance`. Runtime rewrites these too. There are some minor behavioral compatibility issues here, but they seem pretty minor, and in the end, we end up with a hierarchy that describes the way we want users to see the type system.
Re: RefObject and ValObject
> De: "Brian Goetz" > À: "Maurizio Cimadamore" > Cc: "valhalla-spec-experts" > Envoyé: Lundi 15 Avril 2019 16:38:58 > Objet: Re: RefObject and ValObject >> Let's model the value vs. reference constraint explicitly - e.g. with 'val' >> and >> 'ref' type kinds (and let's not open the can worm as to what the syntax >> should >> be, whether it should be a type anno, etc.) >> So: >> val Object ---> accepts all values >> ref Object ---> accepts all references >> any Object ---> accepts all references > We explored this sort of thing in Q world. One place where this really was > painful was what it did to generic tvar constraints; we had an entire new > algebra of constraints: > void m(T t) { … } Brian, val, ref and any only applies to Object. if you prefer in term of notation, let's rename ref Object to Object?, val Object to Object! and any Object to Object*, you can apply ? on any value type, Object included, you can apply ! and * only on Object. Object? is erased to Ljava/lang/Object; by the generic signature is Ljava/lang/Object/* (so it's Object for the VM and Object! for the compiler), Object! is erased to Qjav/lang/Object; Object* is erased to Ljava/lang/Object; As a bound of a type variable, Object is equivalent to Object? by backward compatibility. and void m(T t) { … } means that T is not nullable, so it's a value type. And at runtime, instead of (o instanceof Object!) one will write o.getClass().isValue() Rémi > which was ad hoc and composed terribly. This hell is _exactly_ the thing that > pushed us to Ref/ValObject as _types_ in the first place. (More of the same: > what is “ref Object”.class, and how does it differ from “any Object”.class?). >> 2) reinterpret `Object` as `any Object` >> That is, the semantics of `Object` is being redefined here - code which >> assumed >> to work with references might need to opt-in to additional constraints (e.g. >> add `ref`) in order to make sure it still work as intended. > Right. Q-world tried it the other way, and we were in utter migration hell. > There are migration cases in this direction too, but we are convinced they are > orders of magnitude fewer. >> I don't see another way out of this conundrum - other than adding a special >> rule >> (z2) which says that `new Object()` is treated specially and always has kind >> `ref`. But doing so will run afoul in almost every possible way - as soon as >> you manipulate the result of the `new` in any way (cast, assignment to >> variable >> of type `Object`, ...) you go back to `any` and you are back to a place that >> is >> incompatible with `ref Object`. > Yes, this is the cost. I have to think that given a choice between some > weirdness around ’new Object’, and dramatic, awful new kinds of types that > complicate type uses, type descriptors, reflection, etc, etc, etc, that making > the former work is going to be less painful, both for us and for users. >> Your idea of treating Object as abstract is, I believe, a sound one (which >> doesn't need any extra rule) - but we might have to figure out some story for >> anonymous inner classes of the kind `new Object() { ... }`. > Right. And, again, this can be treated as a migration issue, and we can start > warning users to migrate their source now.
Re: RefObject and ValObject
> > Well, not quite. The choice is between treating 'new Object' specially or > not. If it's not treated specially there is no new scary type. It's just > that`new Object` might not have the properties one might hope for (but again, > those same properties would be lost as soon as you touch the result of the > expression). OK, but let’s not lose sight of the fact that “new Object()” is not so much a language _feature_, as much as an accidental convention that people have settled on for accomplishing a very specific thing. In a way, it’s like the so-called-but-not-really-a-feature double-brace idiom; it’s an accidental consequence of how the language works, that people have come to use because it’s convenient. I would much rather spend our complexity budget migrating uses of this idiom away and get a simpler type system, than the other way around.
Re: RefObject and ValObject
On 15/04/2019 15:38, Brian Goetz wrote: Let's model the value vs. reference constraint explicitly - e.g. with 'val' and 'ref' type kinds (and let's not open the can worm as to what the syntax should be, whether it should be a type anno, etc.) So: val Object ---> accepts all values ref Object ---> accepts all references any Object ---> accepts all references We explored this sort of thing in Q world. One place where this really was painful was what it did to generic tvar constraints; we had an entire new algebra of constraints: void m(T t) { … } which was ad hoc and composed terribly. This hell is _exactly_ the thing that pushed us to Ref/ValObject as _types_ in the first place. (More of the same: what is “ref Object”.class, and how does it differ from “any Object”.class?). This is not a language proposal (as stated in the email). Just a way to be clearer about semantics, w/o appealing to subclasses and type hierarchies (which are, at this point, IMHO confusing). 2) reinterpret `Object` as `any Object` That is, the semantics of `Object` is being redefined here - code which assumed to work with references might need to opt-in to additional constraints (e.g. add `ref`) in order to make sure it still work as intended. Right. Q-world tried it the other way, and we were in utter migration hell. There are migration cases in this direction too, but we are convinced they are orders of magnitude fewer. I don't see another way out of this conundrum - other than adding a special rule (z2) which says that `new Object()` is treated specially and always has kind `ref`. But doing so will run afoul in almost every possible way - as soon as you manipulate the result of the `new` in any way (cast, assignment to variable of type `Object`, ...) you go back to `any` and you are back to a place that is incompatible with `ref Object`. Yes, this is the cost. I have to think that given a choice between some weirdness around ’new Object’, and dramatic, awful new kinds of types that complicate type uses, type descriptors, reflection, etc, etc, etc, that making the former work is going to be less painful, both for us and for users. Well, not quite. The choice is between treating 'new Object' specially or not. If it's not treated specially there is no new scary type. It's just that`new Object` might not have the properties one might hope for (but again, those same properties would be lost as soon as you touch the result of the expression). I think you took my type modifiers too literally (as if, I'm proposing to add them, which I'm not) :-) Maurizio Your idea of treating Object as abstract is, I believe, a sound one (which doesn't need any extra rule) - but we might have to figure out some story for anonymous inner classes of the kind `new Object() { ... }`. Right. And, again, this can be treated as a migration issue, and we can start warning users to migrate their source now.
Re: RefObject and ValObject
> Let's model the value vs. reference constraint explicitly - e.g. with 'val' > and 'ref' type kinds (and let's not open the can worm as to what the syntax > should be, whether it should be a type anno, etc.) > > So: > > val Object ---> accepts all values > ref Object ---> accepts all references > any Object ---> accepts all references > We explored this sort of thing in Q world. One place where this really was painful was what it did to generic tvar constraints; we had an entire new algebra of constraints: void m(T t) { … } which was ad hoc and composed terribly. This hell is _exactly_ the thing that pushed us to Ref/ValObject as _types_ in the first place. (More of the same: what is “ref Object”.class, and how does it differ from “any Object”.class?). > 2) reinterpret `Object` as `any Object` > > That is, the semantics of `Object` is being redefined here - code which > assumed to work with references might need to opt-in to additional > constraints (e.g. add `ref`) in order to make sure it still work as intended. > Right. Q-world tried it the other way, and we were in utter migration hell. There are migration cases in this direction too, but we are convinced they are orders of magnitude fewer. > I don't see another way out of this conundrum - other than adding a special > rule (z2) which says that `new Object()` is treated specially and always has > kind `ref`. But doing so will run afoul in almost every possible way - as > soon as you manipulate the result of the `new` in any way (cast, assignment > to variable of type `Object`, ...) you go back to `any` and you are back to a > place that is incompatible with `ref Object`. > Yes, this is the cost. I have to think that given a choice between some weirdness around ’new Object’, and dramatic, awful new kinds of types that complicate type uses, type descriptors, reflection, etc, etc, etc, that making the former work is going to be less painful, both for us and for users. > Your idea of treating Object as abstract is, I believe, a sound one (which > doesn't need any extra rule) - but we might have to figure out some story for > anonymous inner classes of the kind `new Object() { ... }`. > Right. And, again, this can be treated as a migration issue, and we can start warning users to migrate their source now.
Re: RefObject and ValObject
Mu suggestion here is to leave subtyping on the side, at least for now and use some other way to describe what's going on. Let's model the value vs. reference constraint explicitly - e.g. with 'val' and 'ref' type kinds (and let's not open the can worm as to what the syntax should be, whether it should be a type anno, etc.) So: val Object ---> accepts all values ref Object ---> accepts all references any Object ---> accepts all references Now that everything is explicit, for types we have two possible moves: 1) reinterpret `Object` as `ref Object` This will keep semantics as is - that is, upon recompilation, if source code doesn't change, a program that expected references cannot start receiving value parameters. If code wants to work on both references and values, it will have to opt in (by using `any`). 2) reinterpret `Object` as `any Object` That is, the semantics of `Object` is being redefined here - code which assumed to work with references might need to opt-in to additional constraints (e.g. add `ref`) in order to make sure it still work as intended. I think we are leaning towards (2) - that is, we want meaning of Object to be upgraded, and we want users that are not happy with that to opt out in some form. Ok, now let's think about expressions; given an expression E, I have to figure out (i) its type and its (ii) kind, since the type system I'm describing here takes both factors into account. Here I'm expecting rules of the kind: a) if E is an identifier pointing to a variable decl, then type and kind are derived from the declaration b) if E is a method call, where declared method is M, type and kind are derived from M's return type declaration ... z) if E is a new expression, of the kind `new T()`, the type is T and the kind can be either `ref` or `val` depending on whether T is a reference class or not. If T can be both, then kind `any` is inferred. So, we can use (z) e.g. to say that `new String()` has kind `ref`. But, if we want Object to be the top type for both values and references, I believe one consequence is that `new Object` is interpreted as `any` which means you cannot pass it to `ref Object`. I don't see another way out of this conundrum - other than adding a special rule (z2) which says that `new Object()` is treated specially and always has kind `ref`. But doing so will run afoul in almost every possible way - as soon as you manipulate the result of the `new` in any way (cast, assignment to variable of type `Object`, ...) you go back to `any` and you are back to a place that is incompatible with `ref Object`. Your idea of treating Object as abstract is, I believe, a sound one (which doesn't need any extra rule) - but we might have to figure out some story for anonymous inner classes of the kind `new Object() { ... }`. Maurizio On 15/04/2019 14:26, Brian Goetz wrote: But the other concerns remain, e.g. as to the fact that the boundary between reinterpreted types (Object as RefObject) and non-reinterpreted types (Object as top type) seems very fuzzy. Right, which is why we’re still searching for an answer :) We really, really want to be able to represent ref/val-ness in the type system. Why? Ignoring pedagogical concerns (which are significant): - If certain operations (e.g., locking) are partial, we want to be able to provide a way to ask if the operation could succeed. Such as: if (x instanceof RefObejct) { … lock on x … } Saying “lock, and hope it doesn’t throw” is not a very good answer. We already have a tool for querying the dynamic type of an object — instanceof. - Saying that a method should only accept reference objects should be something expressible in the method signature, as in m(RefObject o) { … } Types are how we do that. - Similarly, we might want to express the above constraint generically; again, types are the way we do that: class Foo { } And, Q-world already taught us that we wanted to retain Object as the top type. This mean, necessarily, that Object gets a little weirder; it takes on some partly-class, partly-interface behavior. Here’s an idea: What if we migrated `Object` to be an abstract class? The casualty would be the code that says `new Object()`. While there’s certainly a lot of it out there, perhaps this is something amenable to migration: - At the source level, for N versions, `new Object()` gets a warning that says “I’ll pretend you said `Object.newLockInstance()` or something. - At the bytemode level, for M versions, we do something similar, likely for M > N. We can start this now, before Valhalla even previews.
Re: RefObject and ValObject
> But the other concerns remain, e.g. as to the fact that the boundary between > reinterpreted types (Object as RefObject) and non-reinterpreted types (Object > as top type) seems very fuzzy. Right, which is why we’re still searching for an answer :) We really, really want to be able to represent ref/val-ness in the type system. Why? Ignoring pedagogical concerns (which are significant): - If certain operations (e.g., locking) are partial, we want to be able to provide a way to ask if the operation could succeed. Such as: if (x instanceof RefObejct) { … lock on x … } Saying “lock, and hope it doesn’t throw” is not a very good answer. We already have a tool for querying the dynamic type of an object — instanceof. - Saying that a method should only accept reference objects should be something expressible in the method signature, as in m(RefObject o) { … } Types are how we do that. - Similarly, we might want to express the above constraint generically; again, types are the way we do that: class Foo { } And, Q-world already taught us that we wanted to retain Object as the top type. This mean, necessarily, that Object gets a little weirder; it takes on some partly-class, partly-interface behavior. Here’s an idea: What if we migrated `Object` to be an abstract class? The casualty would be the code that says `new Object()`. While there’s certainly a lot of it out there, perhaps this is something amenable to migration: - At the source level, for N versions, `new Object()` gets a warning that says “I’ll pretend you said `Object.newLockInstance()` or something. - At the bytemode level, for M versions, we do something similar, likely for M > N. We can start this now, before Valhalla even previews.
Re: RefObject and ValObject
- Mail original - > De: "Maurizio Cimadamore" > À: "Brian Goetz" , "valhalla-spec-experts" > > Envoyé: Lundi 15 Avril 2019 14:20:35 > Objet: Re: RefObject and ValObject > On 15/04/2019 13:06, Brian Goetz wrote: >> >> >>> But it seems like we have already ruled this out - since, if >>> typeof(new Object()) is 'RefObject', you don't want RefObject <: Object. >> >> I think you misunderstood the "want" here (though it still may not be >> possible.) >> >> The desired model is: >> - Object is the top type. Everything is an Object. >> - Some objects have identity (RefObject), others do not (ValObject). >> But they are all Object. >> >> This means we want {Ref,Val}Object <: Object. (Whether they are >> interface or class or something else.) > > Seems like I've read this requirement: > > "but we don’t want Object <: RefObject for obvious reasons" > > backwards. So, what this means is that it would be type-sound regardless > of interface vs. class choice. But the other concerns remain, e.g. as to > the fact that the boundary between reinterpreted types (Object as > RefObject) and non-reinterpreted types (Object as top type) seems very > fuzzy. yes, divorcing the runtime class from the type is something we can do, but it's not because we can do that we should, as a teacher you usually don't want to talk about the difference between a class and a type until you have reached the subtyping chapter. The initial goal is to make the concept of ref type and value type easier to grasp by providing a simple hierarchy, but new Object() can not be retconed to a RefObject easily, so it's not that simple. And Ruby has tried to do something similar by introducing BasicObject in 1.9 (for another reason, because scopes are liked to classes in Ruby), at the end few people cares, so we have to be careful to not introduce something that will be a hurdle in compatibility for in the end no benefit. So we need to be able to have types representing any Object, ref Object and value Object, but i think that RefObject and ValObject are not the only solution for that. > > Maurizio Rémi > >> >> One of the main reasons for wanting this setup is that it reflects the >> desired reality: everything is an object, but some are special objects >> (those with identity.) The addition of value types is a big >> perturbation to the type system; reflecting it this way makes the >> object hierarchy reflect the reality and the desired intuition, and >> makes the distinction between ref/val slightly less magic. >> >> (There are other reasons too; for example, wouldn't it be nice if >> ValObject.{wait,notify,notifyAll} were _ordinary final methods_ that >> threw in ValObject? Again, slightly less magic.) >>
Re: RefObject and ValObject
On 15/04/2019 13:06, Brian Goetz wrote: But it seems like we have already ruled this out - since, if typeof(new Object()) is 'RefObject', you don't want RefObject <: Object. I think you misunderstood the "want" here (though it still may not be possible.) The desired model is: - Object is the top type. Everything is an Object. - Some objects have identity (RefObject), others do not (ValObject). But they are all Object. This means we want {Ref,Val}Object <: Object. (Whether they are interface or class or something else.) Seems like I've read this requirement: "but we don’t want Object <: RefObject for obvious reasons" backwards. So, what this means is that it would be type-sound regardless of interface vs. class choice. But the other concerns remain, e.g. as to the fact that the boundary between reinterpreted types (Object as RefObject) and non-reinterpreted types (Object as top type) seems very fuzzy. Maurizio One of the main reasons for wanting this setup is that it reflects the desired reality: everything is an object, but some are special objects (those with identity.) The addition of value types is a big perturbation to the type system; reflecting it this way makes the object hierarchy reflect the reality and the desired intuition, and makes the distinction between ref/val slightly less magic. (There are other reasons too; for example, wouldn't it be nice if ValObject.{wait,notify,notifyAll} were _ordinary final methods_ that threw in ValObject? Again, slightly less magic.)
Re: RefObject and ValObject
But it seems like we have already ruled this out - since, if typeof(new Object()) is 'RefObject', you don't want RefObject <: Object. I think you misunderstood the "want" here (though it still may not be possible.) The desired model is: - Object is the top type. Everything is an Object. - Some objects have identity (RefObject), others do not (ValObject). But they are all Object. This means we want {Ref,Val}Object <: Object. (Whether they are interface or class or something else.) One of the main reasons for wanting this setup is that it reflects the desired reality: everything is an object, but some are special objects (those with identity.) The addition of value types is a big perturbation to the type system; reflecting it this way makes the object hierarchy reflect the reality and the desired intuition, and makes the distinction between ref/val slightly less magic. (There are other reasons too; for example, wouldn't it be nice if ValObject.{wait,notify,notifyAll} were _ordinary final methods_ that threw in ValObject? Again, slightly less magic.)
Re: RefObject and ValObject
The gordian knot here is that whenever you see Object as part of a *type* you'd like to interpret it as a top type (the root of the hierarchy). OTOH, when you see Object as part of an *expression* you'd like to interpret it as something else (e.g. RefObject). And there are also things in between - e.g. Object.class, where it's not uber clear whether you want one or the other, but let's not get too distracted by these for the moment. From a type-system perspective, the basic property called _preservation_ mandate (Gavin, correct me if I'm wrong :-)) that, given an expression of *static type* E, the type associated with the value V obtained by executing the expression must be a subtype of E, which we can write: typeof(V) <: E (of course this is only true for type systems which feature subtyping, otherwise typeof(V) == E). So, in our case, the theorem demands something like this: typeof(new Object()) <: Object But it seems like we have already ruled this out - since, if typeof(new Object()) is 'RefObject', you don't want RefObject <: Object. So, from a type-system perspective, we're on unsound territory, at least assuming we only use classes w/ single inheritance. Interfaces (or, more generally, multiple inheritance) add a bit of flexibility because (as Brian said) we could say: typeof(new Object()) = XYZ, where XYZ <: RefObject && XYZ <: Object So this would satisfy the type theory; whether it can be made into something that looks compelling for a Java user, that's another story. Note that the dual nature of *type* vs. *expression* mentioned at the beginning will bite you as soon as you start doing things like this: void m(RefObject ro) { ... } m(new Object()) // ok, as per above m((Object)new Object()) // not ok? m(Object.class.newInstance()) // WAT!? Maurizio On 08/04/2019 20:58, Brian Goetz wrote: No matter which way we go, we end up with an odd anomaly: “new Object()” should yield an instance of RefObject, but we don’t want Object <: RefObject for obvious reasons. Its possible that “new Object()” could result in an instance of a_species_ of Object that implement RefObject… but our theory of species doesn’t quite go there and it seems a little silly to add new requirements just for this.