Seeking review and comments on R/W tags
jdcrowley at gmail.com
Tue May 16 17:18:29 UTC 2017
I’m looking for some help to review a proposal.
For a while now I’ve been working toward the goal of providing declarative immutability in Java - by having Read/Write tags on variables, references, methods, and generics. (Similar, but more extensive, than the const keyword in some languages.)
Things have progressed to the point that a few more (expert) eyes would be appreciated to review and comment on whether this is feasible and desirable.
A short summary follows, but everything is available here - https://drive.google.com/open?id=0B9h3YMINZ271dWcyS1RjbEZLMHc <https://drive.google.com/open?id=0B9h3YMINZ271dWcyS1RjbEZLMHc>
A document with a complete description,
A modified (Java 8) compiler which implements the initial portion of the concept (the Report Card lists what is supported).
A README file which describes where everything is on this drive.
A very condensed description ….
Variables can be protected by making them private and using getters/setters to control access - but at the cost of complexity, additional code, and runtime overhead.
Object instances are a more severe problem - once anyone has a reference, they can modify any visible variable, and call mutating methods.
When calling a method on an instance, the caller does not know if that method mutates that instance (or some referenced instance).
A set of single-character tags is proposed - W = writable, R = read-only - that can be appended to variable definitions, types (references), methods, and generics (more below).
Variable declarations may have 2 tags - the first controls outsiders and the second the owner. (The owner is defined as the innermost context which declares the variable.) For example,
public Date lastActivity:RW = new Date()
specifies that the owner (the declaring class) may assign a new Date instance to lastActivity, but no outsider may. Even stronger,
public Date lastActivity:RR = new Date()
specifies that neither an outsider nor the owner may change the Date instance referenced by lastActivity (equivalent to the final keyword).
The remaining problem is that anyone can change the semantic value by lastActivity.setTime(….). This can be controlled by tags on the type specification:
public Date:RW lastActivity:RR = new Date()
where the Date:R portion specifies that an outsider cannot modify any visible variable in the target instance and can invoke only read-only methods (myMethod:R).
The owner may update the instance - presumably to update the timestamp of the last activity processed. (The owner of an instance is the context which instantiates that instance.)
This is a strong API definition - outsiders know that they may create and retain a local reference and it will always refer to the same instance, and the owner has been able to make the information public with the assurance that no one else can tamper.
(Note that if the variable were private with a getter, then all outsiders would have to invoke a method, and to be safe the owner must return a cloned instance.)
Methods and their parameters may also be tagged. myMethod:R specifies that myMethod does not modify the instance (or any internally referenced instance) - and this is validated by the compiler. myMethod(Date:R myParameter, …) considers the passed Date instance as read-only within the method. If the method is tagged as R, then all parameters must also be tagged as R (the compiler infers this for untagged parameters, an explicit W tag is an error).
Extending these tags to generics allows control at all levels of the declaration. Assume that a class Message… exists, then
specifies that outsiders may not modify the List (add/delete elements) nor mutate any Message within the list. The owner may do both - e.g. add new Messages to the List, update a Message when it is acknowledged, etc.
A final tag of P (= Pure) may also be attached to a type declaration - Date:P - and ensures that no mutable reference exists to this instance - it will be immutable throughout the application. Important information for any developer, and allows the compiler to utilize all possible optimizations.
As a last example, consider:
public Map:RW<Date:P, List:RW<Message:RW>> history:RR = new Map<Date, List<Message>>
which is a (live) history of all of the Messages processed on a given day (assumption: the HH:MM:SS.sss portion of the Date is zeroed).
This tells any user that the Map is live, the key (Date) is Pure (good for the key of a Map), and the List and Messages will be updated as events occur. The history variable will always reference the same Map. The compiler will flag any attempt by an outsider to modify anything at any level, and will also flag an attempt by the owner to use a Date key which is not immutable. (In a multi-threaded environment, synchronized versions of Map, List, and Message would be necessary.)
The modified compiler enforces most of the above at compile time - so it appears that implementation is feasible.
Key questions remain where review by outside experts is needed:
Is the syntax complete? Unambiguous?
Are the semantics complete? Are there reasonable coding paths which lead to contradictions? Is there any path where a readonly tag can be converted to a mutable tag?
Is this useful to the developer? Does it produce more robust code? Better APIs? Fewer LOC? Better runtime performance?
Thanks for your help,
More information about the valhalla-dev