JEP 445 (Unnamed classes): It’s.. still weird (structurally typed).
Reinier Zwitserloot
reinier at projectlombok.org
Fri May 5 21:06:38 UTC 2023
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/20230505/c0d0306b/attachment.htm>
More information about the amber-dev
mailing list