Paving the on-ramp
Remi Forax
forax at univ-mlv.fr
Wed Oct 19 17:50:19 UTC 2022
> From: "Brian Goetz" <brian.goetz at oracle.com>
> To: "amber-spec-experts" <amber-spec-experts at openjdk.java.net>
> Sent: Wednesday, October 19, 2022 6:43:18 PM
> Subject: Re: Paving the on-ramp
> Discussion here seems to have settled down. Here's what seem like the
> possibly-open parts:
> - Most of the concerns centered around the imports part, not the main part. We
> should think some more about alternatives for Console IO other than
> auto-importing a static println method; this might mean that their invocation
> is something more verbose than just "println", but better than
> "System.out.println", and possibly more discoverable. In the meantime, we'll
> separate off the auto-import for STR as part of string templates to decouple
> these trains.
> - The notion of "unnamed class" seems to fill a hole alongside "unnamed package"
> and "unnamed module", but the analogy is imperfect, because these classes
> _will_ have a name, just not one visible from _inside_ the class. The
> alternative is to view these as _implicitly named_ classes, where they have a
> name, just derived from the file system rather than the source code. The
> distinction might influence whether such simplified class declarations can have
> constructors (if you can't name it, you can't name the constructor), whether
> static methods or fields can be accessed from within the class, etc. (However,
> these influences are weak; we could still reasonably choose to disallow
> constructors and instance initializers even if the name is denotable.)
> We could, of course, prevent class access from outside the class too,
> effectively making such classes visible only to the launcher. But this would
> come at no small cost to prevent such accesses, and for limited benefit.
We can cheaply disallow access by
- only allowing one compilation unit if there is an unnamed class.
- mark the generated class as synthetic (see JVMS 4.7.8) so it does not work with separate compilation.
> The status-quo proposal currently says: the name of the class is not visible
> from within the class, you can't have constructors or instance initializers,
> you get an implicit no-arg constructor like every other constructor-less class.
> If you want a more complex construction protocol, or supertypes, or class
> declaration annotations, or to live in a package, declare a class. This still
> seems pretty OK.
I still think that allowing fields inside an unamed class is a mistake (if we can avoid to talk about null at the beginning, it's a win).
Rémi
https://docs.oracle.com/javase/specs/jvms/se19/html/jvms-4.html#jvms-4.7.8
> On 10/12/2022 7:56 PM, Brian Goetz wrote:
>> I've gotten some good feedback from this writeup, much of it illuminating how
>> different people (myself included) make different assumptions about how certain
>> things work / what things are for.
>> #### Role of main
>> A main() method can be viewed from two perspectives: it is an ordinary method
>> with modifiers, parameters, etc, but it is also an externally significant
>> _entry point_ into a program. (We've seen a similar double-duty with method
>> parameters in the context of the underscore discussion: a method parameter is
>> both a "local" that serves the implementation of the method, but its also part
>> of the specification of the method (e.g., the name and type bleed into the
>> Javadoc.))
>> Java made the choice in 1995 that an entry point is "just" a suitably named /
>> structured / accessible method. This was somewhat "un-Java-ish", as
>> identification of main-ness is a structural property, and Java (certainly back
>> then) mostly avoided such structural techniques where it could. I think its
>> fair to question whether this decision is still serving us (Stephen's proposal
>> does exactly that), but I think this was a pragmatic choice and I'm inclined to
>> continue to run with it, rather than look at it as something "to be fixed".
>> So the proposal broadens the structure parameters around "what is a main", but
>> ultimately leaves the "some methods are special" decision in place. (Aside: if
>> we'd had annotations in Java 1.0, we might well have used an @EntryPoint
>> capture the metadata of "this method is externally launchable", and let the
>> launcher reflect over the methods of a class to find a suitable entry point.
>> But we didn't, and trying to rewrite history in this way is likely a losing
>> game.)
>> #### Role of launcher
>> This raises a secondary question about the role of the launcher. My mental model
>> was one of "the launcher is just some out-of-sight code that invokes the main
>> method." That `main` (currently) should be public plays into this, because if
>> this out-of-sight code is going to call this method, surely accessibility will
>> have a chance to say "can this code call this method". I was surprised, though,
>> to find out that others have a different view of this. Kevin asked whether it
>> was a "bug" in the model that, in order for the launcher to be able to call a
>> `main` method, then so must other code. This is a form of coupling that I had
>> assumed is pure good (because it means there's a unified model of invocation),
>> but is reasonable to call it out as a coupling.
>> In my view, treating the launcher as "just another caller" simplifies the story,
>> because we don't have to have a separate story for invoking entry points. If
>> we're doubling down on "main is just a method with some special metadata that
>> says 'start here'", then doubling down on accessibility here as well makes
>> sense to me also. Hence my suggestion to frame the launcher as "ordinary code
>> in the unnamed package". But as natural as this seems to me, I recognize that
>> this may seem weird to others.
>> #### Role of unnamed packages
>> Another place where there were differing viewpoints is on the role of unnamed
>> packages. Some felt that using the unnamed package at all should be reserved
>> for the most transient of "run it once and delete it" programs; others felt
>> that it had a role beyond mere experiments.
>> In part because of the difficulties described in the On Ramp document, Java has
>> not found extensive use in scripts, but we are hoping that this will become
>> more common over time. (The "Launch Single File Java Programs" JEP was a step
>> towards being more script-friendly.) I would think that the unnamed package is
>> the right place for scripts as well, since users will want to identify the
>> program by its path in the file system, not by setting up a class path and
>> referring to a relative position within that.
>> Overall, I think the result of this project will be that *more* code lives in
>> the unnamed package -- and that's OK, because people will write more simple,
>> self-contained programs in Java, and the unnamed package is an OK place for
>> these things to live.
>> #### Instance main and interfaces
>> Several folks have pointed out that perhaps an instance main should be specified
>> by an interface (say, Program), and that unnamed classes implicitly implement
>> Program. This has some attraction, since this is generally how we prevent names
>> from being magic.
>> There are some downsides, though. We already have two ways to spell "main"; we
>> might want more in the future, and having to spell them all out in an interface
>> (even with defaults) could impede evolution. More importantly, this is using a
>> different mechanism that we use for the main we currently have, which means
>> there are two different ways to identify the two kinds of main. This is
>> starting to veer into the sort of "fix mistakes of the past" siren song which
>> leaves you with two different ways of doing the same thing, which appears to
>> new users as more complexity, not less.
>> #### Moral hazards
>> A significant proportion of the negative feedback to the proposal amounted to
>> some sort of "moral hazard". The biggest component here was that shortening
>> `System.out.println()` to `println()` would encourage people to use console IO
>> more often, in places where it was not appropriate, or that it would be an
>> impediment to testing. I didn't find this argument very compelling, though; I
>> don't think the number of characters required to access console IO is the
>> determining factor here in choosing an IO strategy (or even being aware that
>> there is such a thing as an IO strategy.)
>> #### Console IO
>> There were a variety of opinions on the direction we should go with simplifying
>> console IO. While nearly everyone agrees that `System.out.println` is a
>> mouthful, there were a number of alternate approaches suggested, such as:
>> - Expose new classes (e.g., SystemOut) with static methods (SystemOut.println)
>> instead of new static methods;
>> - Expose a new API for console IO, but don't auto-import anything;
>> - Auto-import console IO methods only in unnamed classes;
>> - others.
>> #### Auto imports
>> After some thought, I realize that the auto-import feature is trying to serve
>> two separate goals, and maybe is getting distorted because of it. The String
>> Templates JEP defines some canned processors (STR and FMT), which are
>> effectively part of the language. At first blush (which is when people are
>> often most vocal about peripheral features), people already don't like having
>> to type STR-dot (sorry, not changing our mind about that), and having to import
>> STR feels like adding insult to injury. STR is effectively part of the
>> language, so we don't want to place hoops in the way of using it. But that is a
>> slightly different use case than rescuing println, for which there are multiple
>> possible paths. Maybe its best to separate these.
>> The On Ramp document outlined some (admittedly fuzzy) goals which may be helpful
>> for evaluating some of these thoughts. We are not trying to create a
>> "beginner's dialect of Java", nor am I particularly interested in using this
>> effort to turn back the clock and redesign features that some people wish had
>> been designed otherwise. The goal here is, to the extent possible, to make
>> elements of program declaration recede into the background until they are
>> actually adding value. Access control, classes, statics, etc, should come into
>> the picture when they are needed, and not before. I'm sure that there are other
>> aspects of the language that people would like to have recede into the
>> background too, and will attempt to attach to this project, but I'm interested
>> in focusing primarily on those that are impediments to learning the language.
>> If we do our job right, these will have benefits well beyond the first few days
>> of using Java.
>> My thoughts:
>> - Java made the choice that "entry points" are specially structured methods, and
>> can be called both programmatically and via the launcher. I see no reason to
>> tinker with this concept, or to materially change how entry points are declared
>> -- just to widen the range of `main` methods that are considered entry points.
>> - While the JLS doesn't really say what the launcher is (just that there is
>> something that launches programs), positioning the launcher as some code in the
>> unnamed package seems helpful, because familiar concepts of access control
>> apply both to programmatic and command line invocation.
>> - While I get that some people find the unnamed package to be a "smell", I think
>> it is actually fine for a range of programs, and I expect that range to broaden
>> over time as people start using Java for scripts.
>> - I am sympathetic to the argument that people don't want "println" and friends
>> to effectively become global; this is separate from simplifying the locution.
>> Where there may be an attractive middle ground is:
>> - Take the new methods for console IO and put them somewhere in java.lang, say
>> ConsoleIO;
>> - Auto-import ConsoleIO only in unnamed classes.
>> While this creates a new coupling, it may align well, in that the programs who
>> most want access to console IO are those that are most likely to be able to be
>> unnamed classes -- and the rest can import all the console IO with a single
>> `import static ConsoleIO.*`. Refactoring to a named class would then involve
>> adding both the class wrapper and this import.
>> - I am open to other approaches for improving the console IO story. Maybe this
>> is Console.println(), which avoids the need for auto-static-import at all at
>> the cost of some more words (but at least doesn't require understanding static
>> fields, and has the advantage that all the console stuff can be in one place),
>> or maybe something else. (A big part of the problem with the status quo now is
>> that not only is System.out.println undiscoverable, but having found that, you
>> still have no idea where "readln" is.)
>> - While I get why people are attracted to putting instance main in an interface,
>> I don't think I'd like the result. As mentioned above, this implicitly places
>> an impediment in future expansion of the launcher protocol, but more
>> importantly, means we have two mechanisms for finding main -- an "old" and
>> "new" one. I try hard to avoid creating this situation, because the benefit is
>> rarely worth the increased cognitive load.
>> On 9/28/2022 1:57 PM, Brian Goetz wrote:
>>> 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.
>>> Markdown source is below, HTML will appear soon at:
>>> [ https://openjdk.org/projects/amber/design-notes/on-ramp |
>>> https://openjdk.org/projects/amber/design-notes/on-ramp ]
>>> # Paving the on-ramp
>>> Java is one of the most widely taught programming languages in the world. Tens
>>> of thousands of educators find that the imperative core of the language combined
>>> with a straightforward standard library is a foundation that students can
>>> comfortably learn on. Choosing Java gives educators many degrees of freedom:
>>> they can situate students in `jshell` or Notepad or a full-fledged IDE; they can
>>> teach imperative, object-oriented, functional, or hybrid programming styles; and
>>> they can easily find libraries to interact with external data and services.
>>> No language is perfect, and one of the most common complaints about Java is that
>>> it is "too verbose" or has "too much ceremony." And unfortunately, Java imposes
>>> its heaviest ceremony on those first learning the language, who need and
>>> appreciate it the least. The declaration of a class and the incantation of
>>> `public static void main` is pure mystery to a beginning programmer. While
>>> these incantations have principled origins and serve a useful organizing purpose
>>> in larger programs, they have the effect of placing obstacles in the path of
>>> _becoming_ Java programmers. Educators constantly remind us of the litany of
>>> complexity that students have to confront on Day 1 of class -- when they really
>>> just want to write their first program.
>>> As an amusing demonstration of this, in her JavaOne keynote appearance in 2019,
>>> [Aimee Lucido]( [ https://www.youtube.com/watch?v=BkPPFiXUwYk |
>>> https://www.youtube.com/watch?v=BkPPFiXUwYk ] ) talked about when
>>> she learned to program in Java, and how her teacher performed a rap song
>>> to help students memorize `"public static void main"`. Our hats are off to
>>> creative educators everywhere for this kind of dedication, but teachers
>>> shouldn't have to do this.
>>> Of course, advanced programmers complain about ceremony too. We will never be
>>> able to satisfy programmers' insatiable appetite for typing fewer keystrokes,
>>> and we shouldn't try, because the goal of programming is to write programs that
>>> are easy to read and are clearly correct, not programs that were easy to type.
>>> But we can try to better align the ceremony commensurate with the value it
>>> brings to a program -- and let simple programs be expressed more simply.
>>> ## Concept overload
>>> The classic "Hello World" program looks like this in Java:
>>> ```
>>> public class HelloWorld {
>>> public static void main(String[] args) {
>>> System.out.println("Hello World");
>>> }
>>> }
>>> ```
>>> It may only be five lines, but those lines are packed with concepts that are
>>> challenging to absorb without already having some programming experience and
>>> familiarity with object orientation. Let's break down the concepts a student
>>> confronts when writing their first Java program:
>>> - **public** (on the class). The `public` accessibility level is relevant
>>> only when there is going to be cross-package access; in a simple "Hello
>>> World" program, there is only one class, which lives in the unnamed package.
>>> They haven't even written a one-line program yet; the notion of access
>>> control -- keeping parts of a program from accessing other parts of it -- is
>>> still way in their future.
>>> - **class**. Our student hasn't set out to write a _class_, or model a
>>> complex system with objects; they want to write a _program_. In Java, a
>>> program is just a `main` method in some class, but at this point our student
>>> still has no idea what a class is or why they want one.
>>> - **Methods**. Methods are of course a key concept in Java, but the mechanics
>>> of methods -- parameters, return types, and invocation -- are still
>>> unfamiliar, and the `main` method is invoked magically from the `java`
>>> launcher rather than from explicit code.
>>> - **public** (again). Like the class, the `main` method has to be public, but
>>> again this is only relevant when programs are large enough to require
>>> packages to organize them.
>>> - **static**. The `main` method has to be static, and at this point, students
>>> have no context for understanding what a static method is or why they want
>>> one. Worse, the early exposure to `static` methods will turn out to be a
>>> bad habit that must be later unlearned. Worse still, the fact that the
>>> `main` method is `static` creates a seam between `main` and other methods;
>>> either they must become `static` too, or the `main` method must trampoline
>>> to some sort of "instance main" (more ceremony!) And if we get this wrong,
>>> we get the dreaded and mystifying `"cannot be referenced from a static
>>> context"` error.
>>> - **main**. The name `main` has special meaning in a Java program, indicating
>>> the starting point of a program, but this specialness hides behind being an
>>> ordinary method name. This may contribute to the sense of "so many magic
>>> incantations."
>>> - **String[]**. The parameter to `main` is an array of strings, which are the
>>> arguments that the `java` launcher collected from the command line. But our
>>> first program -- likely our first dozen -- will not use command-line
>>> parameters. Requiring the `String[]` parameter is, at this point, a mistake
>>> waiting to happen, and it will be a long time until this parameter makes
>>> sense. Worse, educators may be tempted to explain arrays at this point,
>>> which further increases the time-to-first-program.
>>> - **System.out.println**. If you look closely at this incantation, each
>>> element in the chain is a different thing -- `System` is a class (what's a
>>> class again?), `out` is a static field (what's a field?), and `println` is
>>> an instance method. The only part the student cares about right now is
>>> `println`; the rest of it is an incantation that they do not yet understand
>>> in order to get at the behavior they want.
>>> That's a lot to explain to a student on the first day of class. There's a good
>>> chance that by now, class is over and we haven't written any programs yet, or
>>> the teacher has said "don't worry what this means, you'll understand it later"
>>> six or eight times. Not only is this a lot of _syntactic_ things to absorb, but
>>> each of those things appeals to a different concept (class, method, package,
>>> return value, parameter, array, static, public, etc) that the student doesn't
>>> have a framework for understanding yet. Each of these will have an important
>>> role to play in larger programs, but so far, they only contribute to "wow,
>>> programming is complicated."
>>> It won't be practical (or even desirable) to get _all_ of these concepts out of
>>> the student's face on day 1, but we can do a lot -- and focus on the ones that
>>> do the most to help beginners understand how programs are constructed.
>>> ## Goal: a smooth on-ramp
>>> As much as programmers like to rant about ceremony, the real goal here is not
>>> mere ceremony reduction, but providing a graceful _on ramp_ to Java programming.
>>> This on-ramp should be helpful to beginning programmers by requiring only those
>>> concepts that a simple program needs.
>>> Not only should an on-ramp have a gradual slope and offer enough acceleration
>>> distance to get onto the highway at the right speed, but its direction must
>>> align with that of the highway. When a programmer is ready to learn about more
>>> advanced concepts, they should not have to discard what they've already learned,
>>> but instead easily see how the simple programs they've already written
>>> generalize to more complicated ones, and both the syntatic and conceptual
>>> transformation from "simple" to "full blown" program should be straightforward
>>> and unintrusive. It is a definite non-goal to create a "simplified dialect of
>>> Java for students".
>>> We identify three simplifications that should aid both educators and students in
>>> navigating the on-ramp to Java, as well as being generally useful to simple
>>> programs beyond the classroom as well:
>>> - A more tolerant launch protocol
>>> - Unnamed classes
>>> - Predefined static imports for the most critical methods and fields
>>> ## A more tolerant launch protocol
>>> The Java Language Specification has relatively little to say about how Java
>>> "programs" get launched, other than saying that there is some way to indicate
>>> which class is the initial class of a program (JLS 12.1.1) and that a public
>>> static method called `main` whose sole argument is of type `String[]` and whose
>>> return is `void` constitutes the entry point of the indicated class.
>>> We can eliminate much of the concept overload simply by relaxing the
>>> interactions between a Java program and the `java` launcher:
>>> - Relax the requirement that the class, and `main` method, be public. Public
>>> accessibility is only relevant when access crosses packages; simple programs
>>> live in the unnamed package, so cannot be accessed from any other package
>>> anyway. For a program whose main class is in the unnamed package, we can
>>> drop the requirement that the class or its `main` method be public,
>>> effectively treating the `java` launcher as if it too resided in the unnamed
>>> package.
>>> - Make the "args" parameter to `main` optional, by allowing the `java` launcher
>>> to
>>> first look for a main method with the traditional `main(String[])`
>>> signature, and then (if not found) for a main method with no arguments.
>>> - Make the `static` modifier on `main` optional, by allowing the `java` launcher
>>> to
>>> invoke an instance `main` method (of either signature) by instantiating an
>>> instance using an accessible no-arg constructor and then invoking the `main`
>>> method on it.
>>> This small set of changes to the launch protocol strikes out five of the bullet
>>> points in the above list of concepts: public (twice), static, method parameters,
>>> and `String[]`.
>>> At this point, our Hello World program is now:
>>> ```
>>> class HelloWorld {
>>> void main() {
>>> System.out.println("Hello World");
>>> }
>>> }
>>> ```
>>> It's not any shorter by line count, but we've removed a lot of "horizontal
>>> noise" along with a number of concepts. Students and educators will appreciate
>>> it, but advanced programmers are unlikely to be in any hurry to make these
>>> implicit elements explicit either.
>>> Additionally, the notion of an "instance main" has value well beyond the first
>>> day. Because excessive use of `static` is considered a code smell, many
>>> educators encourage the pattern of "all the static `main` method does is
>>> instantiate an instance and call an instance `main` method" anyway. Formalizing
>>> the "instance main" protocol reduces a layer of boilerplate in these cases, and
>>> defers the point at which we have to explain what instance creation is -- and
>>> what `static` is. (Further, allowing the `main` method to be an instance method
>>> means that it could be inherited from a superclass, which is useful for simple
>>> frameworks such as test runners or service frameworks.)
>>> ## Unnamed classes
>>> In a simple program, the `class` declaration often doesn't help either, because
>>> other classes (if there are any) are not going to reference it by name, and we
>>> don't extend a superclass or implement any interfaces. If we say an "unnamed
>>> class" consists of member declarations without a class header, then our Hello
>>> World program becomes:
>>> ```
>>> void main() {
>>> System.out.println("Hello World");
>>> }
>>> ```
>>> Such source files can still have fields, methods, and even nested classes, so
>>> that as a program evolves from a few statements to needing some ancillary state
>>> or helper methods, these can be factored out of the `main` method while still
>>> not yet requiring a full class declaration:
>>> ```
>>> String greeting() { return "Hello World"; }
>>> void main() {
>>> System.out.println(greeting());
>>> }
>>> ```
>>> This is where treating `main` as an instance method really shines; the user has
>>> just declared two methods, and they can freely call each other. Students need
>>> not confront the confusing distinction between instance and static methods yet;
>>> indeed, if not forced to confront static members on day 1, it might be a while
>>> before they do have to learn this distinction. The fact that there is a
>>> receiver lurking in the background will come in handy later, but right now is
>>> not bothering anybody.
>>> [JEP 330]( [ https://openjdk.org/jeps/330 | https://openjdk.org/jeps/330 ] )
>>> allows single-file programs to be
>>> launched directly without compilation; this streamlined launcher pairs well with
>>> unnamed classes.
>>> ## Predefined static imports
>>> The most important classes, such as `String` and `Integer`, live in the
>>> `java.lang` package, which is automatically on-demand imported into all
>>> compilation units; this is why we do not have to `import java.lang.String` in
>>> every class. Static imports were not added until Java 5, but no corresponding
>>> facility for automatic on-demand import of common behavior was added at that
>>> time. Most programs, however, will want to do console IO, and Java forces us to
>>> do this in a roundabout way -- through the static `System.out` and `System.in`
>>> fields. Basic console input and output is a reasonable candidate for
>>> auto-static import, as one or both are needed by most simple programs. While
>>> these are currently instance methods accessed through static fields, we can
>>> easily create static methods for `println` and `readln` which are suitable for
>>> static import, and automatically import them. At which point our first program
>>> is now down to:
>>> ```
>>> void main() {
>>> println("Hello World");
>>> }
>>> ```
>>> ## Putting this all together
>>> We've discussed several simplifications:
>>> - Update the launcher protocol to make public, static, and arguments optional
>>> for main methods, and for main methods to be instance methods (when a
>>> no-argument constructor is available);
>>> - Make the class wrapper for "main classes" optional (unnamed classes);
>>> - Automatically static import methods like `println`
>>> which together whittle our long list of day-1 concepts down considerably. While
>>> this is still not as minimal as the minimal Python or Ruby program -- statements
>>> must still live in a method -- the goal here is not to win at "code golf". The
>>> goal is to ensure that concepts not needed by simple programs need not appear in
>>> those programs, while at the same time not encouraging habits that have to be
>>> unlearned as programs scale up.
>>> Each of these simplifications is individually small and unintrusive, and each is
>>> independent of the others. And each embodies a simple transformation that the
>>> author can easily manually reverse when it makes sense to do so: elided
>>> modifiers and `main` arguments can be added back, the class wrapper can be added
>>> back when the affordances of classes are needed (supertypes, constructors), and
>>> the full qualifier of static-import can be added back. And these reversals are
>>> independent of one another; they can done in any combination or any order.
>>> This seems to meet the requirements of our on-ramp; we've eliminated most of the
>>> day-1 ceremony elements without introducing new concepts that need to be
>>> unlearned. The remaining concepts -- a method is a container for statements, and
>>> a program is a Java source file with a `main` method -- are easily understood in
>>> relation to their fully specified counterparts.
>>> ## Alternatives
>>> Obviously, we've lived with the status quo for 25+ years, so we could continue
>>> to do so. There were other alternatives explored as well; ultimately, each of
>>> these fell afoul of one of our goals.
>>> ### Can't we go further?
>>> Fans of "code golf" -- of which there are many -- are surely right now trying to
>>> figure out how to eliminate the last little bit, the `main` method, and allow
>>> statements to exist at the top-level of a program. We deliberately stopped
>>> short of this because it offers little value beyond the first few minutes, and
>>> even that small value quickly becomes something that needs to be unlearned.
>>> The fundamental problem behind allowing such "loose" statements is that
>>> variables can be declared inside both classes (fields) and methods (local
>>> variables), and they share the same syntactic production but not the same
>>> semantics. So it is unclear (to both compilers and humans) whether a "loose"
>>> variable would be a local or a field. If we tried to adopt some sort of simple
>>> heuristic to collapse this ambiguity (e.g., whether it precedes or follows the
>>> first statement), that may satisfy the compiler, but now simple refactorings
>>> might subtly change the meaning of the program, and we'd be replacing the
>>> explicit syntactic overhead of `void main()` with an invisible "line" in the
>>> program that subtly affects semantics, and a new subtle rule about the meaning
>>> of variable declarations that applies only to unnamed classes. This doesn't
>>> help students, nor is this particularly helpful for all but the most trivial
>>> programs. It quickly becomes a crutch to be discarded and unlearned, which
>>> falls afoul of our "on ramp" goals. Of all the concepts on our list, "methods"
>>> and "a program is specified by a main method" seem the ones that are most worth
>>> asking students to learn early.
>>> ### Why not "just" use `jshell`?
>>> While JShell is a great interactive tool, leaning too heavily on it as an onramp
>>> would fall afoul of our goals. A JShell session is not a program, but a
>>> sequence of code snippets. When we type declarations into `jshell`, they are
>>> viewed as implicitly static members of some unspecified class, with
>>> accessibility is ignored completely, and statements execute in a context where
>>> all previous declarations are in scope. This is convenient for experimentation
>>> -- the primary goal of `jshell` -- but not such a great mental model for
>>> learning to write Java programs. Transforming a batch of working declarations
>>> in `jshell` to a real Java program would not be sufficiently simple or
>>> unintrusive, and would lead to a non-idiomatic style of code, because the
>>> straightforward translation would have us redeclaring each method, class, and
>>> variable declaration as `static`. Further, this is probably not the direction
>>> we want to go when we scale up from a handful of statements and declarations to
>>> a simple class -- we probably want to start using classes as classes, not just
>>> as containers for static members. JShell is a great tool for exploration and
>>> debugging, and we expect many educators will continue to incorporate it into
>>> their curriculum, but is not the on-ramp programming model we are looking for.
>>> ### What about "always local"?
>>> One of the main tensions that `main` introduces is that most class members are
>>> not `static`, but the `main` method is -- and that forces programmers to
>>> confront the seam between static and non-static members. JShell answers this
>>> with "make everything static".
>>> Another approach would be to "make everything local" -- treat a simple program
>>> as being the "unwrapped" body of an implicit main method. We already allow
>>> variables and classes to be declared local to a method. We could add local
>>> methods (a useful feature in its own right) and relax some of the asymmetries
>>> around nesting (again, an attractive cleanup), and then treat a mix of
>>> declarations and statements without a class wrapper as the body of an invisible
>>> `main` method. This seems an attractive model as well -- at first.
>>> While the syntactic overhead of converting back to full-blown classes -- wrap
>>> the whole thing in a `main` method and a `class` declaration -- is far less
>>> intrusive than the transformation inherent in `jshell`, this is still not an
>>> ideal on-ramp. Local variables interact with local classes (and methods, when
>>> we have them) in a very different way than instance fields do with instance
>>> methods and inner classes: their scopes are different (no forward references),
>>> their initialization rules are different, and captured local variables must be
>>> effectively final. This is a subtly different programming model that would then
>>> have to be unlearned when scaling up to full classes. Further, the result of
>>> this wrapping -- where everything is local to the main method -- is also not
>>> "idiomatic Java". So while local methods may be an attractive feature, they are
>>> similarly not the on-ramp we are looking for.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/amber-spec-observers/attachments/20221019/64fb7293/attachment-0001.htm>
More information about the amber-spec-observers
mailing list