Paving the on-ramp

Victor Nazarov asviraspossible at gmail.com
Thu Sep 29 09:56:37 UTC 2022


Hello expert,

One more design space point that allows beginner friendly arrangement of
concepts is to extend the Java compilation unit to always allow top-level
method declarations.

Top-level methods are always static and can not be public.
There is still a value for advanced java-developers: these methods allow a
succinct way to write private static utility-methods to be used from the
normal class.

It's not hard to imagine that normal Java code inside the
my/company/pkg/Test.java file can contain:

````
package my.company.pkg;

import ...;
import ...;

void helper1(HiddenClass hiddenClass) {
   ...
}

void helper2() {
   ...
}


public class Test {
    record NormalInnerDeclaration() {}
}
class HiddenClass {
}
````

The semantics of this is that only the code inside the compilation unit can
call `helper1` or `helper2`, nobody else can.
Semantically there is no enclosing class, there is no name that can be used
to disambiguate method name: Test.helper1() is not the top level method and
can not be used to reference `helper1` method, only the stand-alone name
`helper1` can.

For the beginners this means that they can write a file with the `.java`
suffix and use the unnamed package and they should never care about class
names or exact file name.

````
void main() {
   ...
}

void quicksort() {
}

record MyRecord {
}
````

We can extend the Java mechanism to run a single file to look up top-level
static methods. Having some `excersise1.java` we can execute it with the
simple

````
    $ java excersise1.java
````

Technically in JVM this method can be implemented as something like a
companion Utility class in the same nest. For the
`my/company/pkg/Test.java` source file we may get
`my/company/pkg/Test.class` and
`my/company/pkg/Test$utilitycompanion.class` or something like this.

When a beginner progresses towards large-scale organization of code, they
still don't need to unlearn to use top-level methods, they will stay always
available.

--
Victor Nazarov


On Thu, Sep 29, 2022 at 3:37 AM Brian Goetz <brian.goetz at oracle.com> wrote:

>
>
> A major design goal of yours seems clear: to get there without rendering
> Java source files explicitly bimorphic ("class" source files all look like
> this, "main" source files all look like that). Instead you have a set of
> independent features that can compose to get you there in a "smooth ramp".
> The design looks heavily influenced by that goal.
>
>
> Yes, I sometimes call this "telescoping", because there's a chain of "x is
> short for y is short for z".  For example, with lambdas:
>
>     x -> e
>
> is-short-for
>
>     (x) -> e
>
> is-short-for
>
>     (var x) -> e
>
> is-short-for
>
>     (int x) -> e  // or whatever the arg is
>
> As a design convention, it enables a mental model where there is really
> just one form, with varying things you could leave out.  Early in the
> Lambda days, we saw articles like "there are N forms of lambda
> expressions", and that stuff infuriates me, it is as if people go out of
> their way to find more complex mental models than necessary.
>
> As my program grows and gets more complex, I will make changes like
>
> * use more other libraries
> * add args to main()
> * add helper methods
> * add constants
> * create new classes and use them from here
>
> But: when and why would I be motivated to change *this* code *itself* to
> "become" a class, become instantiable, acquire instance state, etc. etc.? I
> don't imagine ever having that urge. main() is just main()! It's just a way
> in. Isn't it literally just a way to (a) transfer control back and forth
> and (b) hand me args?
>
>
> This doesn't seem like such a leap to me.  You might start out hardcoding
> a file path that will be read.  Then you might decide to let that be passed
> in (so you add the args parameter to main).  Then you might want to treat
> the filename to be read as a field so it can be shared across methods, so
> you turn it into a constructor parameter.  One could imagine "introduce X"
> refactorings to do all of these.  The process of hardcoding to main()
> parameter to constructor argument is a natural sedimentation of things
> finding their right level.  (And even if you don't do all of this, knowing
> that its an ordinary class (like an enum or a record) just with a concise
> syntax means you don't have to learn new concepts.  I don't want Foo
> classes and Bar classes.)
>
>
> Note I was only reacting to "static bad!" here. I would be happy if *that*
> argument were dropped, but you do still have another valid argument: that
> `static` is another backward default, and the viral burden of putting it
> not just on main() but every helper method you factor out is pure nuisance.
> (I'd suggest mentioning the viral nature of this particular burden
> higher/more prominently in the doc, as it's currently out of place under
> the "unnamed classes" section.)
>
> (That doesn't mean "so let's do it"; I still hope to see that benefit
> carefully measured against the drawbacks. Btw, *some* of those drawbacks
> might be eased by disallowing an explicit constructor... and jeez, please
> disallow type parameters too... I'm leaving the exact meaning of "disallow"
> undefined here.)
>
>
> Indeed, I intend that there are no explicit constructors or instance
> initializers here.  (There can't be constructors, because the class is
> unnamed!)  I think I said somewhere "such classes can contain ..." and
> didn't list constructors, but I should have been more explicit.
>
>
> ## 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.
>>
>
> How do I tell `java` which class file to load and call main() on? Class
> name based on file name, I guess?
>
>
> Sadly yes.  More sad stories coming on this front, Jim can tell.
>
>
> 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");
>> }
>> ```
>>
>
>
> One or more class annotations could appear below package/imports?
>
>
> No package statement (unnamed classes live in the unnamed package), but
> imports are OK.  No class annotations.  No type variables.  No
> superclasses.
>
> Such source files can still have fields, methods, and even nested classes,
>>
>
> Do those get compiled to real nested classes, nested inside an unnamed
> class? So if I edit a "regular" `Foo.java` file, go down below the last `}`
> and add a `main` function there, does that cause the whole `Foo` class
> above to be reinterpreted as "nested inside an unnamed class" instead of
> top-level?
>
>
> To be discussed!
>
> This is my notion of a natural progression:
>
> 1. Write procedural code: calling static methods, using existing data
> types, soon calling their instance methods
> 2. Proceed to creating your own types (from simple data types onward) and
> using them too
> 3. One day learn that your main() function is actually a method of an
> instantiable type too... at pub trivia night, then promptly forget it
>
>
> Right.
>
>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/amber-spec-observers/attachments/20220929/6cae0e89/attachment.htm>


More information about the amber-spec-observers mailing list