Making io.StdIn.readLine imported by default

As we know, we can use print and println without any imports. Thanks to this, we can teach beginners how to print something.

However, reading something is tough in current package hierarchy (We should call io.StdIn.readLine). We have to teach about packaging system before reading something.

Shouldn’t readLine also be able to be called without additional import so that it is symmetrical with println? (And making it called as readln)

When I was Scala newbie, that was painful experience. Making readln sibling to println is worthy for everyone.

4 Likes

Agreed, currently I’m exporting it project-wide in a package.scala file to avoid either importing it in every file or writing io.StdIn.readLine everywhere.

I think this question was just raised somewhere? I remember because I’ve never used readLine, and I thought to myself, “I’ve never even used readLine.”

Note that println is just a forwarder in Predef.

You can also roll your own, call it Postdef, with -Yimports (without replacing Predef and without waiting for the SIP or unfreezing the library, etc).

3 Likes

The problem with this solution of course is that it isn’t very beginner friendly in itself
What’s Predef ?
What’s -Yimports ?
What’s a compiler flag ?
What’s a compiler ?

(Those are all rhetorical)

In the short term, it might be a good solution for teachers, as long as they explain at some point that you need an import outside of the course environment
(And possibly later still about the whole -Yimport solution)

1 Like

Everyone is a beginner at first. Do you remember the day you first started programming? Or the first time you baked bread, or whatever.

First, they perform simple integer addition with a REPL or Scastie, then they learn strings, and so on. They don’t know anything. Or of compilers, or of Predefs.

These beginners don’t even know how to define the package or methods yet.

To successfully build a beginner’s mental model, it is wise to start with what they already know. We need to proceed gradually.

We should just say: “do readln, do println. that’s all”.

Obviously, we should not assume that the novice should be able to set the compiler’s compilation options. That would only make things more unintelligible to the novice.

2 Likes

Yep! :+1:

export solution works well for me at the moment… I just say “use readLine”. REPL / worksheets don’t need/use it, so it comes a bit later when we use “proper files”.

When it’s time to explain imports/exports I say: “that readLine was something I exported for you all along…”

2 Likes

I totally agree. If there is only one thing I was
allowed to add to Predef it would be a forwarder from readln as this would make the initial steps of a complete beginner much easier out of the box to do simple text-based terminal apps. Every extra hurdle for the complete beginner is a downside. A complete beginner does not know what import or package is.

2 Likes

I don’t think Predef is the right place to add such things. Predef is not a teaching environment, it’s part of the core language.

Already having println there (which should be actually called printLine as the convention is camelCase, and abbreviations in code are pure evil) is imho kind of a smell. (One could argue that it’s a basic and necessary debugging aid, and I would agree I think).

If you want a teaching environment a toolkit would be the right place, imho.

Toolkits could come with their own “Predefs”. (I think that would be new?)

Than you would just have one “using clause” to get your teaching environment; which could be designed by you however you please, for example with other short static aliases for most basic operations, before going into OO modularity later on.

This way your teaching environment would not “pollute” global namespace for all users of Scala. Because you know, using readLine is actually extremely seldom, up to none existent, in real world applications (maybe besides “scripts”, which are a quite new type of application for Scala). In real apps you would usually use proper CLI handling libs (e.g. some readline wrapper) which offer all the common things like parsing of complex commands, flags and options, validation, and instantiation of classes from user input, etc.

I think the whole toolkit thingy is much underrated currently and does not expose its whole potential. But it’s actually a pretty good idea if executed consequently: You could have custom “Scala environments” with all kinds of task specific functionality out-of-the-box just by using the right toolkit. Curated toolkits as modular but opinionated frameworks (maybe with some online registry / catalog) would be really awesome!

Scala as language is extremely unopinionated, and that’s a good thing. But if you have a specific task at hand you prefer most of the time to use something already specialized to that task, “right tool for the job”, and so (except you want to build your own super customized and specialized thing, than something like vanilla Scala is actually great OOTB). Toolkits are the right way to resolve this dichotomy I think. They could give you some ready to use environment that’s specialized to some task category.

At the same time toolkits are very flexible. In the end they’re “just” a collection of libs, and maybe a custom Predef if this idea gets picked up. As your app grows and your requirements get less typical you can easily fork and modify a toolkit definition, or ditch it completely and replace with module specific sets of imports (even it seems that people are actively asking to have some “common imports” quite often; there is an active thread about that again; easy to create custom toolkits could also solve this issue).

So I would really like to see the toolkit feature expand and get some more love.

I think it could work like the task meta-packages in Debian Linux. In case you’re not familiar with this concept have a quick look at the following:

https://wiki.debian.org/metapackage

https://packages.debian.org/stable/metapackages/

Also note in the description of how tasksel works that it supports pre / post install scripts; a handy feature Scala toolkits could also pick up I think!

I would place this feature in Scala-CLI. (As it’s the “apt” of Scala). Something like:

$ scala-cli setup ${environment}

Result should be a project directory you could open with something like Codium with Metals. Maybe just containing a worksheet file with a using toolkit ${environment} line, maybe a full blown pre-configured project with some skeletons, or whatever is typical for the task at hand…

But it could also install all kind of stuff (like Codium + Metals if not present, or WSL on Win) if the pre / post install scripts idea was implemented.

2 Likes

I strongly disagree with this. Just because something is useful for teaching doesnt mean it should be in the standard library. Production codebases around the world should not have random teaching fixtures included by default.

The correspondance between println and readline is also not useful, because println is used in a wide variety of systems many orders of magnitude more often than readline. People use println in production all the time

If we need a teaching/getting-started distribution of Scala we can provide one. That’s what Scala-CLI is trying to be. Ammonite works too. Both provide one-click push button downloads and installs. But we should not make a habit of shoving teaching fixtures into the standard library just because its convenient for one narrow use case when it just adds bloat to all others.

1 Like

I almost never use readLine in teaching. I’ve seen too many unexpected behavior depending on whether I run a program from the terminal, or from IntelliJ, or from sbt.

I use command-line arguments for small inputs and file/URL I/O for larger ones (I write the I/O code). The one exception is programs that require a true user interaction (e.g., rounds in a two-player game) but I don’t have many of those. (In fairness, I also teach at a level where asking students to import my teaching library is not a big deal.)

1 Like

I have to agree with the last few replies: things in Predef show up in everyone’s scope, all the time. We should not pollute it with things that are use-case-dependent, even if that use case happens to be teaching to complete beginners. They can handle an

import scala.io.StdIn.readLine

with a single sentence of explanation like

This line tells Scala we want to use its “readLine” feature to read input from the command-line.

println was mentioned as something we wouldn’t use in a real-world application either. While that’s true in a committable state, clearly we use it transiently for debugging purposes. Having to go insert an import scala.io.StdOut.println to do some debugging, only to go and delete it again afterwards, would defeat the purpose. The same argument was used years ago to justify why ??? had to be in Predef to be even useful.

2 Likes

The custom Predef for toolkits sounds like a good idea, IMO. It could also solve some issues with alternative toolkits not being as easy to use.

For example, maybe the Typelevel tookit would include the usual imports in the predef ( cats.*, cats.data.*, cats.syntax.all.*… Maybe also cats.effect.*).
Users that just want to write a quick script with Typelevel libraries wouldn’t have to worry with that.

The Typelevel toolkit is also an example of something that might not want scala.io.StdIn.readLine in the Predef.

2 Likes

I recognize the arguments against Predef.readln and I don’t expect it to ever happen, but I would prioritize ease of on-boarding to Scala higher than non-pollution at a balance. And there is some precedence for prioritizing having a user input function in the global scope; Python has input, C has scanf etc.

image

image

I like the idea of generalizing the toolkit concept. I also like the idea of having a nice and easy to use terminal api in the default toolkit (as an alternative to Predef.readln). This could be a simple wrapper around jLine for the JVM and something similar for Native and JS+node with a unified api across platforms.

And readln is actually useful when doing (naïve) println debugging if you want to insert a simple “breakpoint”.

1 Like

To add some feature creep to the previous suggestions, I (still) think the promise of a “scalable” language includes that my environment knows whether I’m in “develop” mode or whatever the other mode would be (“CI”, “release”, “production”). That is besides “beginner” mode.

For some settings, I’m supposed to set something in sbt (-Werror by some mechanism).

I don’t want to enable settings or change lines in a script: I want my workflow to understand these modes and keep me from, for example, forgetting to update a check file or run a unit test before committing or pushing; or for example ??? becomes illegal (by some mechanism that is seamless to me) as well as println and other evils.

The other feature is enabling -Wunused at just the right time and with just the right amount of noise (aka motivation).

I was going to suggest that such a feature need not wait for A.I., but maybe A.I. is required to identify what “mode” my code looks like.

If I’m writing a hello world program, then I am a beginner by definition; if I am really coding up snippets for a tutorial, then it could notice that, too, by my use of mdoc.

“Test code” is also a mode, so maybe it infers that from the code adjacent to what I’m working on.

2 Likes

I really like the idea of stronger guarantees that are contextual, as for how to implement them, I hope it will inspire the tooling people !

On the other hand, I want as much predictability from my tools, and AI has certain … issues in that regard
But I think a separate thread might be better for this kind of discussion

IMO this individual API isn’t the end of the world, but as a decision making process its the wrong direction to embark upon. We should get used to using a specialized launcher/distribution for teaching, and we shouldn’t feel limited too what is in the standard library at all

e.g. I can’t imagine teaching someone Scala without having OS-Lib handy (the Java io/nio APIs are crazy verbose), Requests-Scala (“what do you mean i cant just make a request or download?”), or uPickle (“i can’t just save my data?”). For that matter, how do people survive in the REPL without syntax highligted pretty printed output? Do people just manually scan the line-wrapped monochrome output??? To me all that sounds way crazier than asking people to make a single import.

I wouldn’t mind having a standard teaching-specialized distribution with more standard helpers, so we dont have every individual teacher rolling their own. But students install whatever we tell them to, so installing amm or scala-teaching isnt any harder than installing scala, and comes with none of the downsides of putting it in the standard library

There are good arguments in favour of adding readLine to Predef and also against it. However, from the principle of least surprise I would say the same holds true for println. They go together imho. Or both in or both out of Predef (my favourite: both out).

And although println can be handy to debug a program, this is in most cases so for small (test) programs. Adding readLine would not matter in such a situation. For any serious programming you use a logging system, with Logger.debug or so.

And while we are at it, let’s just call it readln.

1 Like

No? Seriously, I work mainly on large-to-enterprise sized systems, and I still use println for debugging routinely, sometimes dozens of times a day. Heck, I even routinely use it for debugging even in pure-FP systems where it’s formally against the rules.

(Admittedly, println is mainly useful in test environments rather than running systems. But the debugging is mainly happening in tests.)

By contrast, I don’t think I’ve used readLine once, in 17 years of Scala programming.

Yes, they look a bit alike, but in my eyes it’s really a pretty apples-and-oranges comparison.

5 Likes

Interesting, I stand corrected in my assumption about the general use of println. I could not imagnine this, debug info on the console … :face_vomiting:

Well, you are right here too, the companion of readLine would be writeLine. :grin:

These differences also illustrate why it might be difficult to come to a consensus about the inclusion of readLine (or exclusion of println) for Predef. We all use it in different ways.