Pre-SIP: cosmetic enhancements

Scala allows certain customizations, and I would like to propose a few that I would like to see as standard in Predef. These are purely cosmetic, but I have been using them for many years, and I think they would make the language more elegant.

type Real = Double // better name for Double
type Bool = Boolean // shorter name for Boolean
type Text = String // better name for String

def not(b: Bool) = ! b // clearer form of not!

The name “Double” has always bothered me. Double what? Double dip? Double trouble? It is an historical anachronism from back when a 64-bit number was a “double” on a 32-bit CPU. For all but small embedded systems, that hasn’t been the case for many years.

Beyond that, floating-point numbers are intended to represent real numbers, and they do an excellent job of it for most practical purposes, so why not name them for what they are intended to represent?

As for “Bool”, it is just a shorthand for “Boolean”, just as “Int” is short for “Integer”.

The name “String” also bothers me. String of what? String of pearls? Why not just call it what it is, namely “Text”? As far as I can tell, that is the perfect name for it – and shorter too. Yes, “String” has a long history in programming, but it is still a poor name as far as I am concerned. Isn’t good naming an essential part of programming?

As for “not”, I think it is a clearer alternative to “!”. And the fact that it requires parentheses makes it clearer what it applies to. It’s a win-win as far as I am concerned, although “!” is still great for succinctness. I use the latter only when I am trying to avoid a line wrap.

2 Likes

Since it’s a matter of personal taste, and can be easily solved by creating your own aliases, I don’t think there is merit in changing the language for it.

9 Likes

They don’t, though. An online search in your favorite engine for “why not use float to represent real numbers” will yield dozens of articles like this one: The Dangers of using Float or Real Datatypes

I don’t think it’s fair to paint the problem as a black and white issue. Good names are important, but so is precedent. Virtually all programming languages use String. We would need extremely compelling evidence that this name is actively harmful to make a creative choice.

Consider all people coming from one of these other languages. They will expect the standard library to define String. It would be awkward to tell them the reason they can’t find it is that the term “string” was bothering us.

Also consider interoperability with Java’s environment. Scala is a host on the JVM, and the JVM has established a few built-in names. Those include, among others, Double, String and Short (which is also a debatable name by the way)`. That is another quite compelling argument to avoid making creative choices.

Some languages have been able to get away with other names for these data types. For example, Rust uses i64 for Long and i32 for Short. But these languages typically don’t make a point about being interoperable with Java and/or have good reasons for baking information about memory representation into type names.

By your own admission, people would still be tempted to use ! for succinctness. Therefore introducing an alias would just create an arbitrary choice for developers. Uniformity is a nice thing to promote for a programming language because it makes sharing code across different codebases, documentation, and educational material easier.

4 Likes

It wouldn’t really be “changing the language.” It would simply add a few aliases for commonly used types that are poorly named by tradition.

To avoid any misunderstanding, I am not proposing that the original names be eliminated or deprecated. I may be a naive, but I am not that naive.

I also understand that naming conventions are largely a matter of personal taste, but if you consider your code a work of art you want good names that correspond as closely as possible with the domain. When real numbers are used in an algorithm, they are never thought of as “doubles”. That name is a historical artifact of computer word sizes and has nothing whatsoever to do with the domain of the problem they are being used to solve.

I figured I’d get this kind of response, but I decided to take a shot at it anyway. I’m just trying to make programming in Scala a bit more of an art form. Yes, I realize that I can easily do what I am suggesting for my own code (and I have), but I would like to gently encourage others to do the same if they wish.

Yes, it would add a slight overhead to reading code, but I think it would be worth the trivial effort. Anyone who has difficulty understanding what a “Real” is or what “Text” is must be either completely swamped by their workload or perhaps just very rigid in their thinking.

I have a few replies below.

That is all true, but it is also irrelevant to the issue here. Similar claims could be made for Int, which does not represent all integers. The simple fact is that so-called “Doubles” are intended to represent real numbers, and how well they do it is not the issue. If I use a type called Aircraft in a simulation, it is not a real aircraft.

Again, I am not suggesting that the traditional names should be eliminated or deprecated. Moreover, any programmer who has difficulty figuring out or remembering what “Text” means should perhaps be in another line of work.

I don’t understand why Java naming conventions must be carried over completely to Scala. I thought Scala was supposed to be an improvement over Java.

Anyone who has trouble figuring out or remembering what “not” means should perhaps consider another line of work.

I think it belongs in Predef if you’re proposing to deprecate and eliminate the old names.

Conversely, though, I wish there were a stronger sentiment that “local domain” were a thing.

Probably there is concern that if we have “local names for things” then onboarding new hires would be a chore.

But local Predef should be the norm, not the exception. The rhetoric around “more than one of doing something undermines” something (such as the integrity of the language? our common heritage?) is quite limiting.

And it is belied by the obvious division of the community into camps by “stack” and “style”.

Worth adding that, as in all the arts, the art is in the process, not the product. Probably we fall into the trap of associating “process” with the corporatization of programming, that is, the assembly of groups or “teams” to build “products”.

1 Like

We can’t get rid of the old names because they’re ubiquitously used.

And having fewer names is, overall, a win, because there are fewer things to remember.

Furthermore, people probably have already used Real, Text, etc., for their own things in their own code. (I’ve certainly used Text myself on more than one occasion.)

So, therefore, no, as appealing as it might be when designing from scratch, it’s not a good idea.

4 Likes

I’m not sure how “local Predef” would be implemented. Is it possible to get the effect of an import in all files in a package without adding the import statement in each file? Just wondering.

1 Like

It is available as -Yimports, which by default is -Yjava.lang,scala,scala.Predef to specify the “root imports”. You can specify packages and static objects.

I think the feature is underutilized, and it’s still a -Y “forking” option.

3 Likes

I’m entirely sympathetic to that goal – I’m a great believer in coding-as-art – but the problem is, you’re injecting your own aesthetics, and that’s intensely subjective. On an aesthetic level, I mostly disagree with these suggestions. (Which is why these belong at the codebase level, not in the language.)

Basically, you’re arguing that your suggestions are clear improvements, but none of that is clearly objectively true. Indeed, by making the artform analogy, you’re implicitly admitting how fundamentally subjective this point is…

5 Likes

Is “-Yimports” or something equivalent available for Scala 3? I don’t seem to see it.

I see the PR adding this feature landed in 3.3.0, and it’s in the list of available flags when I run scalac -Y.

OK, thanks, but where can I find instructions on how to use it? I tried it the obvious ways one might expect, and it did not work for me.

This page seems to show that it is not available in 3.3:

While we’re at it, that page also shows that -Xcheckinit in 2.13.x is now
-Ycheck-init in 3.3.x, but that did not work for me. (I tried -Xcheck-init too, but that didn’t work either.)

I’m starting to understand why people get frustrated with Scala documentation.

➜  scalac -Yimports:help
Specify a list of packages and objects to import from as "root" imports.
Root imports form the root context in which all Scala source is evaluated.
The names supplied to `-Yimports` must be fully-qualified.

For example, the default scala.Predef results in an `import scala.Predef._`.
Ordinary access and scoping rules apply. Root imports increase the scoping
depth, so that later root imports shadow earlier ones. In addition,
names bound by root imports have lowest binding precedence, so that they
cannot induce ambiguities in user code, where definitions and imports
always have a higher precedence. Root imports are imports of last resort.

By convention, an explicit import from a root import object such as
Predef disables that root import for the current source file. The import
is disabled when the import expression is compiled, so, also by convention,
the import should be placed early in source code order. The textual name
in the import does not need to match the value of `-Yimports`; the import
works in the usual way, subject to renames and name binding precedence.

Thanks, but that doesn’t answer my question. Since this feature is apparently undocumented, let me show what I tried and the result I got. In my build.sbt file (using Scala 3.3.1 and sbt 1.9.6 on Linux), I added this line:

scalacOptions += “-Yimports scala.* java.lang.* Predef.*”

I thought this would explicitly invoke the standard default imports, then later I could add my own. However, here is the result I got back:

[warn] bad option ‘-Yimports scala.* java.lang.* Predef.*’ was ignored

Any suggestions?

"-Yimports:scala,java.lang,scala.Predef" should do what you want I think

1 Like

That line appears to be valid, but it does not do what I expected it to do, which is nothing. In other words, I was expecting that line to provide the same default imports that I would normally get without using it, but it doesn’t.

Can anyone tell me how to add my own custom local Predef? This shouldn’t be difficult, but I can’t seem to find any documentation on it.

Here is everything you need play3/build.sbt at main · DevInsideYou/play3 · GitHub

1 Like

Can you clarify how you used it? Example usage, etc? And how you determined that it wasn’t working?

I use this in every build and it works perfectly. I suppose I’m not 100% sure if the default order puts scala or java.lang first, but in my usage I set -Yimports to java.lang,scala,scala.Predef,my.Predef and I’m able to use any symbol from any of those imports without explicitly importing.

Sure, I just put the following line in my build.sbt file (as I wrote earlier, I am using Scala 3.3.1 and sbt 1.9.6 on Linux):

scalacOptions += “-Yimports:scala,java.lang,scala.Predef”

I say it "isn’t working’ because I get a long list of error messages due to unrecognized symbols that I don’t get without this line.