SimpleIO in JEP draft 8323335
Brian Goetz
brian.goetz at oracle.com
Tue Feb 20 15:46:06 UTC 2024
Your vote for "let's not do this at all" is hereby recorded!
On 2/20/2024 10:41 AM, Cay Horstmann wrote:
> Actually, that's not quite what I am saying.
>
> What I am saying is: If you give something for beginning programmers
> (which is, according to the JEP, the purpose of SimpleIO), and it adds
> additional complexity to the programming model, then it better be
> compelling enough for beginners to use, and for teachers to adopt.
> Otherwise, it is better to give nothing at all.
>
> On 20/02/2024 15.34, Brian Goetz wrote:
>> What you're really saying is "I want more than you're giving me."
>> And there's nothing wrong with wanting more. You seem to have mostly
>> accepted that the primitive is "read a line of input as a string",
>> but it leaves you with a problem: you don't want to read strings, you
>> want to read numbers. That's all fine.
>>
>> But the flip side of "this isn't good enough, give us more" is that
>> if there isn't a "more" that is good enough to meet the the bar,
>> you're going to get ... nothing. It's a foundational design
>> principle for Java (thanks James!) that if you don't know the right
>> thing to do, then don't do anything (yet). The alternatives that
>> have been proposed (all of which we already went through before they
>> came around again here) did not meet the bar. This is what meets the
>> bar. It may not be as much as you want, but it is something, and it
>> combines with all the other possible next steps.
>>
>> As a teacher, you have many choices. You can keep doing what you've
>> been doing, teaching Scanner; many teachers will. Or you could
>> distribute your own library of convenience methods -- many teachers
>> do. Or you could teach Integer::parseInt, which is messy, but has
>> the benefit of being exactly as messy as the problem is -- which is
>> also a useful lesson. Or, or, or, or.
>>
>> And if you don't like the magic static import, don't use it! Tell
>> your students to use `SimpleIO::readAStringPlease`. We are not
>> trying to create a beginners dialect here.
>>
>> All of this is to say: we are not trying to put out a One True Only
>> Way To Teach Java. We're smoothing out the path in a way that admits
>> many teaching paths, including ignoring all this stuff. Is there
>> more that could be done? Of course. And when we have a *good*
>> candidate for what the next hundred feet of onramp looks like, we
>> will proceed. And I am confident that it will not conflict with
>> these first hundred feet, because -- how could it?
>>
>>
>>
>>
>>
>> On 2/20/2024 6:44 AM, Cay Horstmann wrote:
>>> I am one of the people who writes books for beginners. I have a
>>> whole bunch of example programs that involve reading numbers.
>>> Professors adopting my books have a ton of exercises that involve
>>> reading numbers. I can't ignore reading numbers.
>>>
>>> I agree that input and println are reasonable primitives for
>>> beginners, and that number parsing can be done in a separate step.
>>> But if that parsing step is not simple for beginners, I don't think
>>> input will find much use for beginners either.
>>>
>>> For my books, I need to decide what to do in the (n + 1)st edition.
>>> Should I stick with
>>>
>>> Scanner in = new Scanner(System.in);
>>> ...
>>> System.out.print("How old are you? ");
>>> int age = in.nextInt();
>>>
>>> or switch to
>>>
>>> println("How old are you?");
>>> int age = in.nextInt();
>>>
>>> or go all the way to
>>>
>>> int age = Integer.parseInt(input("How old are you"));
>>>
>>> I have no conceptual problem with in.nextInt(). I need to explain
>>> method calls early on, so that students can work with strings.
>>>
>>> With the new way, I have a different problem. Now I need to explain
>>> to students that they can call an unqualified input, but parseInt
>>> needs to be qualified. And I have to accelerate the coverage of
>>> static methods.
>>>
>>> As Brian says, there are too many conflicting goals.
>>>
>>> If the goal is simplicity and consistency, it would be more useful
>>> not to use a magic static import. If SimpleIO.input is too long, it
>>> could be IO.in, with IO in java.lang.
>>>
>>> If the goal is convenience, it would be better to have more
>>> magically statically imported methods, in particular parseInt,
>>> parseDouble. Or readAnInt, readADouble...
>>>
>>> Cheers,
>>>
>>> Cay
>>>
>>>
>>> On 19/02/2024 18.06, Brian Goetz wrote:
>>>> There's a reason there are so many opinions here: because the goals
>>>> are in conflict. Everyone wants simplicity, but people don't agree
>>>> on what "simple" means. (Cue the jokes about "I would simply not
>>>> write programs with bugs.")
>>>>
>>>> Yes, getting numbers from the user is a basic task. But it is not,
>>>> in any way, simple! Because reading numbers from the input is
>>>> invariably complected with discarding things that are "acceptably
>>>> non-numbery" (e.g., whitespace), which is neither simple nor
>>>> usually terribly well documented. We've all encountered the problem
>>>> in many language runtimes where reading a number using the
>>>> "friendly way" leaves the input in a state that requires fixing or
>>>> yields surprises for the next operation.
>>>>
>>>> This is because reading a number from an input stream is not any
>>>> sort of primitive; it is the composite of reading from the input,
>>>> deciding what to skip, deciding when to stop reading, converting to
>>>> another type, deciding what state to leave the input stream in, and
>>>> deciding what to do if no number could be found (or if the number
>>>> was too big to fit into an int, etc.) This is not^3 simple!
>>>>
>>>> C starts with a simple and principled answer, which is that the IO
>>>> primitive is getchar() and putchar(). Reading or writing one
>>>> character is unquestionably a primitive. (But also, unless you are
>>>> writing `cat`, no one wants to program with getchar and putchar,
>>>> because it's too primitive.)
>>>>
>>>> One can make a reasonable case for "write a line / read a line"
>>>> being sensible primitives. They are simple enough: no parsing, no
>>>> deciding what to throw away, no possible errors other than EOF, it
>>>> is clear what state you leave the stream in. These may not be what
>>>> the student wants, but they are primitives a student can deal with
>>>> without having to understand parsing and error handling and
>>>> statefulness yet.
>>>>
>>>> String s = getALine();
>>>> printALine(s);
>>>>
>>>> is a program every student can reason about.
>>>>
>>>> But, it is true that dealing in strings, while honest and simple,
>>>> is not always what the student wants. But herein lies the
>>>> strongest argument for not trying to reinvent Scanner here: the
>>>> ability to read numbers makes the complexity of the problem, and
>>>> hence of the API, much much bigger. (Scanner was very well
>>>> intentioned, and was not written by children, and yet none of us
>>>> want to use it. That's a sign that a one-size-fits-all magic input
>>>> processing system is harder than it looks, and for something that
>>>> is explicitly aimed at beginners, is a double warning sign.)
>>>>
>>>> I could imagine someone suggesting "why don't you just add
>>>> `readLineAsInt`". But what would happen next? Well, there would
>>>> be a million requests (including from folks like Cay) of "you
>>>> should add X", and then the result is a mishmash jumble of an API
>>>> (that's already terrible), but worse, it's an onramp that leads to
>>>> nowhere. Once the user's needs are slightly more complicated, they
>>>> are nowhere.
>>>>
>>>> Remi has it absolutely right (yes, I really said that) with
>>>>
>>>>> The classical program is:
>>>>> input -> strings -> objects -> strings -> output
>>>>
>>>> We do not do users a favor by blurring the distinction between
>>>> "input -> string" and "string -> object", and because the latter is
>>>> so much more open-ended than the former, the latter infects the
>>>> former with its complexity if we try.
>>>>
>>>> Is this simple API the most wonderful, be-all of APIs? Of course
>>>> not. But it is a sensible set of primitives that users can
>>>> understand and *build on* in a transparent way.
>>>>
>>>> Some teachers may immediately reach for teaching Integer::parseInt;
>>>> that's a reasonable strategy, it exposes students to the questions
>>>> of "what happens when preconditions fail", and the two compose just
>>>> fine. But maybe you don't like Integer::parseInt for some reason.
>>>> Another way to teach this is to have them write it themselves.
>>>> This will expose them to all sorts of interesting questions (what
>>>> about whitespace? what about double negatives?), but of course is
>>>> also throwing in the deep end of the pool. But
>>>> SimpleIO::readMeALinePlease is agnostic; it works with both
>>>> approaches.
>>>>
>>>> Could the JDK use some better tools for parsing? Sure; pattern
>>>> matching has a role to play here, a `String::unformat` would be
>>>> really cool, and I love parser combinators. All of this can happen
>>>> in the future, and none have the effect of making this API look
>>>> like yet another white elephant like Scanner. Because it focused
>>>> purely on the basics.
>>>>
>>>>
>>>> On 2/19/2024 7:25 AM, Remi Forax wrote:
>>>>> I agree with Brian here,
>>>>> as a teacher, you have to talk about parsing and formatting, those
>>>>> should not be hidden.
>>>>>
>>>>> The classical program is:
>>>>> input -> strings -> objects -> strings -> output
>>>>>
>>>>> Rémi
>>>>>
>>>>> ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
>>>>>
>>>>>
>>>>> *From: *"Tagir Valeev" <amaembo at gmail.com>
>>>>> *To: *"Cay Horstmann" <cay at horstmann.com>
>>>>> *Cc: *"Brian Goetz" <brian.goetz at oracle.com>, "amber-dev"
>>>>> <amber-dev at openjdk.org>
>>>>> *Sent: *Monday, February 19, 2024 10:09:35 AM
>>>>> *Subject: *Re: SimpleIO in JEP draft 8323335
>>>>>
>>>>> I agree that simple methods to get numeric input are essential
>>>>> for beginners. They should not be distracted with a complex
>>>>> ceremony. Instead, they should be able to learn control flow
>>>>> statements and simple algorithms as soon as possible, having a
>>>>> simple way to get numbers from the user.
>>>>> With best regards,
>>>>> Tagir Valeev.
>>>>>
>>>>> On Mon, Feb 19, 2024 at 9:10 AM Cay Horstmann
>>>>> <cay at horstmann.com> wrote:
>>>>>
>>>>> Yes, that's what I am saying. If scanners live in vain,
>>>>> stick with a subset of the Console methods. Use its readLine. Make
>>>>> it so that SimpleIO uses System.console(). And add print and
>>>>> println to Console.
>>>>>
>>>>> The JEP talks about being able to start programming
>>>>> without having to know about static methods. How does a beginner
>>>>> read a number? With Integer.parseInt(readLine(prompt))?
>>>>>
>>>>> What about locales? Is print/println localized?
>>>>> Console.printf is. If so, how are beginners from around the world
>>>>> supposed to read localized numbers? With
>>>>> NumberFormat.getInstance().parse(readLine(prompt))?
>>>>>
>>>>> Adding localized readInt/readDouble to SimpleIO might do
>>>>> the trick. Do they consume the trailing newline? (The equivalent
>>>>> Scanner methods don't, which is definitely a sharp edge for
>>>>> beginners.)
>>>>>
>>>>> On 18/02/2024 23.08, Brian Goetz wrote:
>>>>> > OK, so is this really just that that you are
>>>>> bikeshedding the name? Renaming `input` to `readLine`?
>>>>> >
>>>>> > This is a perfectly reasonable naming choice, of course,
>>>>> but also, not what you suggested the first time around:
>>>>> >
>>>>> > > ... "a third API" ...
>>>>> >
>>>>> > > ... "there are two feasible directions" ...
>>>>> >
>>>>> > So what exactly are you suggesting?
>>>>> >
>>>>> >
>>>>> >
>>>>> > On 2/18/2024 5:03 PM, Cay Horstmann wrote:
>>>>> >> Like I said, either the scanner methods or the console
>>>>> methods are fine.
>>>>> >>
>>>>> >> I am of course aware of the utility/complexity of
>>>>> Scanner, and can understand the motivation to have a
>>>>> simpler/feebler behavior in SimpleIO. Like the one in Console.
>>>>> >>
>>>>> >> You don't have to "get a console". A SimpleIO.readLine
>>>>> method can just invoke readLine on the system console.
>>>>> >>
>>>>> >> My objection is to add yet another "input" method into
>>>>> the mix. "input" is weak. Does it read a token or the entire line?
>>>>> Does it consume the newline? And if it does just what readLine
>>>>> does, why another method name? Because "input" is three characters
>>>>> fewer? Let's not count characters.
>>>>> >>
>>>>> >> On 18/02/2024 22.43, Brian Goetz wrote:
>>>>> >>> I think you are counting characters and not counting
>>>>> concepts.
>>>>> >>>
>>>>> >>> Scanner has a ton of complexity in it that can easily
>>>>> trip up beginners. The main sin (though there are others) is that
>>>>> input and parsing are complected (e.g., nextInt), which only
>>>>> causes more problems (e.g., end of line issues.) Reading from
>>>>> the console is clearly a () -> String operation. The input()
>>>>> method does one thing, which is get a line of text. That's simple.
>>>>> >>>
>>>>> >>> Integer.parseInt (or, soon, patterns that match
>>>>> against string and bind an int) also does one thing: convert a
>>>>> string from int. It may seem verbose to have to do both
>>>>> explicitly, but it allows each of these operations to be simple,
>>>>> and it is perfectly obvious what is going on. On the other hand,
>>>>> Scanner is a world of complexity on its own.
>>>>> >>>
>>>>> >>> Console::readLine is nice, but first you have to get a
>>>>> Console. ("Why can I print something without having to get some
>>>>> magic helper object, but I can't do the same for reading?") What
>>>>> we're optimizing for here is conceptual simplicity; the simplest
>>>>> possible input method is the inverse of println. The fact that
>>>>> input has to be validated is a fact of life; we can treat
>>>>> validation separately from IO (and we should), and it gets simpler
>>>>> when you do.
>>>>> >>>
>>>>> >>> On 2/18/2024 4:12 PM, Cay Horstmann wrote:
>>>>> >>>> I would like to comment on the simplicity of
>>>>> https://openjdk.org/jeps/8323335 for beginning students.
>>>>> >>>>
>>>>> >>>> I am the author of college texts for introductory
>>>>> programming. Like other authors, I introduce the Scanner class
>>>>> (and not Console) for reading user input. Given that students
>>>>> already know about System.out, it is simpler to call
>>>>> >>>>
>>>>> >>>> System.out.print("How old are you? ");
>>>>> >>>> int x = in.nextInt(); // in is a Scanner
>>>>> >>>>
>>>>> >>>> than
>>>>> >>>>
>>>>> >>>> int x = Integer.parseInt(console.readLine("How old
>>>>> are you? "));
>>>>> >>>>
>>>>> >>>> or with the JEP draft:
>>>>> >>>>
>>>>> >>>> int x = Integer.parseInt(input("How old are you? "));
>>>>> >>>>
>>>>> >>>> Then again, having a prompt string is nice too, so I
>>>>> could imagine using the Console API with Integer.parseInt and
>>>>> Double.parseDouble, instead of Scanner.nextInt/nextDouble.
>>>>> >>>>
>>>>> >>>> But why have a third API, i.e. "input"?
>>>>> >>>>
>>>>> >>>> I think there are two feasible directions. Either
>>>>> embrace the Scanner API and next/nextInt/nextDouble/nextLine, or
>>>>> the Console API and readLine. Adding "input" into the mix is just
>>>>> clutter, and ambiguous clutter at that. At least readLine makes it
>>>>> clear that the entire line is consumed.
>>>>> >>>>
>>>>> >>>> Cheers,
>>>>> >>>>
>>>>> >>>> Cay
>>>>> >>>>
>>>>> >>>> --
>>>>> >>>>
>>>>> >>>> Cay S. Horstmann |
>>>>> https://urldefense.com/v3/__http://horstmann.com__;!!ACWV5N9M2RV99hQ!IuXZk_tqIH8rEw1bD3uYb8UcIZF-nnoeFT3UG17pMO5EVXIYVRaAKi7XCq_T02HwnAek1wuV8Wed08w$
>>>>> | mailto:cay at horstmann.com
>>>>> >>>
>>>>> >>
>>>>> >
>>>>>
>>>>> --
>>>>> --
>>>>>
>>>>> Cay S. Horstmann |
>>>>> https://urldefense.com/v3/__http://horstmann.com__;!!ACWV5N9M2RV99hQ!JDq2P0DR423V62MvLF-CBrjfMSFshyy9lkQdQQPt5aEojp3WbQriYDtG-00NepYgsFay4aXHAQFHA24$
>>>>> <https://urldefense.com/v3/__http://horstmann.com__;!!ACWV5N9M2RV99hQ!IZrLgaQxOHBjUURoC5mWbfsijev257bb4C0DMamUDpoGqS5JMACpaMKsbUNQlWcGds7fifmS9sARC6aKMHEf$>
>>>>> | mailto:cay at horstmann.com
>>>>>
>>>>>
>>>>
>>>
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/amber-dev/attachments/20240220/16443684/attachment-0001.htm>
More information about the amber-dev
mailing list