<div dir="ltr"><div>There are some points that seem not right to me.</div><div><br></div><div>> C starts with a simple and principled answer, which is that the IO primitive is getchar() and putchar().</div><div></div><div><br></div><div>For the purpose of learning, in C we do `scanf("%d", &n)` and in C++ we do `std::cin >> n`. I believe no one uses `getchar()` and `putchar()` to read integral or floating-point values.</div><div><br></div><div>In fact, most beginner problem requires parsing of integral or floating-point inputs. The simplest problem that anyone starts learning to code will probably be adding 2 integers, when the learners have not even known what a `String` is. At that point, they simply do not have adequate knowledge to start understanding what `Integer::parseInt` does so it is just some magical characters that needs to be there.</div><div><br></div><div>> Scanner was very well intentioned, and was not written by children, and yet none of us want to use it.</div><div><br></div><div>Beginners use `Scanner` all the time, if you find `read an int java` on Google most of the results will use `Scanner`. And personally when learning to code I only did not use `Scanner` when doing competitive programming (due to its terrible performance).</div><div><br></div><div>Cheers,</div><div>Quan Anh</div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Tue, 20 Feb 2024 at 00:07, Brian Goetz <<a href="mailto:brian.goetz@oracle.com">brian.goetz@oracle.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><u></u>

  
  <div>
    <font size="4" face="monospace">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.")  <br>
      <br>
      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.  <br>
      <br>
      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!  <br>
      <br>
      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.)  <br>
      <br>
      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.  <br>
      <br>
          String s = getALine();<br>
          printALine(s);<br>
      <br>
      is a program every student can reason about.  <br>
      <br>
      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.) 
      <br>
      <br>
      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.  <br>
      <br>
      Remi has it absolutely right (yes, I really said that) with<br>
      <br>
      <blockquote type="cite">
        <div>The classical program is:<br>
        </div>
           input -> strings -> objects -> strings -> output</blockquote>
      <br>
      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.  <br>
      <br>
      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.  <br>
      <br>
      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.  <br>
      <br>
      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.  <br>
      <br>
    </font><br>
    <div>On 2/19/2024 7:25 AM, Remi Forax wrote:<br>
    </div>
    <blockquote type="cite">
      
      <div style="font-family:arial,helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)">
        <div>I agree with Brian here,<br>
        </div>
        <div>as a teacher, you have to talk about parsing and
          formatting, those should not be hidden.<br>
        </div>
        <div><br>
        </div>
        <div>The classical program is:<br>
        </div>
        <div>   input -> strings -> objects -> strings ->
          output<br>
        </div>
        <div><br>
        </div>
        <div>Rémi<br>
        </div>
        <div><br>
        </div>
        <hr id="m_-4219301334617571878zwchr">
        <div>
          <blockquote style="border-left:2px solid rgb(16,16,255);margin-left:5px;padding-left:5px;color:rgb(0,0,0);font-weight:normal;font-style:normal;text-decoration:none;font-family:Helvetica,Arial,sans-serif;font-size:12pt"><b>From:
            </b>"Tagir Valeev" <a href="mailto:amaembo@gmail.com" target="_blank"><amaembo@gmail.com></a><br>
            <b>To: </b>"Cay Horstmann" <a href="mailto:cay@horstmann.com" target="_blank"><cay@horstmann.com></a><br>
            <b>Cc: </b>"Brian Goetz" <a href="mailto:brian.goetz@oracle.com" target="_blank"><brian.goetz@oracle.com></a>,
            "amber-dev" <a href="mailto:amber-dev@openjdk.org" target="_blank"><amber-dev@openjdk.org></a><br>
            <b>Sent: </b>Monday, February 19, 2024 10:09:35 AM<br>
            <b>Subject: </b>Re: SimpleIO in JEP draft 8323335<br>
          </blockquote>
        </div>
        <div>
          <blockquote style="border-left:2px solid rgb(16,16,255);margin-left:5px;padding-left:5px;color:rgb(0,0,0);font-weight:normal;font-style:normal;text-decoration:none;font-family:Helvetica,Arial,sans-serif;font-size:12pt">
            <div dir="ltr">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.<br>
              <div>With best regards,</div>
              <div>Tagir Valeev.</div>
            </div>
            <br>
            <div class="gmail_quote">
              <div dir="ltr" class="gmail_attr">On Mon, Feb 19, 2024 at
                9:10 AM Cay Horstmann <<a href="mailto:cay@horstmann.com" target="_blank">cay@horstmann.com</a>>
                wrote:<br>
              </div>
              <blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">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.<br>
                <br>
                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))?<br>
                <br>
                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))?<br>
                <br>
                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.)<br>
                <br>
                On 18/02/2024 23.08, Brian Goetz wrote:<br>
                > OK, so is this really just that that you are
                bikeshedding the name?  Renaming `input` to `readLine`?<br>
                > <br>
                > This is a perfectly reasonable naming choice, of
                course, but also, not what you suggested the first time
                around:<br>
                > <br>
                >  > ... "a third API" ...<br>
                > <br>
                >  > ... "there are two feasible directions" ...<br>
                > <br>
                > So what exactly are you suggesting?<br>
                > <br>
                > <br>
                > <br>
                > On 2/18/2024 5:03 PM, Cay Horstmann wrote:<br>
                >> Like I said, either the scanner methods or the
                console methods are fine.<br>
                >><br>
                >> 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.<br>
                >><br>
                >> You don't have to "get a console". A
                SimpleIO.readLine method can just invoke readLine on the
                system console.<br>
                >><br>
                >> 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.<br>
                >><br>
                >> On 18/02/2024 22.43, Brian Goetz wrote:<br>
                >>> I think you are counting characters and not
                counting concepts.<br>
                >>><br>
                >>> 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.<br>
                >>><br>
                >>> 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.<br>
                >>><br>
                >>> 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.<br>
                >>><br>
                >>> On 2/18/2024 4:12 PM, Cay Horstmann wrote:<br>
                >>>> I would like to comment on the
                simplicity of <a href="https://openjdk.org/jeps/8323335" rel="noreferrer" target="_blank">https://openjdk.org/jeps/8323335</a>
                for beginning students.<br>
                >>>><br>
                >>>> 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<br>
                >>>><br>
                >>>> System.out.print("How old are you? ");<br>
                >>>> int x = in.nextInt(); // in is a
                Scanner<br>
                >>>><br>
                >>>> than<br>
                >>>><br>
                >>>> int x =
                Integer.parseInt(console.readLine("How old are you? "));<br>
                >>>><br>
                >>>> or with the JEP draft:<br>
                >>>><br>
                >>>> int x = Integer.parseInt(input("How old
                are you? "));<br>
                >>>><br>
                >>>> 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.<br>
                >>>><br>
                >>>> But why have a third API, i.e. "input"?<br>
                >>>><br>
                >>>> 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.<br>
                >>>><br>
                >>>> Cheers,<br>
                >>>><br>
                >>>> Cay<br>
                >>>><br>
                >>>> -- <br>
                >>>><br>
                >>>> Cay S. Horstmann | <a href="https://urldefense.com/v3/__http://horstmann.com__;!!ACWV5N9M2RV99hQ!IuXZk_tqIH8rEw1bD3uYb8UcIZF-nnoeFT3UG17pMO5EVXIYVRaAKi7XCq_T02HwnAek1wuV8Wed08w$" rel="noreferrer" target="_blank">https://urldefense.com/v3/__http://horstmann.com__;!!ACWV5N9M2RV99hQ!IuXZk_tqIH8rEw1bD3uYb8UcIZF-nnoeFT3UG17pMO5EVXIYVRaAKi7XCq_T02HwnAek1wuV8Wed08w$</a>
                | mailto:<a href="mailto:cay@horstmann.com" target="_blank">cay@horstmann.com</a><br>
                >>><br>
                >><br>
                > <br>
                <br>
                -- <br>
                <br>
                --<br>
                <br>
                Cay S. Horstmann | <a href="https://urldefense.com/v3/__http://horstmann.com__;!!ACWV5N9M2RV99hQ!IZrLgaQxOHBjUURoC5mWbfsijev257bb4C0DMamUDpoGqS5JMACpaMKsbUNQlWcGds7fifmS9sARC6aKMHEf$" rel="noreferrer" target="_blank">http://horstmann.com</a> |
                mailto:<a href="mailto:cay@horstmann.com" target="_blank">cay@horstmann.com</a><br>
              </blockquote>
            </div>
            <br>
          </blockquote>
        </div>
      </div>
    </blockquote>
    <br>
  </div>

</blockquote></div>