Re: JEP 445 (Unnamed classes): It’s.. still weird (structurally typed).

Brian Goetz brian.goetz at oracle.com
Tue May 9 17:42:19 UTC 2023


Yes, when the glass is 99% full, it is often tempting to focus on that 
last 1%.  This is natural.

The points you raise here are sensible, but you sort of answer your own 
question: the TerminalInteractive interface is a significant exercise in 
API design, which would likely run into other tradeoffs that we don't 
love.  We are not a mere small step away from "closing that last 1%", we 
are reopening a whole can of worms, with a pretty limited payback (and 
one for which its not even obvious it meets the "beginner-friendly" 
requirement.)

Stephen tried a similar exploration with "entry points", and ran into a 
similar degree of "poor return on incremental complexity."

As the "Paving" document outlined, this project started out with 
deliberately modest goals -- small adjustments for smoothing out the 
main class declaration and launch model, not "design a new launch 
model."  This was not a mere budgetary compromise (though we are ever 
mindful that every minute spent on one project is a minute not spent on 
another, and that we're working with limited complexity budgets in 
multiple dimensions), but also a recognition of the fact that there are 
some practical aspects here and some spiral-off-into-blue-sky aspects, 
and we will be resisting the temptation of the latter.  We already 
thought through a number of the "could we go farther" prior to proposing 
the project (including all the things you have suggested, and more), and 
concluded that this was the sweet spot.

> These concepts can be handwaved away as out of scope, but if that’s 
> done I don’t think this JEP fully accomplishes what it should.

This is a valid opinion!  But I don't think you've fully thought out 
what you're saying.  (Don't worry, though, you'd be in good company; the 
opinion of "I think you should do more" is common and commonly 
expressed, and most folks who do so make the same mistake.)

You think that by expressing this opinion, that the only reasonable 
responses on our part are either:

  A: Great, you convinced us, we'll do as you ask
  B: Yes, you're right, but we're going to do the wrong thing anyway

What you (and others who play this card) don't realize is: you are 
actually arguing for "Let's cancel this project right now, because what 
we can reasonably deliver is not good enough."

It's fine to express "I wish we could do more", or even "Here's 
something more that I think we could do at minimal incremental cost."  
But when you frame the argument as "if you don't do more, you have 
failed" (that's what "doesn't accomplish what it should" means), you are 
arguing "let's cancel the project."

Personally, I don't think this project is remotely a failure if we stop 
where we set out to stop.  But I recognize that others might feel 
differently.  But if you mean to be pulling the emergency brake cord, 
you should be clear about that!

> the very first API they run into (Namely, Scanner)

I don't agree that every student will run into Scanner so early, but I 
do agree that Scanner could use a redesign.  But that doesn't need to be 
coupled to this JEP; that's a straightforward library improvement that 
can be an ordinary RFE, and surely doesn't need language support.

> the rest of JEP445 doesn’t seem to accomplish meaningful change

because JEP 445 is focused on the language aspects.  The library aspects 
are separate.









