<!DOCTYPE html><html><head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  </head>
  <body>
    <br>
    <blockquote type="cite" cite="mid:CAE+3fjZyJ_RZ-Mm5Wk6D+x0opRJ64QU2eJeHtMATFH4bVGem7A@mail.gmail.com">
      
      <div dir="ltr">
        <div>I guess, this is not about "give me more" but about "design
          a thing that will be really useful". If SimpleIO will not be
          satisfactory for solving 90% of student problems, then
          probably there's no point in introducing it at all? </div>
      </div>
    </blockquote>
    <br>
    I'm willing to have that discussion!  <br>
    <br>
    We could draw the line at "nothing", which is the status quo. 
    That's always an option.  <br>
    <br>
    We could draw the line at "println only".  That is surely better
    than nothing, succeeds 100% at hitting a very very limited goal, but
    is very .. limited.  But it is a perfectly viable candidate.  <br>
    <br>
    This proposal goes one step farther, giving the dual of println.  It
    also seems a viable candidate, and also seems better than nothing. 
    None of the proposals involving parsing numbers seem remotely good
    enough.  So they are all, currently, worse than nothing.   <br>
    <br>
    Now, better-than-nothing is surely not the bar, though.  One of the
    other considerations is "if we do more later, will progress be
    colinear with this."  Given the degree of restraint shown here
    (line-based console IO <--> String), it is hard to imagine
    concluding later "wow, we goofed there."  Could happen, but seems
    unlikely, because we are sticking to sensible primitives.<br>
    <br>
    <blockquote type="cite" cite="mid:CAE+3fjZyJ_RZ-Mm5Wk6D+x0opRJ64QU2eJeHtMATFH4bVGem7A@mail.gmail.com">
      <div dir="ltr">
        <div>Teaching Integer::parseInt or new Scanner() sounds
          contradicting to the idea of hiding irrelevant details like
          classes and static methods from the students until they need
          them. </div>
      </div>
    </blockquote>
    <br>
    But, hiding all irrelevant detail is not the goal!  Otherwise we'd
    be saying "Ask ChatGPT to write your program for you!"  What we're
    looking to hide (temporarily) is *linguistic* mechanisms whose value
    is in *structuring larger programs*, until such mechanisms *provide
    value to the program*.  Packages, access control, static, etc, are
    program-organization concepts, which benefit large programs but are
    a tax on small ones.  When your program is six lines long, it
    doesn't need help organizing itself, and even if it did, these
    things don't help yet, they just feel like a tax.  <br>
    <br>
    That's not to say we can't also try to simplify more things; this is
    the *first* JEP in this area, not the last.  But mission-creep is a
    real risk here, and whenever the goal is as diffuse as "simplicity",
    which everyone interprets their own way, mission-creep is almost
    inevitable.  So I don't object to "I want to solve this problem too"
    (we can keep talking, the JDK is a living entity), but I prefer to
    avoid "If you don't solve this problem, it's not worth doing
    anything."  Because there's always more we could do.  <br>
    <br>
    Here's a thought experiment: what concept would we want to guide
    users towards for going from string to number?  My favorite is
    exposing the pattern dual of Integer::toString.   The existing
    Integer::toString is a function int -> String; like most total
    functions, it has a pattern dual:<br>
    <br>
        if (s instanceof Integer.toString(int i)) { <br>
           // use i<br>
        }<br>
    <br>
    This is already much better than `Integer::parseInt`, because (a) it
    captures the inherent partiality of the operation, (b) you can't
    sweep the failure under the rug by ignoring NumberFormatException,
    and (c) the duality with the function `Integer::toString` is
    obvious; they are two sides of the same coin.  <br>
    <br>
    But there are many other possibilities, nearly all better than
    Scanner.  Its a totally valid discussion, but I'm not sure we should
    gate this installment on it?<br>
    <br>
    <br>
    <br>
    <blockquote type="cite" cite="mid:CAE+3fjZyJ_RZ-Mm5Wk6D+x0opRJ64QU2eJeHtMATFH4bVGem7A@mail.gmail.com">
      <div dir="ltr">
        <div>Because you'll need to explain what is Integer and what is
          parseInt. Or probably you can say "don't care now, just write
          it this way, we'll cover this later". But is it much different
          from public static void main? Why did we start all of this in
          the first place?</div>
        <div><br>
        </div>
        <div>When I was a child, I learned BASIC, and I remember that
          one could ask a number from a user typing simply INPUT X. The
          concept of string variables like X$ was introduced much later,
          so in my mind it was completely ok that we can ask for a
          number, without an intermediate step like ask for a string,
          then convert it to the number. Not sure if it was
          pedagogically correct but it certainly simplified my first
          steps in computer programming (well, paper programming in
          fact, as I had no computer).</div>
        <div><br>
        </div>
        <div>With best regards,</div>
        <div>Tagir Valeev.</div>
        <br>
        <div class="gmail_quote">
          <div dir="ltr" class="gmail_attr">On Tue, Feb 20, 2024 at
            3:34 PM Brian Goetz <<a href="mailto:brian.goetz@oracle.com" moz-do-not-send="true" class="moz-txt-link-freetext">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">
            <div> <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>On 2/20/2024 6:44 AM, Cay Horstmann wrote:<br>
              </div>
              <blockquote type="cite">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 href="mailto:amaembo@gmail.com" target="_blank" moz-do-not-send="true"><amaembo@gmail.com></a>
                    <br>
                        *To: *"Cay Horstmann" <a href="mailto:cay@horstmann.com" target="_blank" moz-do-not-send="true"><cay@horstmann.com></a>
                    <br>
                        *Cc: *"Brian Goetz" <a href="mailto:brian.goetz@oracle.com" target="_blank" moz-do-not-send="true"><brian.goetz@oracle.com></a>,
                    "amber-dev" <a href="mailto:amber-dev@openjdk.org" target="_blank" moz-do-not-send="true"><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 href="mailto:cay@horstmann.com" target="_blank" moz-do-not-send="true"><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 href="https://openjdk.org/jeps/8323335" target="_blank" moz-do-not-send="true" class="moz-txt-link-freetext">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$" target="_blank" moz-do-not-send="true" class="moz-txt-link-freetext">https://urldefense.com/v3/__http://horstmann.com__;!!ACWV5N9M2RV99hQ!IuXZk_tqIH8rEw1bD3uYb8UcIZF-nnoeFT3UG17pMO5EVXIYVRaAKi7XCq_T02HwnAek1wuV8Wed08w$</a>
                    | <a href="mailto:cay@horstmann.com" target="_blank" moz-do-not-send="true">mailto:cay@horstmann.com</a>
                    <br>
                            >>> <br>
                            >> <br>
                            > <br>
                    <br>
                            -- <br>
                            -- <br>
                    <br>
                            Cay S. Horstmann |
                    <a href="https://urldefense.com/v3/__http://horstmann.com__;!!ACWV5N9M2RV99hQ!JDq2P0DR423V62MvLF-CBrjfMSFshyy9lkQdQQPt5aEojp3WbQriYDtG-00NepYgsFay4aXHAQFHA24$" target="_blank" moz-do-not-send="true" class="moz-txt-link-freetext">https://urldefense.com/v3/__http://horstmann.com__;!!ACWV5N9M2RV99hQ!JDq2P0DR423V62MvLF-CBrjfMSFshyy9lkQdQQPt5aEojp3WbQriYDtG-00NepYgsFay4aXHAQFHA24$</a> 
                    <a href="https://urldefense.com/v3/__http://horstmann.com__;!!ACWV5N9M2RV99hQ!IZrLgaQxOHBjUURoC5mWbfsijev257bb4C0DMamUDpoGqS5JMACpaMKsbUNQlWcGds7fifmS9sARC6aKMHEf$" target="_blank" moz-do-not-send="true"><https://urldefense.com/v3/__http://horstmann.com__;!!ACWV5N9M2RV99hQ!IZrLgaQxOHBjUURoC5mWbfsijev257bb4C0DMamUDpoGqS5JMACpaMKsbUNQlWcGds7fifmS9sARC6aKMHEf$></a>
                    | <a href="mailto:cay@horstmann.com" target="_blank" moz-do-not-send="true">mailto:cay@horstmann.com</a>
                    <br>
                    <br>
                    <br>
                  </blockquote>
                  <br>
                </blockquote>
                <br>
              </blockquote>
              <br>
            </div>
          </blockquote>
        </div>
      </div>
    </blockquote>
    <br>
  </body>
</html>