Skip to content

proposal: new package simpleinput for Go education #54646

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
earthboundkid opened this issue Aug 24, 2022 · 7 comments
Closed

proposal: new package simpleinput for Go education #54646

earthboundkid opened this issue Aug 24, 2022 · 7 comments
Labels
Milestone

Comments

@earthboundkid
Copy link
Contributor

earthboundkid commented Aug 24, 2022

Problem:

Every few months, someone will post on Reddit that they're trying to make an intro-to-CS-style input prompt and they're confused why their code using fmt.Scan* doesn't work (inevitably because the input has a space and they're ignoring errors). I think that just for the use case of education, there should be something that does the thing that they want.

Here are some typical posts

You can find more by searching site:https://reddit.com/r/golang fmt.scan.

New programmers aren't a huge source of new Go users. On the other hand, even if most Go programmers come from other languages, it is convenient when learning the language to be able to write simple intro-to-CS-style question/response programs to get a feel for the language. There ought to be something very simple that novice can find without knowing anything about Go or IO. Obviously, novices could and probably should be using bufio.Scanner already, but they aren't finding it today. Instead they look in fmt (because they are already using fmt.Println) and find the Scan family of functions. Then they are frustrated when it does not work the way they expect.

Proposed solutions

Proposed solution one is simple: mark the Scan functions as deprecated to hide them from the documentation. Even Rob Pike regrets adding the Scan functions.

Proposed solution two is to add a new package just for education purposes.

// Package simpleinput provides function for basic reading from os.Stdin.
// Functions in this package are concurrent safe. 
// For better performance, use bufio directly.
package simpleinput

// SetSource sets the source that simpleinput buffers reads from. 
// By default, it uses os.Stdin.
func SetSource(io.Reader)

// Source returns the underlying io.Reader that simpleinput buffers reads from.
func Source() io.Reader

// Error returns the last error encountered by simpleinput, if any.
func Error() error

// Read reads from Source until it receives an error, typically io.EOF.
func Read() string

// ReadLine reads one line from Source. It removes "\n" and "\r\n" from its output.
func ReadLine() string

// ReadLines reads Source until it encounters an error.
// It removes "\n" and "\r\n" from its output.
func ReadLines() []string

Obviously, none of this is strictly necessary, but it would help Go learners, particularly those new to programming.

Cf. #54620


Edit: Proposed API is now:

// Package simpleinput provides function for basic reading from os.Stdin.
// Functions in this package are concurrent safe. 
// For better performance, use bufio directly.
package simpleinput

// SetSource sets the source that simpleinput buffers reads from. 
// By default, it uses os.Stdin.
func SetSource(io.Reader)

// Source returns the underlying io.Reader that simpleinput buffers reads from.
func Source() io.Reader

// Read reads from Source until it receives an error. 
// If the error is io.EOF, it returns a nil error.
func Read() (string, error)

// ReadLine reads one line from Source. It removes "\n" and "\r\n" from its output.
// If it encounters an io.EOF, it returns the error.
func ReadLine() (string, error)

// ReadLines reads Source until it encounters an error.
// It removes "\n" and "\r\n" from its output.
// If the error is io.EOF, it returns a nil error.
func ReadLines() ([]string, error)
@gopherbot gopherbot added this to the Proposal milestone Aug 24, 2022
@mvdan
Copy link
Member

mvdan commented Aug 24, 2022

How about https://go.dev/doc/faq#x_in_std? Suggesting better fmt scanning APIs is one thing, but if these APIs are primarly for new Go users, perhaps they should be in a Go module that's aimed and promoted to them.

At the same time, I would be conflicted with APIs targetting new users only. If I was learning a language, I would want to learn the "normal" or "proper" way to accomplish a task, not an oversimplified way that I should probably un-learn later.

@earthboundkid
Copy link
Contributor Author