On 5/5/2023 5:06 PM, Reinier Zwitserloot wrote:
> JEP 445 does a ton of work cleaning up that experience of ‘meeting 
> java for the first time’. I fear the herculean effort required for the 
> bazillion tutorials and books out there to adopt this new style, but 
> no time like the present to start that work.
>
> But, java is just about the least structurally typed language I’m 
> aware of. It even insists functions have a type name (lambdas aren’t 
> legit unless context allows the compiler to treat them as a functional 
> interface).
>
> There are only 4 exceptions baked into java itself:
>
> (1) main().
>
> (2) agentmain().
>
> (3) serialization, which brings in 5x structural typing 
> (serialVersionUID field, writeReplace, readResolve, writeObject, and 
> readObject).
>
> (4) The beanspec. Clearly the JDK doesn’t care about it given that 
> records decided to go with |property()| instead of |getProperty()|.
>
> Given the repeated strong discouragement from OpenJDK about those last 
> 2, that really just leaves main and agentmain, and given that agents 
> are more or less rocket science (in the sense that only quite seasoned 
> java programmers are likely to ever get assigned the job of writing an 
> agent), that really *just leaves main() as the one and only place in 
> the entire java ecosystem where structural typing is a thing*.
>
> Which is really, really, weird. It’s the very first method someone 
> being introduced to java is ever going to write, and it therefore 
> -immediately- sends new programmers down the wrong path. When you meet 
> java for the first time, you meet an aspect of java that is literally 
> not how anything else in java works.
>
> The /reasons/ for it are obvious enough: Like |switch| syntax, it’s a 
> holdover from C - it was familiar to C programmers, java did indeed 
> manage to become one of the most popular languages in fairly short 
> order and during a time when C was incredibly popular, so this is no 
> complaint about java’s past choices: Who knows where java would be 
> today if it hadn’t chosen to emulate C syntax as much as possible even 
> in cases where it was a highly dubious choice, even in historical 
> context (who likes original switch syntax? Surely that was always 
> horrible. This structural typing thing similarly is just bizarre).
>
> But, if a JEP comes along to fix it, let’s… fix it. If possible. Which 
> may just be too difficult, in the sense that fixing it requires even 
> more deviation from what java programmers are used to. Then again, the 
> point of JEP445 is surely mostly about those new to java and far less 
> to cater to the needs of seasoned java programmers who are just 
> slapping together a quick one-off script in java; they have tools that 
> generate this stuff for you and/or have jshell or similar already 
> installed if they run into this situation of needing a quickly 
> fire-and-forget stand-alone java app more than once in a blue moon.
>
> Should look something like:
> package java.lang;
> public interface Application {
>   void main() throws Exception;
> }
>
> and to use, of course:
>
> class MyApp implements Application {
>   public void main() {
>   }
> }
>
> An obvious issue here is that it eliminates some of the gains JEP445 
> just set up for us. Even taking into account that in the vein of 
> JEP445, |class MyApp implements Application| is made optional, the 
> lang spec still requires you to make that |main| method public. 
> Decreeing via JEP that you don’t have to is perhaps pushing things a 
> little too close into the “voodoo magic” column.
>
> But, it does open up an opportunity to fix the rest of those first 
> minutes of meeting java in a far cleaner fashion.
>
> |Scanner| is 90%+ suggested as the thing to use when writing 
> keyboard-interactive apps, and keyboard-interactive apps are 90%+ of 
> all first-steps java apps. SOURCE: Answering a ton of questions on 
> stack overflow, and searching the web for some java tutorials, 
> /all/ of which mention Scanner very early on. Which is a problem - 
> because as far as I can tell Scanner was never designed primarily for 
> keyboard input! - Or at least I hope not or it can vie for the dubious 
> honour of being the most unfit-for-stated-purpose core API library vs, 
> I guess, |java.util.Calendar| or some other sordid historic artefact 
> of the core libs.
>
> If |Application| is set up to look something like this:
>
> public interface TerminalInteractive {
>   int promptInt(String prompt);
>   String promptString(String prompt);
>   String promptPassword(String prompt);
>   void print(String text); // no more need for printf due to JEP about 
> formatters, maybe?
>   void exit(int returnCode);
>   // you get the idea
>   // possibly some command line parsing tooling is appropriate here, too?
> }
>
> and Application itself is turned into:
>
> public abstract class Application {
>   protected TerminalInteractive terminal;
>   protected abstract void main();
> }
>
>
> with an additional rule that on boot a JVM will properly initialize 
> the |terminal| field (or terminal is exposed in some other fashion to 
> Application classes, the above is to serve as inspiration and not a 
> fully fleshed out proposal), all those pesky issues with first-steps 
> java (or at least, 90%+ of what beginners are asking questions about) 
> disappear. It also gets rid of some more utterly unlike anything else 
> in the entire ecosystem aspects of java, such as |System.in| and 
> |System.out| and |System.err| which are public static fields (very 
> rare in the java ecosystem), marked final, which are nevertheless not 
> at all final but you have to modify them using |System.setOut| and 
> friends which is - unique. In a bad way. Even just writing 
> |System.out.println| ends up not looking like anything else in the 
> ecosystem and is sending folks learning java down the wrong path. 
> Nothing in java is like this. If you start designing APIs, do not 
> design them like |System.out|.
>
> I’m not sure it’s worthwhile talking about simplifying main classes if 
> ‘how does one emit text and read stuff from the command line’ is not 
> just ‘out of scope’ but ‘beyond even considering’, given that it seems 
> self evident that virtually all main apps break down into one of two 
> camps:
>
> (1) They are merely the entrypoint to hundreds of thousands+ lines of 
> code, such as a webserver that reads in routing tables and starts 
> serving, or, say, Eclipse, or IntelliJ. Trying to simplify them is 
> irrelevant; not at all what JEP445 is targeting, surely.
>
> (2) A very simple app, possibly (probably) somebody learning java. In 
> which case terminal interaction is virtually guaranteed to be very 
> relevant.
>
>
> These concepts can be handwaved away as out of scope, but if that’s 
> done I don’t think this JEP fully accomplishes what it should. 
> Teaching someone how to write ‘hello world’ in java will still involve 
> /multiple/ completely bizarre (in the sense that /nothing/ in the java 
> ecosystem works like that) concepts. And will soon lead to very 
> commonly asked questions because the very first API they run into 
> (Namely, Scanner) is very badly written API for the needs that these 
> first steps programmers have. JEP445 as-is is still useful: Getting 
> rid of |static| is /very/ worthwhile (again, dealing with newbie 
> questions is the primary source for this, as |static| causes a ton of 
> confusion and leads very significant first-steps java programmers down 
> the wrong path, making everything they write |static| because if they 
> do not “the compiler complains about 'something something something 
> static context’"), but other than allowing |main| to be non-static, 
> the rest of JEP445 doesn’t seem to accomplish meaningful change 
> without addressing structural typing and the API used to interact with 
> sysout/err/in.
>
> Treating the JEP as merely steps along the way to perfection feels 
> like making a choice that OpenJDK will later regret, by enshrining 
> into spec even further the notion of a structurally typed main method.
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/amber-dev/attachments/20230509/306a92af/attachment-0001.htm>


More information about the amber-dev mailing list