Paving the on-ramp

Kevin Bourrillion kevinb at google.com
Thu Sep 29 04:12:58 UTC 2022


Meta-comment: I think you have the right *motivating* use cases (beginners,
small/temporary programs), but I expect pretty much *any* main method to
want to use this, and I don't see why it shouldn't. That makes those use
cases important and worth reasonable attempts at accommodation, regardless
of whether we'd even be doing this for their sake alone.


On Wed, Sep 28, 2022 at 6:36 PM 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.
>


Right, and that design was good inasmuch as there were good use cases for
every one of the rungs (and there were).


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.
>

So far so good up to that last part. A constructor parameter? I thought you
were going to say you just add the field and all your non-static methods
read and write it at will. Getting a bit lost in the twists n' folds.



>   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!)
>

Hmm, I was under the impression I could drop all my `static`s while keeping
the class signature if I wanted? But, if I can and even then explicit
constrs and initers are banned, then indeed, at least one of my drawbacks
is invalid. I don't think it undercuts my overall case that much.



> 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.
>

I'm confused; what does any of this have to do with package location? Isn't
that orthogonal to everything we're discussing?

I'm also not sure why we're talking about "unnamed" so much; the condition
we're talking about is really "signatureless" or "body-only", which as far
as I know could be a purely source-level distinction, producing a
completely normal-looking, named class in a classfile. (Wrote that before
Guy's message came in, even.)


No class annotations.  No type variables.  No superclasses.
>

Okay, in the motivating use cases (beginner/small/temp), yeah, they're
awfully unlikely to want class annotations.

But again, to me every main() method out there is a use case too. And
plenty of class annotations are used for purposes that aren't about
*classes*, just "this whole range of code here". It feels like they should
be allowed, unless you want to talk about `ElementType.COMPILATION_UNIT`
... :-)

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.
>
>
>
>

-- 
Kevin Bourrillion | Java Librarian | Google, Inc. | kevinb at google.com
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/amber-spec-observers/attachments/20220928/24bb2b95/attachment.htm>


More information about the amber-spec-observers mailing list