<!DOCTYPE html><html><head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  </head>
  <body>
    <font size="4" face="monospace">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.<br>
      <br>
      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.  <br>
      <br>
      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.  <br>
      <br>
      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.  <br>
      <br>
      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? 
      <br>
      <br>
      <br>
      <br>
      <br>
    </font><br>
    <div class="moz-cite-prefix">On 2/20/2024 6:44 AM, Cay Horstmann
      wrote:<br>
    </div>
    <blockquote type="cite" cite="mid:d351f8d9-5654-4f3e-a887-d9a2f9d9d024@horstmann.com">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.
      <br>
      <br>
      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.
      <br>
      <br>
      For my books, I need to decide what to do in the (n + 1)st
      edition. Should I stick with
      <br>
      <br>
      Scanner in = new Scanner(System.in);
      <br>
      ...
      <br>
      System.out.print("How old are you? ");
      <br>
      int age = in.nextInt();
      <br>
      <br>
      or switch to
      <br>
      <br>
      println("How old are you?");
      <br>
      int age = in.nextInt();
      <br>
      <br>
      or go all the way to
      <br>
      <br>
      int age = Integer.parseInt(input("How old are you"));
      <br>
      <br>
      I have no conceptual problem with in.nextInt(). I need to explain
      method calls early on, so that students can work with strings.
      <br>
      <br>
      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.
      <br>
      <br>
      As Brian says, there are too many conflicting goals.
      <br>
      <br>
      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.
      <br>
      <br>
      If the goal is convenience, it would be better to have more
      magically statically imported methods, in particular parseInt,
      parseDouble. Or readAnInt, readADouble...
      <br>
      <br>
      Cheers,
      <br>
      <br>
      Cay
      <br>
      <br>
      <br>
      On 19/02/2024 18.06, Brian Goetz wrote:
      <br>
      <blockquote type="cite">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">The classical program is:
          <br>
             input -> strings -> objects -> strings ->
          output
          <br>
        </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>
        <br>
        On 2/19/2024 7:25 AM, Remi Forax wrote:
        <br>
        <blockquote type="cite">I agree with Brian here,
          <br>
          as a teacher, you have to talk about parsing and formatting,
          those should not be hidden.
          <br>
          <br>
          The classical program is:
          <br>
             input -> strings -> objects -> strings ->
          output
          <br>
          <br>
          Rémi
          <br>
          <br>
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
          <br>
          <br>
              *From: *"Tagir Valeev" <a class="moz-txt-link-rfc2396E" href="mailto:amaembo@gmail.com"><amaembo@gmail.com></a>
          <br>
              *To: *"Cay Horstmann" <a class="moz-txt-link-rfc2396E" href="mailto:cay@horstmann.com"><cay@horstmann.com></a>
          <br>
              *Cc: *"Brian Goetz" <a class="moz-txt-link-rfc2396E" href="mailto:brian.goetz@oracle.com"><brian.goetz@oracle.com></a>,
          "amber-dev" <a class="moz-txt-link-rfc2396E" href="mailto:amber-dev@openjdk.org"><amber-dev@openjdk.org></a>
          <br>
              *Sent: *Monday, February 19, 2024 10:09:35 AM
          <br>
              *Subject: *Re: SimpleIO in JEP draft 8323335
          <br>
          <br>
              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>
              With best regards,
          <br>
              Tagir Valeev.
          <br>
          <br>
              On Mon, Feb 19, 2024 at 9:10 AM Cay Horstmann
          <a class="moz-txt-link-rfc2396E" href="mailto:cay@horstmann.com"><cay@horstmann.com></a> wrote:
          <br>
          <br>
                  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 class="moz-txt-link-freetext" href="https://openjdk.org/jeps/8323335">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 class="moz-txt-link-freetext" href="https://urldefense.com/v3/__http://horstmann.com__;!!ACWV5N9M2RV99hQ!IuXZk_tqIH8rEw1bD3uYb8UcIZF-nnoeFT3UG17pMO5EVXIYVRaAKi7XCq_T02HwnAek1wuV8Wed08w$">https://urldefense.com/v3/__http://horstmann.com__;!!ACWV5N9M2RV99hQ!IuXZk_tqIH8rEw1bD3uYb8UcIZF-nnoeFT3UG17pMO5EVXIYVRaAKi7XCq_T02HwnAek1wuV8Wed08w$</a>
          | <a class="moz-txt-link-freetext" href="mailto:cay@horstmann.com">mailto:cay@horstmann.com</a>
          <br>
                  >>>
          <br>
                  >>
          <br>
                  >
          <br>
          <br>
                  -- <br>
                  --
          <br>
          <br>
                  Cay S. Horstmann |
<a class="moz-txt-link-freetext" href="https://urldefense.com/v3/__http://horstmann.com__;!!ACWV5N9M2RV99hQ!JDq2P0DR423V62MvLF-CBrjfMSFshyy9lkQdQQPt5aEojp3WbQriYDtG-00NepYgsFay4aXHAQFHA24$">https://urldefense.com/v3/__http://horstmann.com__;!!ACWV5N9M2RV99hQ!JDq2P0DR423V62MvLF-CBrjfMSFshyy9lkQdQQPt5aEojp3WbQriYDtG-00NepYgsFay4aXHAQFHA24$</a> 
<a class="moz-txt-link-rfc2396E" href="https://urldefense.com/v3/__http://horstmann.com__;!!ACWV5N9M2RV99hQ!IZrLgaQxOHBjUURoC5mWbfsijev257bb4C0DMamUDpoGqS5JMACpaMKsbUNQlWcGds7fifmS9sARC6aKMHEf$"><https://urldefense.com/v3/__http://horstmann.com__;!!ACWV5N9M2RV99hQ!IZrLgaQxOHBjUURoC5mWbfsijev257bb4C0DMamUDpoGqS5JMACpaMKsbUNQlWcGds7fifmS9sARC6aKMHEf$></a>
          | <a class="moz-txt-link-freetext" href="mailto:cay@horstmann.com">mailto:cay@horstmann.com</a>
          <br>
          <br>
          <br>
        </blockquote>
        <br>
      </blockquote>
      <br>
    </blockquote>
    <br>
  </body>
</html>