From asviraspossible at gmail.com Thu Sep 29 09:56:37 2022 From: asviraspossible at gmail.com (Victor Nazarov) Date: Thu, 29 Sep 2022 11:56:37 +0200 Subject: Paving the on-ramp In-Reply-To: <2e779dc3-8a16-8d89-1e5d-6b14b04998d3@oracle.com> References: <1b6200d3-a7a6-6479-e8ab-d932eedbceb1@oracle.com> <2e779dc3-8a16-8d89-1e5d-6b14b04998d3@oracle.com> Message-ID: 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 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: