<html><body><div style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000"><div><br></div><div><br></div><hr id="zwchr" data-marker="__DIVIDER__"><div data-marker="__HEADERS__"><blockquote style="border-left:2px solid #1010FF;margin-left:5px;padding-left:5px;color:#000;font-weight:normal;font-style:normal;text-decoration:none;font-family:Helvetica,Arial,sans-serif;font-size:12pt;"><b>From: </b>"Brian Goetz" <brian.goetz@oracle.com><br><b>To: </b>"amber-spec-experts" <amber-spec-experts@openjdk.java.net><br><b>Sent: </b>Wednesday, September 28, 2022 7:57:19 PM<br><b>Subject: </b>Paving the on-ramp<br></blockquote></div><div data-marker="__QUOTED_TEXT__"><blockquote style="border-left:2px solid #1010FF;margin-left:5px;padding-left:5px;color:#000;font-weight:normal;font-style:normal;text-decoration:none;font-family:Helvetica,Arial,sans-serif;font-size:12pt;"><font size="4"><font face="monospace">At various points, we've
explored the question of which program elements are most and
least helpful for students first learning Java. After
considering a number of alternatives over the years, I have a
simple proposal for smoothing the "on ramp" to Java programming,
while not creating new things to unlearn. <br><br>
Markdown source is below, HTML will appear soon at: <br><br><a class="moz-txt-link-freetext" href="https://openjdk.org/projects/amber/design-notes/on-ramp" target="_blank">https://openjdk.org/projects/amber/design-notes/on-ramp</a><br><br><br>
# Paving the on-ramp<br><br>
Java is one of the most widely taught programming languages in
the world. Tens<br>
of thousands of educators find that the imperative core of the
language combined<br>
with a straightforward standard library is a foundation that
students can<br>
comfortably learn on. Choosing Java gives educators many
degrees of freedom:<br>
they can situate students in `jshell` or Notepad or a
full-fledged IDE; they can<br>
teach imperative, object-oriented, functional, or hybrid
programming styles; and<br>
they can easily find libraries to interact with external data
and services. <br><br>
No language is perfect, and one of the most common complaints
about Java is that<br>
it is "too verbose" or has "too much ceremony." And
unfortunately, Java imposes<br>
its heaviest ceremony on those first learning the language, who
need and<br>
appreciate it the least. The declaration of a class and the
incantation of<br>
`public static void main` is pure mystery to a beginning
programmer. While<br>
these incantations have principled origins and serve a useful
organizing purpose<br>
in larger programs, they have the effect of placing obstacles in
the path of<br>
_becoming_ Java programmers. Educators constantly remind us of
the litany of<br>
complexity that students have to confront on Day 1 of class --
when they really<br>
just want to write their first program. <br><br>
As an amusing demonstration of this, in her JavaOne keynote
appearance in 2019,<br>
[Aimee Lucido](<a class="moz-txt-link-freetext" href="https://www.youtube.com/watch?v=BkPPFiXUwYk" target="_blank">https://www.youtube.com/watch?v=BkPPFiXUwYk</a>)
talked about when<br>
she learned to program in Java, and how her teacher performed a
rap song<br>
to help students memorize `"public static void main"`. Our hats
are off to<br>
creative educators everywhere for this kind of dedication, but
teachers<br>
shouldn't have to do this.<br><br>
Of course, advanced programmers complain about ceremony too. We
will never be<br>
able to satisfy programmers' insatiable appetite for typing
fewer keystrokes,<br>
and we shouldn't try, because the goal of programming is to
write programs that<br>
are easy to read and are clearly correct, not programs that were
easy to type.<br>
But we can try to better align the ceremony commensurate with
the value it<br>
brings to a program -- and let simple programs be expressed more
simply. <br><br>
## Concept overload<br><br>
The classic "Hello World" program looks like this in Java:<br><br>
```<br>
public class HelloWorld { <br>
public static void main(String[] args) { <br>
System.out.println("Hello World");<br>
}<br>
}<br>
```<br><br>
It may only be five lines, but those lines are packed with
concepts that are<br>
challenging to absorb without already having some programming
experience and<br>
familiarity with object orientation. Let's break down the
concepts a student<br>
confronts when writing their first Java program:<br><br>
- **public** (on the class). The `public` accessibility level
is relevant<br>
only when there is going to be cross-package access; in a
simple "Hello<br>
World" program, there is only one class, which lives in the
unnamed package.<br>
They haven't even written a one-line program yet; the notion
of access<br>
control -- keeping parts of a program from accessing other
parts of it -- is<br>
still way in their future.<br><br>
- **class**. Our student hasn't set out to write a _class_,
or model a<br>
complex system with objects; they want to write a
_program_. In Java, a<br>
program is just a `main` method in some class, but at this
point our student<br>
still has no idea what a class is or why they want one.<br><br>
- **Methods**. Methods are of course a key concept in Java,
but the mechanics<br>
of methods -- parameters, return types, and invocation --
are still<br>
unfamiliar, and the `main` method is invoked magically from
the `java`<br>
launcher rather than from explicit code. <br><br>
- **public** (again). Like the class, the `main` method has
to be public, but<br>
again this is only relevant when programs are large enough
to require<br>
packages to organize them. <br><br>
- **static**. The `main` method has to be static, and at this
point, students<br>
have no context for understanding what a static method is or
why they want<br>
one. Worse, the early exposure to `static` methods will
turn out to be a<br>
bad habit that must be later unlearned. Worse still, the
fact that the<br>
`main` method is `static` creates a seam between `main` and
other methods;<br>
either they must become `static` too, or the `main` method
must trampoline<br>
to some sort of "instance main" (more ceremony!) And if we
get this wrong,<br>
we get the dreaded and mystifying `"cannot be referenced
from a static<br>
context"` error.<br><br>
- **main**. The name `main` has special meaning in a Java
program, indicating<br>
the starting point of a program, but this specialness hides
behind being an<br>
ordinary method name. This may contribute to the sense of
"so many magic<br>
incantations."<br><br>
- **String[]**. The parameter to `main` is an array of
strings, which are the<br>
arguments that the `java` launcher collected from the
command line. But our<br>
first program -- likely our first dozen -- will not use
command-line<br>
parameters. Requiring the `String[]` parameter is, at this
point, a mistake<br>
waiting to happen, and it will be a long time until this
parameter makes<br>
sense. Worse, educators may be tempted to explain arrays at
this point,<br>
which further increases the time-to-first-program.<br><br>
- **System.out.println**. If you look closely at this
incantation, each<br>
element in the chain is a different thing -- `System` is a
class (what's a<br>
class again?), `out` is a static field (what's a field?),
and `println` is<br>
an instance method. The only part the student cares about
right now is<br>
`println`; the rest of it is an incantation that they do not
yet understand<br>
in order to get at the behavior they want.<br><br>
That's a lot to explain to a student on the first day of class.
There's a good<br>
chance that by now, class is over and we haven't written any
programs yet, or<br>
the teacher has said "don't worry what this means, you'll
understand it later"<br>
six or eight times. Not only is this a lot of _syntactic_
things to absorb, but<br>
each of those things appeals to a different concept (class,
method, package,<br>
return value, parameter, array, static, public, etc) that the
student doesn't<br>
have a framework for understanding yet. Each of these will have
an important<br>
role to play in larger programs, but so far, they only
contribute to "wow,<br>
programming is complicated." <br><br>
It won't be practical (or even desirable) to get _all_ of these
concepts out of<br>
the student's face on day 1, but we can do a lot -- and focus on
the ones that<br>
do the most to help beginners understand how programs are
constructed.<br><br>
## Goal: a smooth on-ramp<br><br>
As much as programmers like to rant about ceremony, the real
goal here is not<br>
mere ceremony reduction, but providing a graceful _on ramp_ to
Java programming.<br>
This on-ramp should be helpful to beginning programmers by
requiring only those<br>
concepts that a simple program needs. <br><br>
Not only should an on-ramp have a gradual slope and offer enough
acceleration<br>
distance to get onto the highway at the right speed, but its
direction must<br>
align with that of the highway. When a programmer is ready to
learn about more<br>
advanced concepts, they should not have to discard what they've
already learned,<br>
but instead easily see how the simple programs they've already
written<br>
generalize to more complicated ones, and both the syntatic and
conceptual<br>
transformation from "simple" to "full blown" program should be
straightforward<br>
and unintrusive. It is a definite non-goal to create a
"simplified dialect of<br>
Java for students".<br><br>
We identify three simplifications that should aid both educators
and students in<br>
navigating the on-ramp to Java, as well as being generally
useful to simple<br>
programs beyond the classroom as well:<br><br>
- A more tolerant launch protocol<br>
- Unnamed classes<br>
- Predefined static imports for the most critical methods and
fields<br><br>
## A more tolerant launch protocol<br><br>
The Java Language Specification has relatively little to say
about how Java<br>
"programs" get launched, other than saying that there is some
way to indicate<br>
which class is the initial class of a program (JLS 12.1.1) and
that a public<br>
static method called `main` whose sole argument is of type
`String[]` and whose<br>
return is `void` constitutes the entry point of the indicated
class. <br><br>
We can eliminate much of the concept overload simply by relaxing
the<br>
interactions between a Java program and the `java` launcher:<br><br>
- Relax the requirement that the class, and `main` method, be
public. Public<br>
accessibility is only relevant when access crosses packages;
simple programs<br>
live in the unnamed package, so cannot be accessed from any
other package<br>
anyway. For a program whose main class is in the unnamed
package, we can<br>
drop the requirement that the class or its `main` method be
public,<br>
effectively treating the `java` launcher as if it too resided
in the unnamed<br>
package.<br><br>
- Make the "args" parameter to `main` optional, by allowing the
`java` launcher to<br>
first look for a main method with the traditional
`main(String[])`<br>
signature, and then (if not found) for a main method with no
arguments.<br><br>
- Make the `static` modifier on `main` optional, by allowing
the `java` launcher to<br>
invoke an instance `main` method (of either signature) by
instantiating an<br>
instance using an accessible no-arg constructor and then
invoking the `main`<br>
method on it.<br><br>
This small set of changes to the launch protocol strikes out
five of the bullet<br>
points in the above list of concepts: public (twice), static,
method parameters,<br>
and `String[]`. <br><br>
At this point, our Hello World program is now:<br><br>
```<br>
class HelloWorld { <br>
void main() { <br>
System.out.println("Hello World");<br>
}<br>
}<br>
```<br><br>
It's not any shorter by line count, but we've removed a lot of
"horizontal<br>
noise" along with a number of concepts. Students and educators
will appreciate<br>
it, but advanced programmers are unlikely to be in any hurry to
make these<br>
implicit elements explicit either. <br><br>
Additionally, the notion of an "instance main" has value well
beyond the first<br>
day. Because excessive use of `static` is considered a code
smell, many<br>
educators encourage the pattern of "all the static `main` method
does is<br>
instantiate an instance and call an instance `main` method"
anyway. Formalizing<br>
the "instance main" protocol reduces a layer of boilerplate in
these cases, and<br>
defers the point at which we have to explain what instance
creation is -- and<br>
what `static` is. (Further, allowing the `main` method to be an
instance method<br>
means that it could be inherited from a superclass, which is
useful for simple<br>
frameworks such as test runners or service frameworks.)<br><br>
## Unnamed classes<br><br>
In a simple program, the `class` declaration often doesn't help
either, because<br>
other classes (if there are any) are not going to reference it
by name, and we<br>
don't extend a superclass or implement any interfaces. If we
say an "unnamed<br>
class" consists of member declarations without a class header,
then our Hello<br>
World program becomes:<br><br>
```<br>
void main() { <br>
System.out.println("Hello World");<br>
}<br>
```<br><br>
Such source files can still have fields, methods, and even
nested classes, so<br>
that as a program evolves from a few statements to needing some
ancillary state<br>
or helper methods, these can be factored out of the `main`
method while still<br>
not yet requiring a full class declaration:<br><br>
```<br>
String greeting() { return "Hello World"; }<br><br>
void main() {<br>
System.out.println(greeting());<br>
}<br>
```<br><br>
This is where treating `main` as an instance method really
shines; the user has<br>
just declared two methods, and they can freely call each other.
Students need<br>
not confront the confusing distinction between instance and
static methods yet;<br>
indeed, if not forced to confront static members on day 1, it
might be a while<br>
before they do have to learn this distinction. The fact that
there is a<br>
receiver lurking in the background will come in handy later, but
right now is<br>
not bothering anybody.<br><br>
[JEP 330](<a class="moz-txt-link-freetext" href="https://openjdk.org/jeps/330" target="_blank">https://openjdk.org/jeps/330</a>) allows single-file
programs to be<br>
launched directly without compilation; this streamlined launcher
pairs well with<br>
unnamed classes. <br><br>
## Predefined static imports<br><br>
The most important classes, such as `String` and `Integer`, live
in the<br>
`java.lang` package, which is automatically on-demand imported
into all<br>
compilation units; this is why we do not have to `import
java.lang.String` in<br>
every class. Static imports were not added until Java 5, but no
corresponding<br>
facility for automatic on-demand import of common behavior was
added at that<br>
time. Most programs, however, will want to do console IO, and
Java forces us to<br>
do this in a roundabout way -- through the static `System.out`
and `System.in`<br>
fields. Basic console input and output is a reasonable
candidate for<br>
auto-static import, as one or both are needed by most simple
programs. While<br>
these are currently instance methods accessed through static
fields, we can<br>
easily create static methods for `println` and `readln` which
are suitable for<br>
static import, and automatically import them. At which point
our first program<br>
is now down to:<br><br>
```<br>
void main() {<br>
println("Hello World");<br>
}<br>
```<br><br>
## Putting this all together<br><br>
We've discussed several simplifications:<br><br>
- Update the launcher protocol to make public, static, and
arguments optional<br>
for main methods, and for main methods to be instance methods
(when a<br>
no-argument constructor is available); <br>
- Make the class wrapper for "main classes" optional (unnamed
classes);<br>
- Automatically static import methods like `println`<br><br>
which together whittle our long list of day-1 concepts down
considerably. While<br>
this is still not as minimal as the minimal Python or Ruby
program -- statements<br>
must still live in a method -- the goal here is not to win at
"code golf". The<br>
goal is to ensure that concepts not needed by simple programs
need not appear in<br>
those programs, while at the same time not encouraging habits
that have to be<br>
unlearned as programs scale up. <br><br>
Each of these simplifications is individually small and
unintrusive, and each is<br>
independent of the others. And each embodies a simple
transformation that the<br>
author can easily manually reverse when it makes sense to do so:
elided<br>
modifiers and `main` arguments can be added back, the class
wrapper can be added<br>
back when the affordances of classes are needed (supertypes,
constructors), and<br>
the full qualifier of static-import can be added back. And
these reversals are<br>
independent of one another; they can done in any combination or
any order.<br><br>
This seems to meet the requirements of our on-ramp; we've
eliminated most of the<br>
day-1 ceremony elements without introducing new concepts that
need to be<br>
unlearned. The remaining concepts -- a method is a container for
statements, and<br>
a program is a Java source file with a `main` method -- are
easily understood in<br>
relation to their fully specified counterparts. <br><br>
## Alternatives<br><br>
Obviously, we've lived with the status quo for 25+ years, so we
could continue<br>
to do so. There were other alternatives explored as well;
ultimately, each of<br>
these fell afoul of one of our goals.<br><br>
### Can't we go further?<br><br>
Fans of "code golf" -- of which there are many -- are surely
right now trying to<br>
figure out how to eliminate the last little bit, the `main`
method, and allow<br>
statements to exist at the top-level of a program. We
deliberately stopped<br>
short of this because it offers little value beyond the first
few minutes, and<br>
even that small value quickly becomes something that needs to be
unlearned. <br><br>
The fundamental problem behind allowing such "loose" statements
is that<br>
variables can be declared inside both classes (fields) and
methods (local<br>
variables), and they share the same syntactic production but not
the same<br>
semantics. So it is unclear (to both compilers and humans)
whether a "loose"<br>
variable would be a local or a field. If we tried to adopt some
sort of simple<br>
heuristic to collapse this ambiguity (e.g., whether it precedes
or follows the<br>
first statement), that may satisfy the compiler, but now simple
refactorings<br>
might subtly change the meaning of the program, and we'd be
replacing the<br>
explicit syntactic overhead of `void main()` with an invisible
"line" in the<br>
program that subtly affects semantics, and a new subtle rule
about the meaning<br>
of variable declarations that applies only to unnamed classes.
This doesn't<br>
help students, nor is this particularly helpful for all but the
most trivial<br>
programs. It quickly becomes a crutch to be discarded and
unlearned, which<br>
falls afoul of our "on ramp" goals. Of all the concepts on our
list, "methods"<br>
and "a program is specified by a main method" seem the ones that
are most worth<br>
asking students to learn early.<br><br>
### Why not "just" use `jshell`? <br><br>
While JShell is a great interactive tool, leaning too heavily on
it as an onramp<br>
would fall afoul of our goals. A JShell session is not a
program, but a<br>
sequence of code snippets. When we type declarations into
`jshell`, they are<br>
viewed as implicitly static members of some unspecified class,
with<br>
accessibility is ignored completely, and statements execute in a
context where<br>
all previous declarations are in scope. This is convenient for
experimentation<br>
-- the primary goal of `jshell` -- but not such a great mental
model for<br>
learning to write Java programs. Transforming a batch of
working declarations<br>
in `jshell` to a real Java program would not be sufficiently
simple or<br>
unintrusive, and would lead to a non-idiomatic style of code,
because the<br>
straightforward translation would have us redeclaring each
method, class, and<br>
variable declaration as `static`. Further, this is probably not
the direction<br>
we want to go when we scale up from a handful of statements and
declarations to<br>
a simple class -- we probably want to start using classes as
classes, not just<br>
as containers for static members. JShell is a great tool for
exploration and<br>
debugging, and we expect many educators will continue to
incorporate it into<br>
their curriculum, but is not the on-ramp programming model we
are looking for. <br><br>
### What about "always local"?<br><br>
One of the main tensions that `main` introduces is that most
class members are<br>
not `static`, but the `main` method is -- and that forces
programmers to<br>
confront the seam between static and non-static members. JShell
answers this<br>
with "make everything static". <br><br>
Another approach would be to "make everything local" -- treat a
simple program<br>
as being the "unwrapped" body of an implicit main method. We
already allow<br>
variables and classes to be declared local to a method. We
could add local<br>
methods (a useful feature in its own right) and relax some of
the asymmetries<br>
around nesting (again, an attractive cleanup), and then treat a
mix of<br>
declarations and statements without a class wrapper as the body
of an invisible<br>
`main` method. This seems an attractive model as well -- at
first.<br><br>
While the syntactic overhead of converting back to full-blown
classes -- wrap<br>
the whole thing in a `main` method and a `class` declaration --
is far less<br>
intrusive than the transformation inherent in `jshell`, this is
still not an<br>
ideal on-ramp. Local variables interact with local classes (and
methods, when<br>
we have them) in a very different way than instance fields do
with instance<br>
methods and inner classes: their scopes are different (no
forward references),<br>
their initialization rules are different, and captured local
variables must be<br>
effectively final. This is a subtly different programming model
that would then<br>
have to be unlearned when scaling up to full classes. Further,
the result of<br>
this wrapping -- where everything is local to the main method --
is also not<br>
"idiomatic Java". So while local methods may be an attractive
feature, they are<br>
similarly not the on-ramp we are looking for.<br><br><br></font></font></blockquote><div><br></div><div>I agree with the goal, i've several remarks.<br></div><div><br data-mce-bogus="1"></div><div>- You do not have to declare a class public to run it so you do not have to explain the first "public".<br> So the sutuation is a little less awful that the one you describe :)<br></div><div><br data-mce-bogus="1"></div><div><div>- I know several teachers that uses an interface instead of a class as the default container for methods for the first weeks, because methods are public by default inside an interface (and nested classes are implicitly static).</div><div> The snippet used is something like this</div><div> </div><div><div> interface Hello {</div><div> static void main(String[] args) {<br> ...<br> }</div><div> }</div><div><br data-mce-bogus="1"></div><div> so technically you do not have to explain "public".<br data-mce-bogus="1"></div></div></div><div><br data-mce-bogus="1"></div><div><div>- You can declare a main() on other things that a class, on an interface, on an enum or a record.</div><div> Being able to declare the "main" without to have to declare it static is nice, but the semantics you propose creates new issues,</div><div> because the auto-instantiation does not work if the container is an interface or a record with components.</div><div> This feels too magical to me, and as a teacher i will have to explain it at some point.<br></div></div><div><br data-mce-bogus="1"></div><div>- Currently there is a nice progression in term of complexity, there are 3 steps :<br data-mce-bogus="1"></div><div> first, you have a class with no package and you can only use the classes of the JDK or the classes of the current folder,</div><div> then, you have the package declaration and you can have multiple folder,<br></div><div> and finally if you want to declare non-visible packages, you need module and the module-info.<br data-mce-bogus="1"></div><div> I think your idea of a classless compilation unit plays well with the current idea if we consider it has the step zero,<br data-mce-bogus="1"></div><div> first you have classless class, then class, then package + class and at the end module + package + class.</div><div><br data-mce-bogus="1"></div><div>- We should not be able to declare fields inside a classless class, students strugle at the beginning to make the difference between a field and a local variable.</div><div> Every syntax that make that distinction murkier is a bad idea.<br data-mce-bogus="1"></div><div> So perhaps what we want is a classless container of methods, not a classless class.<br data-mce-bogus="1"></div><div><br data-mce-bogus="1"></div>- At the begining, teaching records is easier than teaching classes because you can do too much with a class while records have a simple syntax and a simple semantics.</div><div data-marker="__QUOTED_TEXT__"> At my university, real classes (not class as container) are only introduced at week 4, when we start to have mutable thingy. <br data-mce-bogus="1"></div><div data-marker="__QUOTED_TEXT__"> In a dream world, we should be able to declare records inside a classless class, but i do not see how the compiler will not see a top level record instead of a classless class containing records.<br></div><div data-marker="__QUOTED_TEXT__"> I suppose, a classless class can not have nested classes/record/enum/interface..<br data-mce-bogus="1"></div><div data-marker="__QUOTED_TEXT__"><div> </div><div>- At my uni, we start by teaching Python and JavaScript, then C then Java. We do not teach ipython because the semantics is slightly different from python.<br></div><div> For the same reason, we do not use jshell for undergraduates because the semantics is sligthly different than java.</div><div> For the same reason, if the semantics of a classless class is different from the semantics of a regular class , we will not use it too.</div><div> I don't think your proposal has that problem, it's more a remainder for me that a classless class can not have a different semantics than a class,<br data-mce-bogus="1"></div><div> it can do less but it can not do more or worst do something differently.<br data-mce-bogus="1"></div><div><br></div><div>- I don't hink we can add an auto static import without causing source backward compatibility issues, because you can not have several import static using the same last identitfier.</div><div> By example, if an existing class declare<br data-mce-bogus="1"></div><div> import static foo.A.println;<br data-mce-bogus="1"></div><div><br data-mce-bogus="1"></div><div> this class will now fail to compile.</div><div> That's why no auto static import was added in Java 5.</div><div><br></div><div> There is also the problem of the comb rule, Java prefers super types methods (even static methods) to static imports.</div><div> So adding a method println() (or any method named like an auto imported static method) to a non final class becomes a hazard.</div><div> You may argue that we altready have that problem now, which is true, but any auto static imports of methods makes this known problem worst.<br data-mce-bogus="1"></div><div><br></div><div>RĂ©mi<br></div><div><br data-mce-bogus="1"></div></div></div></body></html>