Skip to content

proposal: spec: make imported symbols predictable #29036

@rogpeppe

Description

@rogpeppe

Currently when a package is imported, it's not possible to tell what package name it uses without going to the package itself to see what name it uses.

This can make it slow to analyse code (for example to jump to a symbol definition). For example, say we want to find the definition of the symbol X in the expression m.X. If m isn't defined in the local package, it's necessary to obtain a copy of every imported package's code to see if it's defined by any of them. For large packages, that can take a long time (many seconds). Even with caching proxies, it can still take a long time if the set of dependencies has recently changed.

There's also the issue of cognitive overhead: just seeing an import statement is not sufficient to know what symbols that import statement brings into scope. For large programs, that overhead can be significant, and is worse when one or more of the dependencies are no longer available - the reader of the code is forced to guess what symbols are imported.

This issue is even worse when "." imports are used to bring all a package's exported symbols into local scope. Even though dot-imports are frowned upon, there is still no universal consensus that they should be avoided.

The goimports tool already implicitly acknowledges this issue by adding an explicit package name when the symbol isn't clear from the import path. Although this helps, this doesn't help in the general case, because tools cannot assume that goimports has been run.

I propose the following:

  • dot imports should be disallowed
  • the imported symbol for a given import should be made predictable

This would mean that we can always look at any symbol in a piece of Go source code with local context only and know definitively whether it is defined in the local package or not, and if not, exactly which import path would need to be investigated to find the symbol.

Dot imports

The official guidelines suggest that a dot import should only be used "to let the file pretend to be part of package foo even though it is not". This is a stylistic choice and strictly unnecessary. The tests can still use package-qualified names with only minor inconvenience (the same inconvenience that any external user will see).

Other than that, I believe the most common use is to make the imported package feel "close to a DSL". This seems to be actively opposed to the aims of Go, as it makes the programs much harder to read.

Predictable imported symbols for imports

The simplest possibility here would be to require an import symbol for every import path. As many (most?) people use goimports to manage their import statements, this might not be too onerous a requirement.

Another approach would be to state that when lacking an explicit import package name, the package name must match a name derived from the import path. Possible rules might be:

  • split the import path on /; the expected name is the last element.
  • choose the longest valid Go identifier from the end of the import path.
  • whatever rule goimports currently uses to determine whether it should add an explicit package name.

This means that we could carry on importing "fmt" without redundantly specifying fmt "fmt", but has the disadvantage that nothing else in the language specification talks about what's inside an import path.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions