Meanings of underscore (including wildcard imports)

Some people will freak out about me proposing this: Now that all other confusing uses of _ will be eliminated PR 11231, PR 11240, should we also clean up import syntax? I am pretty much in line with the proposals of @lihaoyi and @julienrf how to go about this. Concretely

  • Replace wildcard import _ with *, which is what basically all other languages use
  • Replace renaming => with a soft keyword as.
  • Allow as outside braces, as in
    import scala.collection.mutable as mut
    import NumPy as np
    

I would probably still keep “as _” for an import exclusion. Having a separate except clause is nicer but also more complex, syntactically. It would give rise to new feature interactions, so it would be more risky to do, in particular at this late stage. So I’d rather go with

  import p.{a as _, *}

because its rules follow directly from the combination of renaming imports and wildcard imports.

15 Likes

Would this disable * as a method name? (bacticks excluded) What about an object * I might want to import?

Of course not. Only if you want to import a thing named “*” you have to put it in backticks:

  import foo.`*`
1 Like

I’m not sure, does Scala allow having both * and *?

I don’t even freak out anymore. I knew this would happen. It started with “small, obviously better changes”, and we couldn’t resist against doing that 2 weeks away from an RC1. So now it’s growing into “a better thing but that impacts basically every file in existence”. Over the week-end we’ll have something even bigger.

I’m not freaked out anymore; I’m just discouraged.

All these things could wait until a later release. We can keep improving the language after 3.0. Thinking that everything must be perfect at 3.0 is a chimera.

14 Likes

We will improve the language after 3.0, but we will not be able to do fundamental syntax changes anymore, simply because all the docs and tutorials, including printed books and online courses have to stay relevant for a long time. So after 3.0 we are back to the more settled standard way of language evolution, which means:

  • don’t break existing code on a large scale (except for things that we have announced for removal today, so they won’t be in the Scala 3 docs)
  • add new capabilities only
  • concentrate on more advanced and specialized use cases.

So if something is confusing today, we cannot decide to remove it once 3.0 has shipped. We can add stuff, but I fear that will not make anything less confusing.

4 Likes

We can also deprecate old stuff and introduce better replacements. This happens all the time, even in minor versions.

7 Likes

Honestly I don’t find the case for renaming => to as convincing. How many times in the past decade have you heard someone say the import => syntax is confusing? I haven’t heard that at all.

w.r.t. the 3.0 release candidate, honestly what I have been saying for a while is that the release timeline is crazy aggressive, especially given the “fix all the tiny things” attitude that we have taken. Here’s a strawman timeline that would make the current attitude towards Scala 3.0 work:

  • Mid Q1 2021 - Mid Q2 2021: 3 months consolidate the current feature sets:

    • Write Design-Docs/SIPs for every language change. Most of these changes are pretty trivial, and writing a good design doc should take <1day to write, so with 1-2 people working on this would allow us to cover 50-100 different features in good detail. Design docs aren’t just for the sake of arguing and discussion, but will also sharpen our own thought processes and ensure the features are thought through
    • Continue expanding the community build (e.g. including Fastparse, Ammonite, etc.): the more real-world code we can get onto Scala 3 at release, the more we can exercise the featureset and the more confident we will be that we do not have any fundamental blockers to upgrades or missed opportunities for improvement (e.g. in the metaprogramming API, which is too large and complex to review just via SIP)
  • Mid Q2 2021: Release the RC1 together with (1) all the SIPs and (2) the expanded community build.

  • Mid Q2 2021 - Mid Q3 2021: 3 months for discussion and review. The expanded community build and SIP writeups would let people exercise the featureset in more-than-toy use cases, and take part in the discussion on the SIPs with the ability to try things out and give better feedback.

    • This also gives time to implement whatever changes arise from the SIP discussions and feedback; I don’t know what the changes will be, I assume there will be some
  • Mid Q3 2021 - Mid Q4 2021: 3 months of burn down period: no more feature changes. Just time to exercise things more, try and find bugs, fuzz test, improve error reporting, all the standard polish things that are easily overlooked during the ideation-and-feature-implementation phase.

  • Target release of Scala 3.0 in Mid Q4 2021

IMO there’s nothing inherently wrong with a “try to fix all the long-standing problems” release, we just need to be realistic about the timeline necessary to properly execute it. Given that we’ll be living with the results for the next decade, I think spending a few more months to let the process breathe would definitely be a worthwhile investment.

(This is a strawman timeline I made up in the last 5 minutes, the exact dates can be argued over and tweaked)

13 Likes

“Where will it stop?” is a fair question about doing syntactic changes so late in the game. I believe many will agree that eliminating all confusing uses of _ will make the language simpler. But is that just a drop in the bucket, or the last thing to arrive at a local optimum?

