Allow `namespace.case` filter for `import`/`export`

We already have import namespace.given to import just givens (also export).

  1. Should we add import ns.case to selectively import just case classes and case objects? I have found myself requiring this feature many times where I want to import/export case classes and case objects of an enum to some other namespaces. Using import enumCompanion.* pollutes the namespace with other unwanted objects and definitions.
  2. If we add this, is there merit to add other filters like ns.def, ns.val, ns.type, ns.class, etc.?
1 Like

Import for givens was required since these can be anonymous, or rather name can be synthesized.
Case classes and other types are always explicitly named. Maybe let’s not try to make reasoning about code harder, we already have wildcards and contextual abstractions that basically require us to have a extended knowledge about class path and what is implicitly hidding behind the asterisk

1 Like

I think the answer to this depends on whether we will get relative scoping for enums or not. If we do, then there’s no need for a special import feature IMO. If we don’t, we should consider this.

Also maybe this is more of a tooling problem ?

There are languages where wildcard importing is either discouraged or impossible, and it does not seem to be a big issue (because the tooling fills that void)

In JS/TS in vs code for example, the imports are automatically added when you autocomplete an identifier, which makes it virtually fade into the background (except for the few times when it gets it wrong)
As such, I have never felt the need for wildcard imports when using it
(And so a “more precise wildcard” would not have been useful to me either)

I know there are a lot of toolings nice to have (tooling nice to haves ?) and not enough manpower
But maybe it still is worth it to try to solve it on this level rather than trough a language feature

Perhaps another flavor of this could be do what Python does: make import foo.* configurable! Python does it by setting an __all__ = ["foo", "bar", "baz"] list, but one could imagine Scala having something like:

A user can prefix definitions (def, val, lazy val, class, trait, object, enum) with the export keyword, e.g. export def bar = ???. If one-or-more export-ed definitions is present on an object foo then import foo.* will only import the export-ed definitions rather than importing everything in the scope. enums have the export keyword automatically applied to their cases, and a user can export additional definitions on the enum companion object if they so desire

Rather than declaring “import foo.* is broken, use import foo.case", this would instead fix import foo.* to do the right thing: on enums it would import the cases, on other library-defined types the library-author could decide what import foo.* means for their types, and for backwards compatibility it would remain unchanged for code that does not use the new feature. That will allow library authors to provide a concise and convenient import foo.* API without the messiness and unwants imports that the current import foo.* provides (e.g. toString, hashCode, equals, etc.)

This is already the case in Scala. Intellij adds imports automatically, including explicit named imports for implicits. There’s no good reason to use wildcard imports in production code if you use Intellij.

The big exceptions to that are REPL, prototyping, API exploration, script, worksheet and documentation examples. Only the first three are acknowledged in recent JEP-494 as motivating reasons to add whole Java module imports feature to Java.

A user can prefix definitions (def, val, lazy val, class, trait, object, enum) with the export keyword, e.g. export def bar = ???. […] then import foo.* will only import the export-ed definitions

I think this is an interesting possibility for the api-designer to help shaping the scope and provide a convenient, single line import, esp when experimenting in repl/worksheet etc.

But I see a problem of changing the semantics of wildcard import…

Perhaps the api-user could get the export-marked members via a new kind of import:

import foo.export

And maybe this feature could be combined with the implicit export of cases discussed here by treating companion object special in that export-marked companion object members are implicitly imported and has a companion as in import foo.export if foo is in scope or a target type. That would solve the enum inconvience.

2 Likes