JEP 476 analog?

Managing import statements for libraries is one of the most common sources of “boilerplate time spending.”
I see Java now has JEP-476 for this purpose: JEP 476: Module Import Declarations (Preview).

Maybe we should think about adopting this to Scala?

Technically, I guess we will have the same problems as with wildcard exports
(see Idiomatic Imports
Zinc doesn't support wildcard-imported 'name' and 'implicit' shadowing · Issue #226 · sbt/zinc · GitHub )

WDYT?

2 Likes

I really like something like the following, and I don’t remember why it doesn’t work/is suboptimal

File 1

object myModule:
  import ...
  import ...
  import ...

  export *

File 2/3/4/…

import myModule.*

I feel like this is the Scala way of solving this

1 Like

Objects, packages, modules, package objects (again), “module objects”…

This is way too complex (and partly too redundant) for my liking.

Could all this be somehow streamlined?

I think people here are asking the right question. But I don’t know that would be the right answer. Embrace Java’s module system? Do something Scala specific? Some hybrid? I see trade-offs.

Let’s hope this thread gets some visibility! The question is not simple, more input is welcome I think.

I really like something like the following, and I don’t remember why it doesn’t work/is suboptimal

Here is an earlier write-up of some problems I encountered experimenting with such an approach.

I feel like this is the Scala way of solving this

Can you phrase the benefits in terms of some objective criteria?

To me, generating potentially thousands of runtime forwarders in bytecode, and then relying on the JIT to remove them from the running application, seems a roundabout, bloated and inefficient way to achieve a goal of composing together some import expressions into a larger expression.

We have language-level constructs for composing data (case classes) and operations (methods/functions). There is a need to compose the shared-structure in import expressions into larger units. IMO it will be simpler and more efficient to do that in the language layer, than co-opting a runtime mechanism that was initially designed for a different purpose.

3 Likes

There is a Scala 2 ticket to support interop.

The JEP is “pitched” toward coding in the small and beginners.

Even so, the tone is: people don’t know how to design packagings, so we have to invent features as a workaround.

The Scala solution will be, obviously, that importing is a capability.

Currently, it can’t figure out that nowarn is in scala.annotation.

But soon it won’t be necessary to add -Yimports:scala.annotation to the build. Of course, everyone who complained about it won’t need to remove it.

And maybe this would help scaladoc produce module documentation, an issue I saw floated about in some other thread.

Importing JVM modules is one thing. But what about creating them?

Java has a lot of “extra language” for that (syntax that you can only put into module definitions). But from Scala you can’t define modules, AFAIK.

Also there is even more redundancy if you think about such things like “directives”.

Should “directives” also be able to import modules?

Why do we actually need to “double import” things when using “directives”? (You need to write “using lib” and then “import lib”, or should this work, “using module” and “import module”).

This needs to be streamlined imho. It’s complex and redundant.

It only relies on existing syntax and feature, therefore extending it this way leads to a simpler language.
And it feels in line with other ways the language has been developed

Totally agree, which is why I think we should optimize away those forwarders ourselves (i.e. the Scala compiler should do it) !

I note that “optimize away those forwarders ourselves (i.e. the Scala compiler should do it” isn’t an existing feature of export, and would surely break the existing documented semantics? It was proposed, via transparent keyword, by @rssh previously… is that what you are suggesting/implying?

I agree supporting import via export is conceptually plausible. My challenge to anyone who advocates this approach, is to please try, in anger and at scale, using the existing export feature (and just ignore the bytecode bloat for now).

I have tried. I care about import boilerplate, I have a lot and wish I could eliminate it, it bugs me, and Ive tried what’s in Scala 3’s present toolkit to factor it.

As well as the bloat issues I raised here, and the current lack of support for wildcard * exports (also a current dealbreaker), there’s the unpredictable, almost emergent behavior of export itself.

It is far from a well-understood or (yet) widely-used mechanism.

  1. We get a first clue from the official docs: “Export clauses raise questions about the order of elaboration during type checking”.

  2. In this earlier thread about exporting in “overlapping” situations I raised more questions than I got answers. I repeat the appeal I made then: “Particularly, at the appropriate point in the solidification of Scala 3, export’s precise behaviour should be specified so we can confidently predict how it will work in a given situation.”

  3. And in this Scala Users thread on exporting across module boundaries, I raised issues with export semantics that were not documented.

My experience is that export is not yet a well-specified, stable feature that we could confidently layer additional features on top of. As well as the reality that the export of today does something different (ie runtime forwarders) to what’s needed for factoring out of repeated imports.

The import problem is pretty straightforward:

  • A module or an application wants to give a simple alias to a collection of imports spanning multiple packages.
  • Importing the alias imports the underlying collection.
  • Able to share and compose such aliases across libraries/modules, the same way we can share or compose eg def definitions.

I feel dedicated language-level support would turn out to address this problem more simply than trying to re-purpose export.

4 Likes