Maybe it is best to take the bull by horns, and make a list of all things that are syntactically more obscure than they need to be. I am not talking about missing constructs, these can be added later. I also don’t want to start to bikeshed. Let’s just pick things that

  • are widely used,
  • are obscure or confusing the way they are defined today,
  • would have simple fixes so that
    • code becomes clearer, and
    • existing code can migrate without problems

I did a run-down through the syntax summary, and came up with the following candidates:

  1. @unchecked annotations for pattern matches that fail

    def f(xs: List[Int]) = 
      val y :: ys = xs @unchecked
      ...
    

    It’s definitely an improvement over silent failures in Scala 2, but the @unchecked feels a bit clunky and vaguely hostile. On the other hand, replacing an annotation with special syntax could also be done later, so no urgency here.

  2. Interaction between union types and pattern alternatives

    case x: A | B   => // these are two patterns
    case x: (A | B) => // now it's a union type test
    

    Here the problem is migration. We could change the operator precedence so that x: A | B was a single type test, but that could change the behavior of existing programs. So I think we have to live with it.

  3. Self types

    x: T =>
    

    is a syntax that is not obvious and has to be memorized. Arguably, the underlying concept is not obvious to most people either, even though it is quite general and fundamental. I believe the best way forward here is to investigate a more general “MyType” construct that can supersede self types. This would mean providing a standard name for the type of this, and allowing to override its definition. Ideally, that name would be This. So instead of

    trait T:
      this: U =>
      ...
    

    we’d write

    trait T:
      type This <: U
      ...
    

    This looks strictly more powerful then self types. But it’s something that has to be done later, and
    can be done later. So, over time self types could be superseded. The only question would be: should we reserve the name This for a type now, to make it possible to do this later? I believe that would be a pretty hard breaking change, as This is quite common in codebases. So, I am not at all sure we can do this.

That’s all from my side for now. Three candidates, but neither can or should be changed for 3.0. So maybe confusing _ was the last major thing to fix for 3.0?

If you have other candidates please mention them. But please, stick to the criteria: a widely used existing feature that is obscure or confusing and can be easily changed with a straightforward code migration path. So," let’s add feature X", or “let’s drop feature Y since I don’t use it” won’t qualify.

Sure, but we cannot deprecate stuff that is in every text book or online course. That would be suicidal.

5 Likes

And yet we’ve deprecated and removed:

  • procedure syntax
  • do..while
  • symbol literals

to name just the few that I am convinced are in every Scala 2 book.

4 Likes

I think that I agree with @lihaoyi. It is important to draw a line, and I would say that we shouldn’t introduce any new features into Scala 3.

But I do also think that it is really important to make sure that the features and new syntax that has been added/introduced work really well together, and to try and remove as many warts now as possible. Particularly, if the plan is to disallow breaking changes after this.

I know that many of the key contributors have been putting in a huge amount of effort to achieve this, but it would be a shame if Scala 3.0 was released a little bit early with some syntax annoyances that could have been fixed. E.g., it is not really clear to me that everyone is convinced that the indentation discussion has necessarily reached the right place.

So, I support reasonable syntax changes for Scala 3 that make the language more consistent with itself (or other languages).

I would really like to see an extended stable beta release of Scala 3 with libraries with many of the libraries ported or compatible. This would allow a wider group of folks to play with the new version of the language, provide feedback, and make sure that everything is right.

1 Like

When to release is a difficult tradeoff. Nobody is ready to put a lot of work into something that will be released a year from now. The Scala Center and the community have done a carefully synchronized ramp-up to the release date. The initially foreseen release date has slipped by 6 weeks now since some constructs (mostly, meta-programming) were still in flux and since library authors felt they needed more time to test their ports. It might slip another 6 weeks; we’ll decide that next week. But that should be as far as it goes.

1 Like

Of course! Scala 3 is when the books are rewritten (and the MOOCs are re-recorded). That’s something you can do maybe once in a decade.

2 Likes

It’s worth pointing out that @lihaoyi’s suggested plan for 2021 is exactly what we have done … in 2020! We have been through all this, a year ago. Since December we were supposed to be in the “it’s freezed” mode. The last language changes were supposed to be introduced 6-12 months ago, not yesterday.

7 Likes

The ‘_’ character is a source of joke about scala, so IMHO: it is a very good improvement.

This is something I would like to avoid, but my computer algebra project currently relies on operator imports. Here is an example with +. So please can we avoid this.

But your project in the community build does not contain an import of *! Nor does any other project in the community build, with more than 1M lines of code inspected. So yes, it’s a theoretical possibility but it does not seem to be very common in practice.

2 Likes

The case with + above is perfectly symmetrical. I will need to import * (or change my design in a yet unknown way).

Using backticks to import * seems like an OK approach though? I have to reference or import things with backticks all the time (e.g. all the Java fields and methods called .type…) and it’s not really any less convenient than non-backticked imports

5 Likes

Problem is, it’s the user facing interface, so the simpler the better. It’s even meant to be an interactive, scripted interface. In fact, import is already to much in this respect, so I suppose I will have to rework my design anyway.