Ergonomic Issues with ARM blocks (original) (raw)

Neal Gafter neal at gafter.com
Sun Mar 29 11:06:45 PDT 2009


Here are some notes on ergonomic (usability) issues I found during my attempt to retrofit some code with ARM blocks based on its latest spec.

(1) "try" is not a natural keyword for the intended semantics.

The use of the try keyword for this construct is natural while migrating existing code, because the existing solution already requires a try-finally statement. But it is a non sequitur (in some cases shockingly inappropriate) when reading code or when writing new code. The problem is that it calls attention to the exceptional case rather than the normal case, distracting the reader from the flow of the business logic. That is the very problem this construct should be designed to solve. This violates the principle that a language construct should be designed to integrate well with the language as it appears with the addition, rather than being designed to fit into the language as it existed before the addition. The try statement is already the most complex statement form in Java; we should avoid making it even more complex. It would be better to use a new context-sensitive keyword to define a new construct. For example, one might select the syntax

identifier (DeclSeq) Statement

Where the identifier would be semantically restricted to "using". This form is unambiguous with the existing language. I believe it can be parsed without additional look-ahead by accepting a superset of the grammar and post-filtering. By selecting a syntax with no overlap to existing forms, a context-sensitive keyword would not break backward compatibility. Java 7 will already be using context-sensitive keyword(s) for the modularity extension, so the approach isn't novel.

(2) Checked exceptions from close() should be discarded for some clients.

Experience with the previous prototype <http://markmahieu.blogspot.com/2008/01/exception-handling-with-arm-blocks.html> of the previous (now 3-year-old) ARM proposal <http://docs.google.com/View?docid=dffxznxr_1nmsqkz&pli=1> raised issues of handling exceptions from closing resources (some APIs wanted exceptions from close() discarded, others did not). Others have raised the same concern this time around. At the time, Josh's position was "It was certainly my intention that the programmer not be required to deal with exceptions thrown when terminating a resource...". That works out very nicely for some clients, and is a killer for others. The latest proposal abandons the set of use cases well served by the previous specification, and attempts to serve the set of use cases where exceptions on termination are best addressed by the programmer. From my (admittedly limited) experience, I found more cases where it was appropriate to discard checked exceptions from close() than otherwise. A revision of the proposal addressing this was promised. The earlier solution was two distinct but related statement forms.

(3) The nesting of new behavior with old behavior in the try statement is a poor fit for many clients.

Another issue arises due to the nesting of new behavior with existing behavior in the try statement. By retrofitting the construct onto an existing statement form, the language has made a decision on the relative nesting of the two behaviors. Is the resource variable in scope in the catch block? Is it in scope in the finally block? Any particular answer to these questions is a good match for some clients and a poor match for others. Given the currently specified nesting, the following code becomes more awkward when retrofitted with the ARM construct:

LineNumberReader reader = getInput(); try { parseInput(reader); } catch (CharacterCodingException ex) { report("character encoding error at line " + reader.getLineNumber()); } finally { try { reader.close(); } catch (IOException ex) {} }

The proposal should not take a position on a "preferred" way of combining/nesting resource usage with catch and finally.

This problem is easily solved by separating the new construct from existing statement forms. Then the programmer can mix or match the separate language constructs as required for the application.

(4) The proposed construct doesn't retrofit onto many APIs in the profile of use-cases for which it was designed.

A separate conversation regarding an analysis of the use cases was moved off-list, ultimately identifying widely used APIs that ought to be retrofittable with this construct. See <http://docs.google.com/View?docid=ddv8ts74_1d7tz65fd&pageview=1&hgd=1&hl=en>. One result of that analysis was that the proposal is incompatible with a number of widely used types that ought to be retrofitted. A revision of the proposal addressing this was promised.

(5) Such ergonomic issues require significant time and experience to resolve

A language construct that affects so many distinct APIs with varying patterns of use requires a significant body of experience to build confidence that it is usable with them. What little experience we have with the present proposal suggests more work is needed. In addition, proposed (but not yet specified) solutions to the retrofitting issue and promised solutions to other issues are novel enough that they should not be adopted without a significant period of time for experience with them to develop, and for the language construct to evolve based on that experience. We would do a disservice to Java programmers by shoving an untried solution in a rush through a process designed to handle small, simple, noncontroversial changes.



More information about the coin-dev mailing list