I think it's a fair question whether Go wants to target new users or just say that's out of scope. However, if the decision is made that targeting new users is in scope, then I don't think it makes any sense to use an external module. You can already find tons of simple input packages online. The forums are full of people who haven't found those modules because they are looking in the standard library for the equivalent of Python's input() and then they get burned using fmt.Scanln instead of what they really want.

@mvdan
Copy link
Member

mvdan commented Aug 24, 2022

I don't think it would be fair to say Go doesn't target new users. It has grown in users at a very fast pace for ten years now :)

It is true that new users will tend to look at the standard library first, but ultimately they will follow the advice of the documentation they are reading. There are plenty of "blessed" modules outside of the standard library, such as x/crypto and x/net, and they don't need to be in the standard library either. If there was a module like x/guardrails or somesuch, I'm sure that pages like tour.golang.org could point to it or show examples with it.

@earthboundkid
Copy link
Contributor Author

The question of why should this be in the standard library seems really straightforward to me. A simpleinput package really only has value in the standard library. The point is to make learning Go easier, and as soon as modules are involved, it's not easier.

If I was learning a language, I would want to learn the "normal" or "proper" way to accomplish a task, not an oversimplified way that I should probably un-learn later.

In Python, practically all learning material uses print() and input(), neither of which did I ever use in my years of programming Python professionally. It's normal to have an inefficient "easy mode" for beginners. Learning styles vary and some people want to learn the "real" way to do things earlier and some people prefer to just get things working and fill in the details later. Neither is better than the other, but I think the latter style is more common.

@ianlancetaylor ianlancetaylor moved this to Incoming in Proposals Aug 24, 2022
@ianlancetaylor
Copy link
Contributor

If we are going to do this, I think we should be more idiomatic and have Read and friends return (string, error).

@apparentlymart
Copy link

After reading this proposal I tried to think about some typical "getting started"-type programs that I might write when learning a new language. It's harder for me to relate to "learning to program" because it's been some time, but I also tried to remember some early programs I wrote.

Here's what came to my mind:

  • Number guessing game: generate random number, prompt for guesses, and print "guess higher", "guess lower", or "correct!" depending on the relationship between the guess and the goal number.

    I distinctly remember writing a program like this in my early experiments with programming, based on an example in the Commodore 64 User Guide. This sort of prompt-and-response program seems well-suited to the API described here, if combined with the writing functions in fmt.

  • Advent of Code challenges: I have anecdotal evidence that some participants in Advent of Code intentionally choose a language they don't already know and use these challenges as a vehicle for learning.

    Advent of Code challenges commonly involve loading in some data in a simple delimited text format, doing some computation based on that data, and printing a result. ReadLines from this proposal in particular seems like it would be useful in many of the Advent of Code challenges.

I don't really have a strong opinion about whether it's important to make these easy to achieve, but similar features do seem to help learners of Python as stated above, and Python is typically presented as an example of a good language for beginners. My examples above also lead me to think that the simpleinput API proposed here would be useful for the sorts of problems I can imagine beginners wanting to use to experiment.

I share Ian's sense that it would be better to make these functions follow typical Go error handling patterns, because that seems like an important part of Go's design that I'd want to learn quickly as a beginner, rather than deferring until later. I think a Read (string, error) does still address the stated problem with fmt.Scanf where beginners don't think to check the error, because they'll want to access the string result and therefore won't be able to avoid learning how to handle multiple return values, which will hopefully then encourage learning what to do with that error result. The pattern they'd learn doing that will then set them up for working with almost every fallible API in Go.

@seankhliao
Copy link
Member

I think this optimizes for the wrong thing especially with globals.
Proposals, if any, should probably be focused on the existing bufio.Scanner type.

@seankhliao seankhliao closed this as not planned Won't fix, can't repro, duplicate, stale Apr 6, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
Status: Incoming
Development

No branches or pull requests

7 participants
@apparentlymart @earthboundkid @ianlancetaylor @mvdan @gopherbot @seankhliao and others