Is case var(var x, var y) a valid syntax ?

forax at univ-mlv.fr forax at univ-mlv.fr
Sun Sep 13 18:20:15 UTC 2020


> De: "Brian Goetz" <brian.goetz at oracle.com>
> À: "Remi Forax" <forax at univ-mlv.fr>
> Cc: "amber-spec-experts" <amber-spec-experts at openjdk.java.net>
> Envoyé: Dimanche 13 Septembre 2020 15:42:39
> Objet: Re: Is case var(var x, var y) a valid syntax ?

>>> - While instance members, they are not inherited (just like constructors)

>> At least you want a deconstructor to be overrideable (which is not fully
>> equivalent to being inherited).
>> A deconstructor is for allowing encapsulation so the world projected by a
>> deconstructor may have a little to share with how the the class are implemented

>> class Employee {
>> int baseSalary;

>> deconstructor Employee(int salary) { return (baseSalary); }
>> }
>> class VP extends Employee {
>> int bonus;

>> deconstructor VP(int salary) { return (baseSalary + bonus); }
>> }

>> ...
>> Vp vp = new VP();
>> vp.setBaseSalary(2000);
>> vp.setBonus(500);
>> Employee employee = vp;
>> if (employee instanceof Employee(salary) {
>> System.out.println(salary); // 2500
>> }

>> and here you can see that Employee(salary) is not a call to the deconstructor
>> but an instanceof Employee + a call to the deconstructor (int salary) !

> I think what you are alluding to here is the idea that a deconstructor is like a
> “multi-accessor”, and accessors are virtual but deconstructors are not.

> But the example is distorted for two reasons; this is already a questionable
> deconstructor API, and even if so, Employee is conflicted about the distinction
> between salary and baseSalary.

if deconstructors are not virtual, Is the salary printed is 2000 or the compiler raises an error somewhere ? 

> So I’m not sure how much we can learn from this particular example. Maybe you
> have a better one?

No a better one, just another one, you want to be able to declare a deconstructor abstract by example on an interface 
interface Map { 
interface Entry<K,V> { 
public abstract deconstructor Entry(K key, V value); 
} 
} 

... 
for(Map.Entry<String, String>(var key, var value) : entries) { 
... 
} 

BTW, it's also an example where 'var' can be useful instead of having to specify the full type 
for(var(var key, var value) : entries) { 
... 
} 

>>> - They can only be called via a pattern match (just as a constructor can only be
>>> called via a `new` operation.)

>> so unlike a constructor that can be called either by a new or by this(...) and
>> super(...) a deconstructor can only be called via pattern matching.

> You should re-read the document about deconstructors, as this symmetry is well
> covered. The case of one deconstructor delegating to another, just like one
> constructor delegating to another, is important, because we want each class to
> be responsible for its own state. So yes, this is covered. (Technically,
> though, this sort of delegated invocation _is_ a pattern match, so the
> statement “only through pattern matching” still stands.)

I get that you can write the code like this if baseSalary is declared private in Employee 

class VP extends Employee { 
int bonus; 

deconstructor VP(int salary) { 
super(var baseSalary) = this; 
return (baseSalary + bonus); 
} 
} 

still, a deconstructor is unlike a constructor because you can call a constructor directly something you can not do with a deconstructor. 

>>> In this way, both ctor and dtor mediate access between an external API and the
>>> internal representation.

>> It's only true for a constructor if the constructor (constructors) are the only
>> way to change the value of an instance.
>> It's only true for a deconstructor if the deconstructor has a matching
>> constructor

> Both of these “only true” claims are not true :)

> You can have multiple constructors with different views of the state
> (overloading), and these views could be overlapping or non-overlapping. And you
> can have multiple deconstructors with different views of the state too. And
> have the choice to align the constructor / deconstructor views, or not. You can
> have matching ctor/dtor, or asymmetric ones — this is a matter of API design.

so why my first example is questionable usage of the deconstructor API. 

> We anticipate it will be common to provide matched ctor/dtor pairs, because
> together these form an adjunction between the state space of the object and the
> external API shared between ctor/dtor, which is a useful and practical building
> block.

> Even for mutable objects, deconstructors are still sensible.

> class C {
> int x;

> C(int x) { this.x = x; }
> deconstructor C(int x) { x = this.x; }

> void setX(int x) { this.x = x; }
> }

> I can do:

> C c = new C(3);
> c.setX(4);
> if (c instanceof C(var x)) { … x is 4 here … }

> The deconstructor is free to track the state, mutable or not. Again, this is a
> tool for API design, and it can be used in multiple ways. The record case is
> notable because records have a highly constrained API and they are not
> extensible, so they are the best behaved of the bunch. But classes can play
> this game too.

and now introduce inheritance to the mix and you will see that a deconstructor has to be virtual. 

Basically, we are at the point where it's not useful anymore to call it a deconstructor because while it's like the reverse of a constructor, at the same time it's also close to an API like clone(). 

Rémi 
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.java.net/pipermail/amber-spec-experts/attachments/20200913/b34597da/attachment-0001.htm>


More information about the amber-spec-experts mailing list